diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..f2c9e97 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..90822c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 cr1ma + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3ad0e17 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# NeuroCommit + +NeuroCommit - це веб-інструмент, який допомагає розробникам створювати якісні повідомлення для комітів на основі виводу команди `git diff`. Цей інструмент перетворює технічні зміни у зрозумілі та інформативні повідомлення для комітів, які можна використовувати з нейромережами для подальшого покращення. + +Спробуйте NeuroCommit зараз: [https://cr1ma.github.io/neuro-commit/](https://cr1ma.github.io/neuro-commit/) + +## Особливості + +- Перетворення виводу `git diff` у структурований промт для нейромережі +- Інтуїтивно зрозумілий веб-інтерфейс +- Підсвічування синтаксису для diff +- Можливість копіювання згенерованого промту одним кліком + +## Як використовувати + +1. Відкрийте термінал у вашому Git-репозиторії. +2. Виконайте команду `git diff` для перегляду змін у робочій директорії: + ``` + git diff + ``` + Або використовуйте `git diff --staged` для перегляду змін у індексі (staged changes): + ``` + git add . + git diff --staged + ``` +3. Скопіюйте вивід команди. +4. Відкрийте [https://cr1ma.github.io/neuro-commit/](https://cr1ma.github.io/neuro-commit/) у вашому браузері. +5. Вставте скопійований diff у ліве текстове поле. +6. Справа з'явиться згенерований промт для нейромережі. +7. Натисніть кнопку "Скопіювати промт", щоб скопіювати згенерований текст. +8. Використовуйте цей промт з вашою улюбленою нейромережею для генерації повідомлення коміту. + +## Ліцензія + +Цей проект розповсюджується під ліцензією MIT. Дивіться файл `LICENSE` для отримання додаткової інформації. \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..128b000 --- /dev/null +++ b/index.html @@ -0,0 +1,36 @@ + + + + + NeuroCommit + + + + + +
+
+

NeuroCommit

+

Перетворіть свій git diff у готовий промт для нейромережі та зробіть свій коміт кращим!

+
+
+
+

Вивід команди git diff

+ +
+
+

Згенерований промт для нейромережі

+
+

+                
+ +
+
+ +
+
+ + + \ No newline at end of file diff --git a/prompt.txt b/prompt.txt new file mode 100644 index 0000000..3ddb79e --- /dev/null +++ b/prompt.txt @@ -0,0 +1,32 @@ +Ти експерт з написання повідомлень до комітів у репозиторіях. Твоє завдання — написати мені повідомлення до коміту на основі мого файлу змін (diff), який я тобі надам. + +### Правила написання повідомлення до коміту: +1. Пиши на українській мові, строго враховуючи контекст. +2. Відповідь повертай з використанням розмітки markdown. +3. Використовуй імперативний стиль, як би ти давав команду системі, що відповідає стилю повідомлень, які створюють зміни в коді. +4. Перша строка повідомлення (заголовок) має бути короткою, зазвичай не довше 50 символів. Це полегшує швидке розуміння змін. Не завершуй заголовок крапкою. +5. Після заголовка залишай одну порожню строку перед початком тіла повідомлення. Це розділення допомагає інструментам для роботи з Git правильно відображати текст повідомлення. +6. Коміти з повідомленнями на кшталт "Fix" або "Update" не дають корисної інформації. Завжди пояснюй, що саме було виправлено або оновлено. +7. **Використовуй нижній регістр для опису типів змін. Використовуй** семантичні теги в заголовках повідомлень: + - `feat:` — додавання нової функції + - `fix:` — виправлення багів + - `docs:` — зміни в документації + - `style:` — зміни, що не впливають на код (наприклад, виправлення форматування) + - `refactor:` — зміна коду, яка не додає нової функціональності і не виправляє багів + - `test:` — додавання або зміна тестів + +### Приклад правильного повідомлення до коміту: +```diff +refactor: оновлено конфігурацію середовища та підключення до API + +- Відредаговано файл `.env` для підтримки різних середовищ (production, development) та режимів підключення до API (docker, local, remote). +- Оновлено `config.py` для завантаження токенів та URL-адрес залежно від середовища та режиму API. +- Видалено логіку визначення операційної системи. +- Оновлено `api_client.py` для використання BASE_API_URL замість URL-адрес для різних ОС. +- Зменшено кількість повторних спроб у `_make_request`. +``` + +### Вивід команди `git diff`, яку я виконав у своєму репозиторії: +```diff + +``` \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..ef4e016 --- /dev/null +++ b/script.js @@ -0,0 +1,105 @@ +// Вибираємо елементи для подальшої роботи +const gitDiffInput = document.getElementById('gitDiffInput'); +const promptOutput = document.getElementById('promptOutput'); +const copyButton = document.getElementById('copyButton'); +const toast = document.getElementById('toast'); + +let promptTemplate = ''; + +// Завантажуємо шаблон промпту з файлу prompt.txt +fetch('prompt.txt') + .then(response => response.text()) + .then(data => { + promptTemplate = data; + }) + .catch(error => { + console.error('Помилка завантаження промпту:', error); + }); + +// Функція для екранування HTML, щоб уникнути XSS атак +function escapeHTML(str) { + return str.replace(/&/g, "&") + .replace(//g, ">"); +} + +// Функція для підсвічування рядків у git diff +function highlightDiff(diff) { + const lines = diff.split('\n'); + return lines.map(line => { + if (line.startsWith('+')) { + return `${escapeHTML(line)}`; + } else if (line.startsWith('-')) { + return `${escapeHTML(line)}`; + } else { + return escapeHTML(line); + } + }).join('\n'); +} + +// Функція для очищення введеного користувачем git diff для GitHub Copilot +function sanitizeInput(input) { + const wordsToSanitize = [ + '@workspace', '/explain', '/tests', '/fix', '/new', '/newNotebook', '/fixTestFailure', '/setupTests', + '@vscode', '/search', '/runCommand', '/startDebugging', + '@terminal', '@github', '#selection', '/help', '#codebase', '#editor', '#terminalLastCommand', '#terminalSelection', '#file' + ]; + + let sanitized = input; + wordsToSanitize.forEach(word => { + const regex = new RegExp(word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'); + sanitized = sanitized.replace(regex, word.startsWith('/') ? word.slice(1) : word); + }); + + return sanitized; +} + +// Функція для оновлення промпту на основі введеного користувачем git diff +function updatePrompt() { + const gitDiff = gitDiffInput.value.trim(); + if (gitDiff === "") { + promptOutput.innerHTML = ""; + return; + } + const sanitizedDiff = sanitizeInput(gitDiff); + const highlightedDiff = highlightDiff(sanitizedDiff); + const filledPrompt = promptTemplate.replace("", highlightedDiff); + promptOutput.innerHTML = filledPrompt; +} + +// Слідкуємо за змінами у полі введення і автоматично оновлюємо промт +gitDiffInput.addEventListener('input', updatePrompt); + +// Функція для показу сповіщення (toast) на екрані +function showToast(message, type = 'success') { + toast.className = 'toast'; + + if (type === 'success') { + toast.classList.add('success'); + } else if (type === 'error') { + toast.classList.add('error'); + } else if (type === 'info') { + toast.classList.add('info'); + } + + toast.innerHTML = message; + toast.classList.add('show'); + + setTimeout(() => { + toast.classList.remove('show'); + toast.classList.add('hide'); + }, 3500); +} + +// Обробляємо натискання кнопки "Скопіювати промт" +copyButton.addEventListener('click', async () => { + const textToCopy = promptOutput.textContent; + + try { + await navigator.clipboard.writeText(textToCopy); + showToast('Промт успішно скопійовано в буфер обміну!', 'success'); + } catch (err) { + console.error('Не вдалося скопіювати текст: ', err); + showToast('Не вдалося скопіювати промт.', 'error'); + } +}); \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..8fccf21 --- /dev/null +++ b/styles.css @@ -0,0 +1,275 @@ +/* Основні стилі для root та загальних елементів сторінки */ +:root { + --background-color: #121212; + --primary-color: #1e88e5; + --secondary-color: #ffffff; + --text-color: #e0e0e0; + --header-footer-bg: #1f1f1f; + --border-color: #333; + --addition-color: #43a047; + --deletion-color: #e53935; + --button-bg: #1e88e5; + --button-hover-bg: #1565c0; + --toast-success-bg: #ffffff; + --toast-error-bg: #ffbcba; + --toast-info-bg: #1e88e5; + --toast-text-color: #000000; + --prompt-bg: #2c2c2c; +} + +/* Стилі для усього документа */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + height: 100%; + overflow: hidden; +} + +body { + font-family: 'Inter', sans-serif; + background-color: var(--background-color); + color: var(--text-color); + line-height: 1.6; +} + +.container { + display: flex; + flex-direction: column; + height: 100vh; +} + +header, footer { + text-align: center; + padding: 20px; + background-color: var(--header-footer-bg); + flex: 0 0 auto; +} + +/* Заголовок на сторінці */ +header h1 { + font-weight: 600; + font-size: 2rem; + margin-bottom: 10px; + color: var(--primary-color); +} + +header p { + font-size: 1rem; + color: var(--secondary-color); +} + +/* Основний контент */ +main { + flex: 1 1 auto; + display: flex; + background-color: var(--header-footer-bg); + overflow: hidden; +} + +.split { + flex: 1; + padding: 20px; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.left { + border-right: 1px solid var(--border-color); + background-color: var(--header-footer-bg); +} + +.right { + position: relative; + background-color: var(--header-footer-bg); +} + +h2 { + font-size: 1.5rem; + margin-bottom: 15px; + color: var(--primary-color); + flex: 0 0 auto; +} + +textarea { + flex: 1 1 auto; + padding: 15px; + font-size: 1rem; + border: 1px solid var(--border-color); + border-radius: 6px; + resize: none; + background-color: #2c2c2c; + color: var(--text-color); + font-family: 'Courier New', Courier, monospace; + overflow-y: auto; +} + +textarea::placeholder { + color: #888; +} + +textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 5px var(--primary-color); +} + +/* Стиль для контейнера промпту */ +.prompt-container { + flex: 1 1 auto; + overflow-y: auto; + background-color: var(--prompt-bg); + border-radius: 6px; + margin-bottom: 15px; + padding: 15px; +} + +.prompt { + color: var(--text-color); + font-family: 'Courier New', Courier, monospace; + white-space: pre-wrap; + word-wrap: break-word; + min-height: 100px; +} + +.prompt .addition { + color: var(--addition-color); +} + +.prompt .deletion { + color: var(--deletion-color); +} + +/* Стиль для кнопок */ +button { + padding: 12px 25px; + background-color: var(--button-bg); + color: #ffffff; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 1rem; + transition: background-color 0.3s, transform 0.2s; + display: block; + width: 100%; + max-width: 250px; + margin: 0 auto; +} + +button:hover { + background-color: var(--button-hover-bg); + transform: scale(1.05); +} + +footer p { + font-size: 0.9rem; + color: #bbb; +} + +footer a { + color: var(--secondary-color); + text-decoration: none; + font-weight: 600; +} + +footer a:hover { + text-decoration: underline; +} + +/* Сповіщення */ +.toast { + position: fixed; + top: 20px; + right: 20px; + background-color: var(--toast-info-bg); + color: var(--toast-text-color); + padding: 15px 20px; + border-radius: 8px; + font-size: 1rem; + opacity: 0; + transform: translateY(-50px) scale(0.9); + transition: opacity 0.5s ease, transform 0.5s ease; + z-index: 1000; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3); + pointer-events: none; + display: flex; + align-items: center; +} + +.toast.show { + opacity: 1; + transform: translateY(0) scale(1); + animation: fadeInUp 0.5s forwards; +} + +.toast.hide { + opacity: 0; + transform: translateY(-50px) scale(0.9); + animation: fadeOutDown 0.5s forwards; +} + +.toast .icon { + margin-right: 10px; + font-size: 1.2rem; +} + +.toast.success { + background-color: var(--toast-success-bg); + color: var(--toast-text-color); +} + +.toast.error { + background-color: var(--toast-error-bg); + color: var(--toast-text-color); +} + +.toast.info { + background-color: var(--toast-info-bg); + color: var(--toast-text-color); +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(-20px) scale(0.95); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +@keyframes fadeOutDown { + from { + opacity: 1; + transform: translateY(0) scale(1); + } + to { + opacity: 0; + transform: translateY(-20px) scale(0.95); + } +} + +@media (max-width: 768px) { + main { + flex-direction: column; + } + + .split { + width: 100%; + height: 50vh; + } + + .left { + border-right: none; + border-bottom: 1px solid var(--border-color); + } + + button { + max-width: 100%; + } +} \ No newline at end of file