├── .gitignore ├── img ├── favicon.ico ├── printscreen.png ├── whatsapp-wallpaper-default.jpg └── editor-de-texto-para-whatsapp-matheus-teixeira.jpg ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── css └── styles.css ├── js └── script.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | .qodo 2 | -------------------------------------------------------------------------------- /img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeusteixeira/whatsapp-text-editor/HEAD/img/favicon.ico -------------------------------------------------------------------------------- /img/printscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeusteixeira/whatsapp-text-editor/HEAD/img/printscreen.png -------------------------------------------------------------------------------- /img/whatsapp-wallpaper-default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeusteixeira/whatsapp-text-editor/HEAD/img/whatsapp-wallpaper-default.jpg -------------------------------------------------------------------------------- /img/editor-de-texto-para-whatsapp-matheus-teixeira.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeusteixeira/whatsapp-text-editor/HEAD/img/editor-de-texto-para-whatsapp-matheus-teixeira.jpg -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "activityBar.background": "#2E2F11", 4 | "titleBar.activeBackground": "#404217", 5 | "titleBar.activeForeground": "#FAFAF2" 6 | } 7 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Editor de Texto para WhatsApp 2 | 3 | Bem-vindo(a) ao Editor de Texto para WhatsApp! Esta é uma ferramenta simples que permite gerar mensagens formatadas para serem usadas no WhatsApp. 4 | 5 | ## Descrição 6 | 7 | O Editor de Texto para WhatsApp é uma aplicação web que facilita a criação de mensagens amigáveis e formatadas para serem enviadas pelo WhatsApp. Com esta ferramenta, você pode adicionar formatações, criar listas e gerar mensagens prontas usando modelos pré-definidos. 8 | 9 | ![alt text](https://raw.githubusercontent.com/aeusteixeira/whatsapp-text-editor/master/img/printscreen.png) 10 | 11 | ## Como usar 12 | 13 | Você pode acessar o Editor de Texto para WhatsApp de duas maneiras: 14 | 15 | 1. **Download:** Você pode baixar os arquivos do projeto e executá-lo localmente em seu computador. Para isso, siga as instruções abaixo: 16 | 17 | - Clique no botão "Code" nesta página e selecione "Download ZIP" para baixar os arquivos do projeto. 18 | - Extraia os arquivos em uma pasta local em seu computador. 19 | - Abra o arquivo "index.html" em seu navegador para usar o Editor de Texto para WhatsApp. 20 | 21 | 2. **Acesso Online:** Você pode acessar o Editor de Texto para WhatsApp diretamente pelo seguinte link: [https://aeusteixeira.github.io/whatsapp-text-editor/](https://aeusteixeira.github.io/whatsapp-text-editor/) 22 | 23 | ## Recursos adicionais 24 | 25 | Além da funcionalidade básica de formatação de texto e criação de listas, o Editor de Texto para WhatsApp também oferece recursos adicionais, como: 26 | 27 | - Modelos pré-definidos: Você pode selecionar modelos de mensagens prontas para agilizar o processo de criação. 28 | - Emojis: Há uma seleção de emojis disponíveis para adicionar um toque divertido às suas mensagens. 29 | 30 | ## Contribuição 31 | 32 | Sinta-se à vontade para contribuir com melhorias, correções de bugs ou novos recursos para o Editor de Texto para WhatsApp. Se você encontrar algum problema ou tiver alguma sugestão, abra uma "Issue" neste repositório ou envie um "Pull Request" com suas alterações. 33 | 34 | ## Licença 35 | 36 | Este projeto é licenciado sob a [Licença MIT](LICENSE). Isso significa que você tem permissão para usar, copiar, modificar, mesclar, publicar, distribuir, sublicenciar e/ou vender cópias do software, bem como permitir que outras pessoas façam o mesmo, sob as seguintes condições: 37 | 38 | - O aviso de direitos autorais e esta permissão devem ser incluídos em todas as cópias ou partes substanciais do software. 39 | - O software é fornecido "como está", sem garantia de qualquer tipo, expressa ou implícita, incluindo, mas não se limitando a, garantias de comercialização, adequação a uma finalidade específica e não violação. Em nenhum caso os autores ou detentores dos direitos autorais serão responsáveis por qualquer reclamação, danos ou outra responsabilidade, seja em uma ação de contrato, delito ou de outra forma, decorrente, de ou em conexão com o software ou o uso ou outros negócios no software. 40 | 41 | Esperamos que você aproveite o projeto e estamos abertos a contribuições e sugestões para melhorá-lo! 42 | 43 | 44 | -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | /* Estilos CSS */ 2 | *, card, .btn{ 3 | border-radius: 0.25rem; 4 | } 5 | 6 | body { 7 | font-family: Arial, sans-serif; 8 | background-color: #45a049 !important; 9 | background-image: url('../img/whatsapp-wallpaper-default.jpg'); 10 | background-repeat: repeat; 11 | background-blend-mode: multiply; /* Mistura a cor com a imagem */ 12 | } 13 | 14 | .container{ 15 | border-radius: 0.25rem; 16 | } 17 | 18 | #chat{ 19 | background-image: url('../img/whatsapp-wallpaper-default.jpg'); 20 | background-size: cover; 21 | background-repeat: no-repeat; 22 | background-position: center; 23 | min-height: 300px; 24 | height: auto; 25 | } 26 | 27 | #chat .message{ 28 | background-color: #E4FFCB; 29 | padding: 10px; 30 | border-radius: 10px; 31 | margin: 10px; 32 | } 33 | 34 | .contact-image{ 35 | width: 40px; 36 | height: 40px; 37 | border-radius: 50%; 38 | } 39 | 40 | .btn-default{ 41 | background-color: #4CAF50; 42 | color: #fff; 43 | border: none; 44 | cursor: pointer; 45 | border-radius: 0.25rem; 46 | transition: .2s; 47 | text-decoration: none; 48 | } 49 | 50 | .btn-default:hover { 51 | background-color: #2d8532; 52 | color: #fff; 53 | } 54 | /* Estilos para o a listagem dos modelos */ 55 | 56 | .model-list { 57 | display: flex; 58 | flex-wrap: wrap; 59 | gap: 10px; 60 | } 61 | 62 | .model-card { 63 | background-color: #f0f0f0; 64 | padding: 10px; 65 | border-radius: 4px; 66 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 67 | transition: box-shadow 0.3s; 68 | width: 100%; 69 | max-width: 300px; 70 | margin: 0 auto; 71 | text-align: center; 72 | } 73 | 74 | .model-card p { 75 | margin-bottom: 10px; 76 | font-size: 16px; 77 | } 78 | 79 | .model-card .button { 80 | margin-right: 5px; 81 | } 82 | 83 | .model-card .delete-button { 84 | background-color: #ff0000; 85 | color: #fff; 86 | } 87 | 88 | .model-card .copy-button { 89 | background-color: #008de6; 90 | color: #fff; 91 | } 92 | 93 | /* Estilos para o input de pesquisa */ 94 | #emojiSearch { 95 | width: 100%; 96 | padding: 8px; 97 | font-size: 16px; 98 | border: 1px solid #ccc; 99 | border-radius: 4px; 100 | margin-bottom: 10px; 101 | } 102 | 103 | /* Estilos para a lista de emojis */ 104 | .emoji-container { 105 | max-height: 300px; 106 | overflow-y: auto; 107 | } 108 | 109 | .emoji-container button { 110 | margin-right: 8px; 111 | margin-bottom: 8px; 112 | padding: 8px; 113 | font-size: 20px; 114 | cursor: pointer; 115 | border: none; 116 | background: none; 117 | } 118 | 119 | .emoji-container button:hover { 120 | background-color: #f2f2f2; 121 | } 122 | 123 | /* Estilos para a barra de rolagem */ 124 | .emoji-container::-webkit-scrollbar { 125 | width: 8px; 126 | } 127 | 128 | .emoji-container::-webkit-scrollbar-track { 129 | background-color: #f1f1f1; 130 | } 131 | 132 | .emoji-container::-webkit-scrollbar-thumb { 133 | background-color: #888; 134 | border-radius: 4px; 135 | } 136 | 137 | .emoji-container::-webkit-scrollbar-thumb:hover { 138 | background-color: #555; 139 | } 140 | 141 | 142 | @media (min-width: 320px) and (max-width: 540px) { 143 | .options { 144 | display: flex; 145 | flex-direction: column; 146 | margin-right: 12px; 147 | } 148 | 149 | button { 150 | margin-top: 12px; 151 | } 152 | } 153 | 154 | @keyframes shine { 155 | 0% { 156 | box-shadow: 0 0 10px rgba(22, 151, 65, 0.7); 157 | } 158 | 50% { 159 | box-shadow: 0 0 20px rgba(28, 230, 89, 0.7); 160 | } 161 | 100% { 162 | box-shadow: 0 0 10px rgba(78, 228, 98, 0.7); 163 | } 164 | } 165 | 166 | .btn.btn-default.btn-highlight { 167 | animation: shine 2s infinite; 168 | } 169 | 170 | #heart { 171 | display: inline-block; 172 | animation: pulse 1s infinite; 173 | } 174 | 175 | @keyframes pulse { 176 | 0% { 177 | transform: scale(1); 178 | } 179 | 50% { 180 | transform: scale(1.2); 181 | } 182 | 100% { 183 | transform: scale(1); 184 | } 185 | } 186 | 187 | -------------------------------------------------------------------------------- /js/script.js: -------------------------------------------------------------------------------- 1 | function saveModel() { 2 | let modelSelector = document.getElementById("inputText"); 3 | let selectedModel = modelSelector.value; 4 | 5 | if (selectedModel) { 6 | // Recupera os modelos salvos do localStorage 7 | let savedModels = JSON.parse(localStorage.getItem("savedModels")) || []; 8 | 9 | // Verifica se o modelo já está salvo 10 | if (savedModels.includes(selectedModel)) { 11 | alert("Este modelo já está salvo."); 12 | } else { 13 | // Adiciona o novo modelo ao array 14 | savedModels.push(selectedModel); 15 | // Salva o array atualizado no localStorage 16 | localStorage.setItem("savedModels", JSON.stringify(savedModels)); 17 | } 18 | } else { 19 | alert("Selecione um modelo antes de salvá-lo."); 20 | } 21 | loadModels(); 22 | } 23 | 24 | function loadModels() { 25 | let modelList = document.getElementById("modelList"); 26 | modelList.innerHTML = ""; 27 | 28 | // Recupera os modelos salvos do localStorage 29 | let savedModels = JSON.parse(localStorage.getItem("savedModels")) || []; 30 | 31 | if (savedModels.length === 0) { 32 | let noModelsText = document.createElement("p"); 33 | noModelsText.className = "text-muted"; 34 | noModelsText.textContent = "Nenhum modelo salvo."; 35 | modelList.className = "text-center"; 36 | modelList.appendChild(noModelsText); 37 | } 38 | 39 | // Percorre os modelos e cria os cards 40 | for (let i = 0; i < savedModels.length; i++) { 41 | let model = savedModels[i]; 42 | 43 | // Cria o card do modelo 44 | let modelCard = document.createElement("div"); 45 | modelCard.className = "model-card"; 46 | 47 | // Cria o texto do modelo 48 | let modelText = document.createElement("p"); 49 | modelText.innerHTML = formatModelText(model); // Aplica a formatação ao texto 50 | modelCard.appendChild(modelText); 51 | 52 | // Cria o botão de exclusão 53 | let deleteButton = document.createElement("button"); 54 | deleteButton.className = "btn btn-danger btn-sm mr-3"; 55 | deleteButton.textContent = "Excluir"; 56 | deleteButton.addEventListener("click", deleteModel.bind(null, modelCard)); 57 | modelCard.appendChild(deleteButton); 58 | 59 | // Cria o botão de cópia 60 | let copyButton = document.createElement("button"); 61 | copyButton.className = "btn btn-info btn-sm copy-button"; 62 | copyButton.textContent = "Copiar"; 63 | copyButton.addEventListener("click", copyModelText.bind(null, modelText.innerText)); 64 | modelCard.appendChild(copyButton); 65 | 66 | modelList.appendChild(modelCard); 67 | } 68 | } 69 | function formatModelText(text) { 70 | let formattedText = text 71 | .replace(/\*([^\*]+)\*/g, "$1") 72 | .replace(/\_([^\_]+)\_/g, "$1") 73 | .replace(/\~([^\~]+)\~/g, "$1") 74 | .replace(/\`([^`]+)\`(?![^<]*<\/code>)/g, "$1") 75 | .replace(/\n\-\s/g, "
- ") 76 | .replace(/\n\d+\.\s/g, "
1. ") 77 | .replace(/`/g, "") 78 | .replace(/\n/g, "
"); 79 | 80 | return formattedText; 81 | } 82 | 83 | function deleteModel(card) { 84 | let modelList = document.getElementById("modelList"); 85 | let modelText = card.querySelector("p").textContent; 86 | 87 | // Recupera os modelos salvos do localStorage 88 | let savedModels = JSON.parse(localStorage.getItem("savedModels")) || []; 89 | 90 | // Remove o modelo do array 91 | savedModels = savedModels.filter(function (model) { 92 | return model !== modelText; 93 | }); 94 | 95 | // Atualiza o localStorage com os modelos restantes 96 | localStorage.setItem("savedModels", JSON.stringify(savedModels)); 97 | 98 | // Remove o card do modelo da lista 99 | modelList.removeChild(card); 100 | } 101 | 102 | function copyModelText(text) { 103 | // Copia o texto do modelo para a área de transferência 104 | let textarea = document.createElement("textarea"); 105 | textarea.value = text; 106 | document.body.appendChild(textarea); 107 | textarea.select(); 108 | document.execCommand("copy"); 109 | document.body.removeChild(textarea); 110 | 111 | alert("Texto copiado para a área de transferência!"); 112 | } 113 | 114 | loadModels(); 115 | 116 | function addFormatting(mark) { 117 | let inputText = document.getElementById("inputText"); 118 | let start = inputText.selectionStart; 119 | let end = inputText.selectionEnd; 120 | let selectedText = inputText.value.substring(start, end); 121 | let formattedText = mark + selectedText + mark; 122 | inputText.value = inputText.value.substring(0, start) + formattedText + inputText.value.substring(end); 123 | 124 | updatePreview(); 125 | } 126 | function addList(type) { 127 | let inputText = document.getElementById("inputText"); 128 | let start = inputText.selectionStart; 129 | let end = inputText.selectionEnd; 130 | let selectedText = inputText.value.substring(start, end); 131 | let listItems = selectedText.split('\n'); 132 | let formattedText = ""; 133 | 134 | if (type === "ul") { 135 | formattedText = listItems.map(item => "- " + item).join("\n"); 136 | } else if (type === "ol") { 137 | let index = 1; 138 | formattedText = listItems.map(item => { 139 | if (item.startsWith(index + ".")) { 140 | index++; 141 | return item.replace(/^\d+\./, index - 1 + "."); 142 | } else if (item.startsWith((index - 1) + ".")) { 143 | return item.replace(/^\d+\./, index - 1 + ".1."); 144 | } else if (item.startsWith((index - 1) + ".1.")) { 145 | return item.replace(/^\d+\.\d+\./, index - 1 + ".2."); 146 | } else { 147 | index++; 148 | return index - 1 + ". " + item; 149 | } 150 | }).join("\n"); 151 | } 152 | 153 | inputText.value = inputText.value.substring(0, start) + formattedText + inputText.value.substring(end); 154 | 155 | updatePreview(); 156 | } 157 | function openEmojiModal() { 158 | let modal = document.getElementById("emojiModal"); 159 | modal.style.display = "block"; 160 | } 161 | 162 | function closeEmojiModal() { 163 | let modal = document.getElementById("emojiModal"); 164 | modal.style.display = "none"; 165 | } 166 | 167 | function insertEmoji(emoji) { 168 | let inputText = document.getElementById("inputText"); 169 | let start = inputText.selectionStart; 170 | let end = inputText.selectionEnd; 171 | let selectedText = inputText.value.substring(start, end); 172 | let formattedText = emoji; 173 | 174 | inputText.value = inputText.value.substring(0, start) + formattedText + inputText.value.substring(end); 175 | 176 | updatePreview(); 177 | closeEmojiModal(); 178 | } 179 | 180 | function updatePreview() { 181 | let inputText = document.getElementById("inputText"); 182 | let previewText = document.getElementById("previewText"); 183 | 184 | let formattedText = inputText.value 185 | .replace(/\*([^\*]+)\*/g, "$1") 186 | .replace(/\_([^\_]+)\_/g, "$1") 187 | .replace(/\~([^\~]+)\~/g, "$1") 188 | .replace(/\`([^`]+)\`(?![^<]*<\/code>)/g, "$1") 189 | .replace(/\n\-\s/g, "
- ") 190 | .replace(/\n\d+\.\s/g, "
1. ") 191 | .replace(/`/g, "") 192 | .replace(/\n/g, "
"); 193 | 194 | previewText.innerHTML = formattedText; 195 | } 196 | function copyText() { 197 | let inputText = document.getElementById("inputText"); 198 | inputText.select(); 199 | inputText.setSelectionRange(0, 99999) 200 | document.execCommand('copy'); 201 | 202 | if (inputText.value === null || inputText.value === '') { 203 | alert('Sem mensagem para cópia!') 204 | } else { 205 | navigator.clipboard.writeText(inputText.value) 206 | confirm(`A mensagem: ${inputText.value} foi copiada com sucesso!`) 207 | } 208 | // Remova a chamada para a função eraseText() aqui 209 | } 210 | 211 | 212 | function clearFormatting() { 213 | let inputText = document.getElementById("inputText"); 214 | let unformattedText = inputText.value.replace(/\*|_|~/g, ""); 215 | 216 | inputText.value = unformattedText; 217 | 218 | updatePreview(); 219 | } 220 | 221 | function fetchEmojis() { 222 | fetch('https://emoji-api.com/emojis?access_key=afbf73a432f34028d70288c21768f4195cd6e0b9') 223 | .then(response => response.json()) 224 | .then(data => { 225 | let emojiContainer = document.getElementById('emojiContainer'); 226 | let emojiSearchInput = document.getElementById('emojiSearch'); 227 | 228 | emojiSearchInput.addEventListener('input', function () { 229 | let searchQuery = emojiSearchInput.value.toLowerCase(); 230 | 231 | let filteredEmojis = data.filter(emoji => { 232 | return emoji.unicodeName.toLowerCase().includes(searchQuery); 233 | }); 234 | 235 | renderEmojis(filteredEmojis); 236 | }); 237 | 238 | renderEmojis(data); 239 | }) 240 | .catch(error => { 241 | console.error('Erro ao buscar emojis:', error); 242 | }); 243 | } 244 | 245 | function renderEmojis(emojis) { 246 | let emojiContainer = document.getElementById('emojiContainer'); 247 | emojiContainer.innerHTML = ''; 248 | 249 | emojis.forEach(emoji => { 250 | let button = document.createElement('button'); 251 | button.textContent = emoji.character; 252 | button.className = 'btn btn-primary'; 253 | button.onclick = function () { 254 | insertEmoji(emoji.character); 255 | closeEmojiModal(); 256 | }; 257 | 258 | emojiContainer.appendChild(button); 259 | }); 260 | } 261 | function insertEmoji(emoji) { 262 | let inputText = document.getElementById("inputText"); 263 | let start = inputText.selectionStart; 264 | let end = inputText.selectionEnd; 265 | let selectedText = inputText.value.substring(start, end); 266 | let formattedText = emoji; 267 | 268 | inputText.value = inputText.value.substring(0, start) + formattedText + inputText.value.substring(end); 269 | 270 | updatePreview(); 271 | } 272 | 273 | function closeEmojiModal() { 274 | let modal = new bootstrap.Modal(document.getElementById('exampleModal')); 275 | modal.hide(); 276 | } 277 | fetchEmojis(); 278 | 279 | function updateSaveButtonVisibility() { 280 | let previewText = document.getElementById("previewText").textContent; 281 | let saveModelButton = document.getElementById("saveModelButton"); 282 | 283 | if (previewText.trim() === "") { 284 | saveModelButton.classList.add("d-none"); 285 | } else { 286 | saveModelButton.classList.remove("d-none"); 287 | } 288 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Editor de Texto para WhatsApp 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 56 | 81 |
82 |

Editor de Texto para WhatsApp

83 |

Esta é uma ferramenta de edição de texto para criar mensagens formatadas para o 84 | WhatsApp.

85 |
86 |
87 |
88 | 92 | 96 | 100 | 104 | 108 | 112 |
113 |
114 |

Para formatar o texto, selecione a parte desejada e clique nos botões acima.

115 |

Para criar uma lista, coloque cada item em uma nova linha e clique no botão correspondente.

116 |
117 |
118 | 120 |
121 |
122 | 126 | 130 | 150 | 154 | 158 | 159 | 231 |
232 |
233 |
234 | 247 | 308 |
309 |
310 |
311 | 317 | 318 | 321 | 322 | 323 | 324 | --------------------------------------------------------------------------------