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