diff --git a/.gitignore b/.gitignore index e1b049b..3f73acc 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,12 @@ Thumbs.db dist/ build/ +# Загруженные аватарки пользователей +public/uploads/* +!public/uploads/.gitignore + +# Временные файлы +/tmp/ +*.tmp + .cursor/ \ No newline at end of file diff --git a/app.js b/app.js deleted file mode 100644 index 25af108..0000000 --- a/app.js +++ /dev/null @@ -1,247 +0,0 @@ -const textInput = document.querySelector(".textInput"); -const btnSave = document.querySelector(".btnSave"); -const notes = document.querySelector(".notes-container"); - -// Получаем кнопки -const boldBtn = document.getElementById("boldBtn"); -const italicBtn = document.getElementById("italicBtn"); -const headerBtn = document.getElementById("headerBtn"); -const listBtn = document.getElementById("listBtn"); -const quoteBtn = document.getElementById("quoteBtn"); -const codeBtn = document.getElementById("codeBtn"); -const linkBtn = document.getElementById("linkBtn"); - -function getFormattedDateTime() { - let now = new Date(); - let day = String(now.getDate()).padStart(2, "0"); - let month = String(now.getMonth() + 1).padStart(2, "0"); - let year = now.getFullYear(); - let hours = String(now.getHours()).padStart(2, "0"); - let minutes = String(now.getMinutes()).padStart(2, "0"); - - return { - date: `${day}.${month}.${year}`, - time: `${hours}:${minutes}`, - }; -} - -// Сохранить заметки в localStorage -function saveNotesToLocalStorage(notesArr) { - localStorage.setItem("notes", JSON.stringify(notesArr)); -} - -// Получить заметки из localStorage -function getNotesFromLocalStorage() { - return JSON.parse(localStorage.getItem("notes")) || []; -} - -// Обновить функцию renderNotes -function renderNotes() { - const notesArr = getNotesFromLocalStorage(); - notes.innerHTML = ""; // Очищаем контейнер перед рендерингом - - notesArr.forEach(function (content, index) { - const noteHtml = ` -
-
- ${content.date} ${content.time} -
Редактировать
-
Удалить
-
-
${marked.parse(content.content)}
-
- `; - notes.insertAdjacentHTML("afterbegin", noteHtml); - }); - - deleteNote(); - editNote(); -} - -// Обновить функцию saveNote -function saveNote() { - btnSave.addEventListener("click", function () { - if (textInput.value.trim() !== "") { - let { date, time } = getFormattedDateTime(); - - const note = { - content: textInput.value, - date: date, - time: time, - }; - - const notesArr = getNotesFromLocalStorage(); - notesArr.push(note); - saveNotesToLocalStorage(notesArr); - - textInput.value = ""; - textInput.style.height = "auto"; // Сбрасываем размер текстового поля - renderNotes(); - } - }); -} - -// Обновить функцию deleteNote -function deleteNote() { - document.querySelectorAll("#deleteBtn").forEach((btn) => { - btn.addEventListener("click", function (event) { - let index = event.target.dataset.index; - const notesArr = getNotesFromLocalStorage(); - notesArr.splice(index, 1); - saveNotesToLocalStorage(notesArr); - renderNotes(); - }); - }); -} - -// Обновить функцию editNote -function editNote() { - document.querySelectorAll("#editBtn").forEach((btn) => { - btn.addEventListener("click", function (event) { - let index = event.target.dataset.index; - let noteContainer = event.target.closest("#note"); - let noteContent = noteContainer.querySelector(".textNote"); - - // Создаем textarea с уже существующим классом textInput - let textarea = document.createElement("textarea"); - textarea.classList.add("textInput"); - textarea.value = noteContent.textContent; - - // Привязываем авторасширение к textarea для редактирования - textarea.addEventListener("input", function () { - autoExpandTextarea(textarea); - }); - autoExpandTextarea(textarea); - - // Кнопка сохранить - let saveEditBtn = document.createElement("button"); - saveEditBtn.textContent = "Сохранить"; - saveEditBtn.classList.add("btnSave"); - - // Очищаем текущий контент и вставляем textarea и кнопку сохранить - noteContent.innerHTML = ""; - noteContent.appendChild(textarea); - noteContent.appendChild(saveEditBtn); - - saveEditBtn.addEventListener("click", function () { - if (textarea.value.trim() !== "") { - let { date, time } = getFormattedDateTime(); - const notesArr = getNotesFromLocalStorage(); - notesArr[index] = { - content: textarea.value, - date: date, - time: time, - }; - saveNotesToLocalStorage(notesArr); - renderNotes(); - } - }); - }); - }); -} - -// Функция для авторасширения текстового поля -function autoExpandTextarea(textarea) { - textarea.style.height = "auto"; - textarea.style.height = textarea.scrollHeight + "px"; -} - -// Привязываем авторасширение к текстовому полю для создания заметки -textInput.addEventListener("input", function () { - autoExpandTextarea(textInput); -}); - -// Изначально запускаем для установки правильной высоты -autoExpandTextarea(textInput); - -function insertMarkdown(tag) { - const start = textInput.selectionStart; - const end = textInput.selectionEnd; - const text = textInput.value; - - const before = text.substring(0, start); - const selected = text.substring(start, end); - const after = text.substring(end); - - if (selected.startsWith(tag) && selected.endsWith(tag)) { - // Если теги уже есть, удаляем их - textInput.value = `${before}${selected.slice( - tag.length, - -tag.length - )}${after}`; - textInput.setSelectionRange(start, end - 2 * tag.length); - } else if (selected.trim() === "") { - // Если текст не выделен - if (tag === "[Текст ссылки](URL)") { - // Для ссылок создаем шаблон с двумя кавычками - textInput.value = `${before}[Текст ссылки](URL)${after}`; - const cursorPosition = start + 1; // Помещаем курсор внутрь текста ссылки - textInput.setSelectionRange(cursorPosition, cursorPosition + 12); - } else if (tag === "- " || tag === "> " || tag === "# ") { - // Для списка, цитаты и заголовка помещаем курсор после `- `, `> ` или `# ` - textInput.value = `${before}${tag}${after}`; - const cursorPosition = start + tag.length; - textInput.setSelectionRange(cursorPosition, cursorPosition); - } else { - // Для остальных типов создаем два тега - textInput.value = `${before}${tag}${tag}${after}`; - const cursorPosition = start + tag.length; - textInput.setSelectionRange(cursorPosition, cursorPosition); - } - } else { - // Если текст выделен - if (tag === "[Текст ссылки](URL)") { - // Для ссылок используем выделенный текст вместо "Текст ссылки" - textInput.value = `${before}[${selected}](URL)${after}`; - const cursorPosition = start + selected.length + 3; // Помещаем курсор в URL - textInput.setSelectionRange(cursorPosition, cursorPosition + 3); - } else if (tag === "- " || tag === "> " || tag === "# ") { - // Для списка, цитаты и заголовка добавляем `- `, `> ` или `# ` перед выделенным текстом - textInput.value = `${before}${tag}${selected}${after}`; - const cursorPosition = start + tag.length + selected.length; - textInput.setSelectionRange(cursorPosition, cursorPosition); - } else { - // Для остальных типов оборачиваем выделенный текст - textInput.value = `${before}${tag}${selected}${tag}${after}`; - const cursorPosition = start + tag.length + selected.length + tag.length; - textInput.setSelectionRange(cursorPosition, cursorPosition); - } - } - - textInput.focus(); -} - -// Обработчики для кнопок -boldBtn.addEventListener("click", function () { - insertMarkdown("**"); // Вставляем жирный текст -}); - -italicBtn.addEventListener("click", function () { - insertMarkdown("*"); // Вставляем курсив -}); - -headerBtn.addEventListener("click", function () { - insertMarkdown("# "); // Вставляем заголовок -}); - -listBtn.addEventListener("click", function () { - insertMarkdown("- "); // Вставляем элемент списка -}); - -quoteBtn.addEventListener("click", function () { - insertMarkdown("> "); // Вставляем цитату -}); - -codeBtn.addEventListener("click", function () { - insertMarkdown("`"); // Вставляем код -}); - -linkBtn.addEventListener("click", function () { - insertMarkdown("[Текст ссылки](URL)"); // Вставляем ссылку -}); - -// Удалено дублирование добавления кнопок Markdown в окно сохранения заметки -// Кнопки уже добавлены в HTML (index.html), поэтому их повторное создание не требуется - -renderNotes(); -saveNote(); diff --git a/favicon-16.png b/favicon-16.png new file mode 100644 index 0000000..e530f14 Binary files /dev/null and b/favicon-16.png differ diff --git a/favicon-32.png b/favicon-32.png new file mode 100644 index 0000000..53fc3f8 Binary files /dev/null and b/favicon-32.png differ diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..71f127a Binary files /dev/null and b/favicon.ico differ diff --git a/icon-192.png b/icon-192.png new file mode 100644 index 0000000..7f89b74 Binary files /dev/null and b/icon-192.png differ diff --git a/icon-512.png b/icon-512.png new file mode 100644 index 0000000..bf7defc Binary files /dev/null and b/icon-512.png differ diff --git a/index.html b/index.html deleted file mode 100644 index 0c71d4c..0000000 --- a/index.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - Document - - - - -
-
Заметки
-
-
- - - - - - - -
- - - -
-
-
-
-
-
-
-
-
-
-
- - - - - diff --git a/manifest.json b/manifest.json index 129f529..5543802 100644 --- a/manifest.json +++ b/manifest.json @@ -7,12 +7,12 @@ "theme_color": "#000000", "icons": [ { - "src": "project.png", + "src": "icon-192.png", "sizes": "192x192", "type": "image/png" }, { - "src": "project.png", + "src": "icon-512.png", "sizes": "512x512", "type": "image/png" } diff --git a/plan.txt b/plan.txt deleted file mode 100644 index af58597..0000000 --- a/plan.txt +++ /dev/null @@ -1,2 +0,0 @@ -✅ 1. Сделать создание заметки по нажатию alt + enter. И написать подсказку возле кнопки Сохранить. -✅ 2. Добавить личный кабинет, который открывается при нажатии на свой ник. В личном кабинете можно менять регистрационные данные, а так же привязать свой эмейл и поставить аватарку. Картинки аватарки загружаются на сервер. \ No newline at end of file diff --git a/public/notes.html b/public/notes.html index d16e240..7c2ff41 100644 --- a/public/notes.html +++ b/public/notes.html @@ -4,7 +4,7 @@ Заметки - + - +
+
Пн
+
Вт
+
Ср
+
Чт
+
Пт
+
Сб
+
Вс
+
+
+
diff --git a/public/style-calendar.css b/public/style-calendar.css new file mode 100644 index 0000000..e0e4c9c --- /dev/null +++ b/public/style-calendar.css @@ -0,0 +1,144 @@ +/* Компактные стили для календаря */ +.main-wrapper { + display: flex; + gap: 15px; + align-items: flex-start; + width: 100%; +} + +.calendar-box { + flex-shrink: 0; + width: 190px; + background: white; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + border-radius: 8px; + padding: 10px; +} + +.calendar-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 8px; + gap: 4px; +} + +.calendar-header h3 { + margin: 0; + font-size: 11px; + font-weight: 600; + color: #333; + min-width: 75px; + text-align: center; + flex: 1; +} + +.calendar-nav-btn { + background: #f0f0f0; + border: 1px solid #ddd; + border-radius: 3px; + padding: 2px 5px; + cursor: pointer; + font-size: 11px; + color: #333; + transition: all 0.2s ease; +} + +.calendar-nav-btn:hover { + background: #007bff; + color: white; + border-color: #007bff; +} + +.calendar-weekdays { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 1px; + margin-bottom: 5px; +} + +.weekday { + text-align: center; + font-size: 8px; + font-weight: 600; + color: #666; + padding: 1px 0; + text-transform: uppercase; +} + +.calendar-days { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 1px; +} + +.calendar-day { + aspect-ratio: 1; + display: flex; + align-items: center; + justify-content: center; + font-size: 10px; + border-radius: 3px; + cursor: pointer; + background: #f8f9fa; + color: #666; + border: 1px solid transparent; + transition: all 0.2s ease; + position: relative; +} + +.calendar-day:hover { + background: #e8f4f8; + border-color: #007bff; +} + +.calendar-day.other-month { + color: #ccc; + background: #fafafa; +} + +.calendar-day.today { + background: #007bff; + color: white; + font-weight: 600; + border-color: #0056cc; +} + +.calendar-day.today:hover { + background: #0056cc; +} + +.calendar-day.selected { + background: #28a745; + color: white; + font-weight: 600; + border-color: #1e7e34; +} + +.calendar-day.selected:hover { + background: #1e7e34; +} + +.main { + flex: 1; + min-width: 300px; +} + +/* Адаптация для мобильных устройств */ +@media (max-width: 768px) { + .main-wrapper { + flex-direction: column; + } + + .calendar-box { + width: 100%; + } + + .main { + min-width: auto; + } + + .container { + max-width: 100%; + } +} diff --git a/public/style.css b/public/style.css index afebf1c..9ca7564 100644 --- a/public/style.css +++ b/public/style.css @@ -66,7 +66,7 @@ header { .container { width: 90%; - max-width: 600px; + max-width: 850px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 8px; padding: 15px; @@ -440,41 +440,39 @@ textarea:focus { text-decoration: underline; } -/* Стили для календаря */ + +/* ========== Стили для компактного календаря ========== */ + .main-wrapper { display: flex; - gap: 20px; + gap: 15px; align-items: flex-start; width: 100%; } -.calendar-sidebar { +.calendar-box { flex-shrink: 0; - width: 240px; -} - -.calendar-container { + width: 190px; background: white; - border: 1px solid #e0e0e0; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 8px; - padding: 15px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + padding: 10px; } .calendar-header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 15px; - gap: 10px; + margin-bottom: 8px; + gap: 4px; } .calendar-header h3 { margin: 0; - font-size: 14px; + font-size: 11px; font-weight: 600; color: #333; - min-width: 120px; + min-width: 75px; text-align: center; flex: 1; } @@ -482,10 +480,10 @@ textarea:focus { .calendar-nav-btn { background: #f0f0f0; border: 1px solid #ddd; - border-radius: 4px; - padding: 4px 8px; + border-radius: 3px; + padding: 2px 5px; cursor: pointer; - font-size: 14px; + font-size: 11px; color: #333; transition: all 0.2s ease; } @@ -499,23 +497,23 @@ textarea:focus { .calendar-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); - gap: 2px; - margin-bottom: 10px; + gap: 1px; + margin-bottom: 5px; } .weekday { text-align: center; - font-size: 11px; + font-size: 8px; font-weight: 600; color: #666; - padding: 5px 0; + padding: 1px 0; text-transform: uppercase; } .calendar-days { display: grid; grid-template-columns: repeat(7, 1fr); - gap: 2px; + gap: 1px; } .calendar-day { @@ -523,8 +521,8 @@ textarea:focus { display: flex; align-items: center; justify-content: center; - font-size: 12px; - border-radius: 4px; + font-size: 10px; + border-radius: 3px; cursor: pointer; background: #f8f9fa; color: #666; @@ -565,24 +563,6 @@ textarea:focus { background: #1e7e34; } -.calendar-day.has-notes { - font-weight: 600; - color: #007bff; -} - -.calendar-day.has-notes::before { - content: ''; - display: block; - width: 4px; - height: 4px; - background: #007bff; - border-radius: 50%; - position: absolute; - bottom: 2px; - left: 50%; - transform: translateX(-50%); -} - .main { flex: 1; min-width: 300px; @@ -594,19 +574,23 @@ textarea:focus { flex-direction: column; } - .calendar-sidebar { + .calendar-box { width: 100%; } - .calendar-container { - max-width: 100%; - } - .main { min-width: auto; } .container { - max-width: 900px; + max-width: 100%; } } + +/* Стиль для основного блока заметок */ +.main { + background: white; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + border-radius: 8px; + padding: 15px; +} diff --git a/service-worker.js b/service-worker.js deleted file mode 100644 index 0644d99..0000000 --- a/service-worker.js +++ /dev/null @@ -1,7 +0,0 @@ -self.addEventListener("install", (event) => { - console.log("Service Worker установлен."); -}); - -self.addEventListener("fetch", (event) => { - console.log("Обработан запрос:", event.request.url); -}); diff --git a/style.css b/style.css deleted file mode 100644 index 9da575e..0000000 --- a/style.css +++ /dev/null @@ -1,178 +0,0 @@ -body { - font-family: "Open Sans", sans-serif; - padding: 0; - margin: 0; - display: flex; - flex-wrap: wrap; - justify-content: center; - background: #f5f5f5; -} - -header { - font-size: 20px; - font-weight: bold; - margin-bottom: 20px; -} - -.container { - width: 90%; - max-width: 600px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - border-radius: 8px; - padding: 15px; - margin-top: 20px; - background: white; -} - -textarea { - width: 100%; - min-height: 50px; /* Минимальная высота */ - resize: none; /* Отключаем возможность ручного изменения размера */ - border: none; - background: white; - margin-bottom: 5px; - overflow-y: hidden; /* Отключаем полосу прокрутки по вертикали */ -} - -textarea:focus { - outline: none; /* Убираем обводку */ -} - -.btnSave { - padding: 5px; - cursor: pointer; - border-width: 1px; - background: white; - border-radius: 5px; - font-family: "Open Sans", sans-serif; - transition: all 0.3s ease; -} - -.date { - font-size: 11px; - color: grey; -} - -.notesHeaderBtn { - display: inline-block; - cursor: pointer; - color: black; - font-weight: bold; -} - -.textNote { - margin-top: 10px; - white-space: pre-wrap; -} - -/* Убираем стандартные отступы для абзацев */ -.textNote p { - margin: 0; - padding: 0; -} - -/* Убираем маргины у заголовков */ -.textNote h1, -.textNote h2, -.textNote h3, -.textNote h4, -.textNote h5, -.textNote h6 { - margin: 0; - padding: 0; -} - -/* Убираем отступы у списков */ -.textNote ul, -.textNote ol { - margin: 0; - padding-left: 20px; -} - -/* Убираем маргины у элементов списка */ -.textNote li { - margin: 0; - padding: 0; -} - -/* Стили для ссылок */ -.textNote a { - color: #007bff; - text-decoration: none; -} - -.textNote a:hover { - text-decoration: underline; -} - -/* Стили для цитат */ -.textNote blockquote { - border-left: 4px solid #007bff; - padding-left: 16px; - margin: 10px 0; - color: #555; - font-style: italic; - background-color: #f8f9fa; - padding: 10px 16px; - border-radius: 0 4px 4px 0; -} - -.textNote blockquote p { - margin: 0; -} - -/* Стили для кода */ -.textNote pre { - background-color: #f5f5f5; - padding: 10px; - border-radius: 5px; - font-size: 14px; - overflow-x: auto; -} - -.textNote code { - background-color: #f5f5f5; - padding: 2px 4px; - border-radius: 5px; - font-size: 14px; -} - -.notes-container { - width: 100%; - display: flex; - flex-direction: column; /* Располагаем элементы в колонку */ - align-items: center; /* Центрируем */ -} - -.markdown-buttons { - margin-top: 10px; - margin-bottom: 10px; -} - -.markdown-buttons .btnMarkdown { - padding: 5px 10px; - margin-right: 5px; - cursor: pointer; - border: 1px solid #ddd; - background-color: #f0f0f0; - border-radius: 5px; - font-size: 14px; -} - -.markdown-buttons .btnMarkdown:hover { - background-color: #e0e0e0; -} - -.footer { - text-align: center; - font-size: 12px; - color: #999; - position: fixed; - bottom: 0; - width: 100%; - padding: 10px 0; -} - -.footer span { - font-weight: bold; -}