From 8fd529302f452953b2a2cddf8c53425cec745143 Mon Sep 17 00:00:00 2001 From: Fovway Date: Sat, 25 Oct 2025 17:14:04 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=BE=20=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=D0=BD=D0=BE=D0=B9=20=D0=B8=20=D1=81=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=BB=D0=BE=D0=B9=20=D1=82=D0=B5=D0=BC=D1=8B=20=D0=B2=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены функции для переключения между темной и светлой темами с использованием localStorage. - Обновлены стили для поддержки темной темы, включая цвета фона, текста и иконок. - Добавлены кнопки для переключения темы на страницах входа, профиля, заметок и настроек. - Оптимизирован код для предотвращения мерцания темы при загрузке страницы. --- .continue/agents/new-config.yaml | 13 - .continue/mcpServers/new-mcp-server.yaml | 10 - public/app.js | 91 ++++++- public/index.html | 37 ++- public/login.js | 68 ++++- public/notes.html | 24 ++ public/profile.html | 24 ++ public/profile.js | 68 ++++- public/register.html | 37 ++- public/register.js | 68 ++++- public/settings.html | 24 ++ public/settings.js | 62 +++++ public/style.css | 300 ++++++++++++++++------- 13 files changed, 691 insertions(+), 135 deletions(-) delete mode 100644 .continue/agents/new-config.yaml delete mode 100644 .continue/mcpServers/new-mcp-server.yaml diff --git a/.continue/agents/new-config.yaml b/.continue/agents/new-config.yaml deleted file mode 100644 index a1a43ed..0000000 --- a/.continue/agents/new-config.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# This is an example configuration file -# To learn more, see the full config.yaml reference: https://docs.continue.dev/reference - -name: Example Config -version: 1.0.0 -schema: v1 - -models: - - name: qwen/qwen3-235b-a22b:free - provider: openrouter - model: qwen/qwen3-235b-a22b:free - apiBase: https://openrouter.ai/api/v1 - apiKey: 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 diff --git a/.continue/mcpServers/new-mcp-server.yaml b/.continue/mcpServers/new-mcp-server.yaml deleted file mode 100644 index 0e32aa6..0000000 --- a/.continue/mcpServers/new-mcp-server.yaml +++ /dev/null @@ -1,10 +0,0 @@ -name: New MCP server -version: 0.0.1 -schema: v1 -mcpServers: - - name: New MCP server - command: npx - args: - - -y - - - env: {} diff --git a/public/app.js b/public/app.js index 671bc0a..9c8aca8 100644 --- a/public/app.js +++ b/public/app.js @@ -14,7 +14,7 @@ async function cacheAvatar(avatarUrl) { const cacheData = { base64: base64, timestamp: Date.now(), - url: avatarUrl + url: avatarUrl, }; localStorage.setItem(AVATAR_CACHE_KEY, JSON.stringify(cacheData)); @@ -475,7 +475,7 @@ function insertMarkdown(tag) { // Определяем, какие теги оборачивают текст (нуждаются в двойных тегах) const wrappingTags = ["**", "*", "`"]; - const isWrappingTag = wrappingTags.some(wrapTag => tag.startsWith(wrapTag)); + const isWrappingTag = wrappingTags.some((wrapTag) => tag.startsWith(wrapTag)); if (isWrappingTag && selected.startsWith(tag) && selected.endsWith(tag)) { // Если оборачивающие теги уже есть, удаляем их @@ -619,7 +619,7 @@ function insertMarkdownForEdit(textarea, tag) { // Определяем, какие теги оборачивают текст (нуждаются в двойных тегах) const wrappingTags = ["**", "*", "`"]; - const isWrappingTag = wrappingTags.some(wrapTag => tag.startsWith(wrapTag)); + const isWrappingTag = wrappingTags.some((wrapTag) => tag.startsWith(wrapTag)); if (isWrappingTag && selected.startsWith(tag) && selected.endsWith(tag)) { // Если оборачивающие теги уже есть, удаляем их @@ -815,11 +815,11 @@ headerBtn.addEventListener("click", function (event) { // Если меню выходит за правую границу, позиционируем его слева if (rect.right > viewportWidth) { - headerDropdown.style.left = 'auto'; - headerDropdown.style.right = '0'; + headerDropdown.style.left = "auto"; + headerDropdown.style.right = "0"; } else { - headerDropdown.style.left = '0'; - headerDropdown.style.right = 'auto'; + headerDropdown.style.left = "0"; + headerDropdown.style.right = "auto"; } headerDropdown.classList.toggle("show"); @@ -1412,7 +1412,9 @@ async function renderNotes(notes) { const updated = parseSQLiteUtc(note.updated_at); dateDisplay = `${formatLocalDateTime( created - )} ${formatLocalDateTime(updated)}`; + )} | ${formatLocalDateTime( + updated + )}`; } else { dateDisplay = formatLocalDateTime(created); } @@ -1626,7 +1628,11 @@ function addNoteEventListeners() { const markdownButtons = [ { id: "editBoldBtn", icon: "mdi:format-bold", tag: "**" }, { id: "editItalicBtn", icon: "mdi:format-italic", tag: "*" }, - { id: "editStrikethroughBtn", icon: "mdi:format-strikethrough", tag: "~~" }, + { + id: "editStrikethroughBtn", + icon: "mdi:format-strikethrough", + tag: "~~", + }, { id: "editColorBtn", icon: "mdi:palette", tag: "color" }, { id: "editHeaderBtn", icon: "mdi:format-header-pound", tag: "header" }, { id: "editListBtn", icon: "mdi:format-list-bulleted", tag: "- " }, @@ -1686,7 +1692,8 @@ function addNoteEventListeners() { // Обработчик открытия/закрытия dropdown headerBtn.addEventListener("click", function (e) { e.stopPropagation(); - headerDropdown.style.display = headerDropdown.style.display === "none" ? "block" : "none"; + headerDropdown.style.display = + headerDropdown.style.display === "none" ? "block" : "none"; headerBtn.classList.toggle("active"); }); @@ -2741,7 +2748,7 @@ async function loadUserInfo() { // Загружаем аватарку с сервера и кэшируем userAvatar.src = user.avatar; // Кэшируем в фоне - cacheAvatar(user.avatar).then(success => { + cacheAvatar(user.avatar).then((success) => { if (success) { console.log("Аватарка закэширована (notes)"); } @@ -3530,3 +3537,65 @@ function initSearchMobile() { } }); } + +// Логика переключения темы +function initThemeToggle() { + const themeToggleBtn = document.getElementById("theme-toggle-btn"); + + if (!themeToggleBtn) return; + + // Загружаем сохраненную тему или используем системную + const savedTheme = localStorage.getItem("theme"); + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + const currentTheme = savedTheme || systemTheme; + + // Применяем тему + applyTheme(currentTheme); + + // Обработчик клика на переключатель + themeToggleBtn.addEventListener("click", () => { + const currentTheme = document.documentElement.getAttribute("data-theme"); + const newTheme = currentTheme === "dark" ? "light" : "dark"; + applyTheme(newTheme); + localStorage.setItem("theme", newTheme); + }); + + // Слушаем изменения системной темы + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", (e) => { + if (!localStorage.getItem("theme")) { + applyTheme(e.matches ? "dark" : "light"); + } + }); +} + +function applyTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + + // Обновляем meta теги для PWA + const themeColorMeta = document.querySelector('meta[name="theme-color"]'); + if (themeColorMeta) { + themeColorMeta.setAttribute( + "content", + theme === "dark" ? "#1a1a1a" : "#007bff" + ); + } + + // Обновляем иконку переключателя + const themeToggleBtn = document.getElementById("theme-toggle-btn"); + if (themeToggleBtn) { + const icon = themeToggleBtn.querySelector(".iconify"); + if (icon) { + icon.setAttribute( + "data-icon", + theme === "dark" ? "mdi:weather-sunny" : "mdi:weather-night" + ); + } + } +} + +// Инициализируем переключатель темы при загрузке страницы +document.addEventListener("DOMContentLoaded", initThemeToggle); diff --git a/public/index.html b/public/index.html index cd3d727..569a722 100644 --- a/public/index.html +++ b/public/index.html @@ -17,6 +17,23 @@ } catch (e) {} + + +
- Вход в систему +
+ Вход в + систему + +
+ К заметкам diff --git a/public/profile.js b/public/profile.js index 65db47a..9b51f5d 100644 --- a/public/profile.js +++ b/public/profile.js @@ -1,3 +1,65 @@ +// Логика переключения темы +function initThemeToggle() { + const themeToggleBtn = document.getElementById("theme-toggle-btn"); + + if (!themeToggleBtn) return; + + // Загружаем сохраненную тему или используем системную + const savedTheme = localStorage.getItem("theme"); + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + const currentTheme = savedTheme || systemTheme; + + // Применяем тему + applyTheme(currentTheme); + + // Обработчик клика на переключатель + themeToggleBtn.addEventListener("click", () => { + const currentTheme = document.documentElement.getAttribute("data-theme"); + const newTheme = currentTheme === "dark" ? "light" : "dark"; + applyTheme(newTheme); + localStorage.setItem("theme", newTheme); + }); + + // Слушаем изменения системной темы + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", (e) => { + if (!localStorage.getItem("theme")) { + applyTheme(e.matches ? "dark" : "light"); + } + }); +} + +function applyTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + + // Обновляем meta теги для PWA + const themeColorMeta = document.querySelector('meta[name="theme-color"]'); + if (themeColorMeta) { + themeColorMeta.setAttribute( + "content", + theme === "dark" ? "#1a1a1a" : "#007bff" + ); + } + + // Обновляем иконку переключателя + const themeToggleBtn = document.getElementById("theme-toggle-btn"); + if (themeToggleBtn) { + const icon = themeToggleBtn.querySelector(".iconify"); + if (icon) { + icon.setAttribute( + "data-icon", + theme === "dark" ? "mdi:weather-sunny" : "mdi:weather-night" + ); + } + } +} + +// Инициализируем переключатель темы при загрузке страницы +document.addEventListener("DOMContentLoaded", initThemeToggle); + // DOM элементы const avatarImage = document.getElementById("avatarImage"); const avatarPlaceholder = document.getElementById("avatarPlaceholder"); @@ -28,7 +90,7 @@ async function cacheAvatar(avatarUrl) { const cacheData = { base64: base64, timestamp: Date.now(), - url: avatarUrl + url: avatarUrl, }; localStorage.setItem(AVATAR_CACHE_KEY, JSON.stringify(cacheData)); @@ -165,7 +227,7 @@ async function loadProfile() { // Загружаем аватарку с сервера и кэшируем avatarImage.src = user.avatar; // Кэшируем в фоне - cacheAvatar(user.avatar).then(success => { + cacheAvatar(user.avatar).then((success) => { if (success) { console.log("Аватарка закэширована"); } @@ -232,7 +294,7 @@ avatarInput.addEventListener("change", async function (event) { deleteAvatarBtn.style.display = "inline-block"; // Обновляем кэш с новой аватаркой - cacheAvatar(result.avatar).then(success => { + cacheAvatar(result.avatar).then((success) => { if (success) { console.log("Новая аватарка закэширована"); } diff --git a/public/register.html b/public/register.html index 8123957..6f86bf2 100644 --- a/public/register.html +++ b/public/register.html @@ -16,6 +16,23 @@ } catch (e) {} + + +
- Регистрация +
+ + Регистрация + +