From a78d976bcf0333867b1580078e51a66bc37f3557 Mon Sep 17 00:00:00 2001 From: Fovway Date: Tue, 4 Nov 2025 10:43:07 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B1=D0=BE=D1=87=D0=B8=D0=B9=20=D1=81=D0=BA?= =?UTF-8?q?=D1=80=D0=B8=D0=BF=D1=82=20=D0=B4=D0=BB=D1=8F=20=D1=83=D0=BB?= =?UTF-8?q?=D1=83=D1=87=D1=88=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BA=D1=8D=D1=88?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2.=20=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BC=D0=B0=D1=80=D1=88=D1=80=D1=83=D1=82=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D1=8D=D1=88=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20HTML=20=D0=B8=20=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D1=85=20=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D1=83=D1=80=D1=81=D0=BE=D0=B2,=20=D0=B0=20=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=D0=B6=D0=B5=20=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=BE?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8=20=D0=B2=D0=BD?= =?UTF-8?q?=D0=B5=D1=88=D0=BD=D0=B8=D1=85=20=D1=81=D1=81=D1=8B=D0=BB=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=20Markdown.=20=D0=A3=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=83=D1=81=D1=82=D0=B0=D1=80=D0=B5=D0=B2=D1=88?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BE=D0=BA=20=D0=B8=20=D1=81?= =?UTF-8?q?=D1=81=D1=8B=D0=BB=D0=BE=D0=BA.=20=D0=9E=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D1=81=20=D1=87=D0=B5?= =?UTF-8?q?=D0=BA=D0=B1=D0=BE=D0=BA=D1=81=D0=B0=D0=BC=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=D1=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dev-dist/sw.js | 29 ++++++++++++--- src/styles/style.css | 2 - src/utils/markdown.ts | 86 +++++++++++++++++++++++-------------------- 3 files changed, 71 insertions(+), 46 deletions(-) diff --git a/dev-dist/sw.js b/dev-dist/sw.js index 6b0fcec..d3e568e 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -81,15 +81,34 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict'; "url": "registerSW.js", "revision": "3ca0b8505b4bec776b69afdba2768812" }, { - "url": "index.html", - "revision": "0.o51qplqi6t" + "url": "/index.html", + "revision": "0.3mraah3n9jg" }], {}); workbox.cleanupOutdatedCaches(); - workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { - allowlist: [/^\/$/] + workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("/index.html"), { + allowlist: [/^\/$/], + denylist: [/^\/api/, /^\/_/] })); + workbox.registerRoute(({ + request + }) => request.destination === "document" || request.url.endsWith("/") || request.url.endsWith("/index.html"), new workbox.NetworkFirst({ + "cacheName": "html-cache", + "networkTimeoutSeconds": 3, + plugins: [new workbox.ExpirationPlugin({ + maxEntries: 10, + maxAgeSeconds: 86400 + })] + }), 'GET'); + workbox.registerRoute(/\.(?:js|css|woff|woff2|ttf|eot)$/, new workbox.CacheFirst({ + "cacheName": "static-resources-cache", + plugins: [new workbox.ExpirationPlugin({ + maxEntries: 200, + maxAgeSeconds: 31536000 + })] + }), 'GET'); workbox.registerRoute(/^https:\/\/api\./, new workbox.NetworkFirst({ "cacheName": "api-cache", + "networkTimeoutSeconds": 3, plugins: [new workbox.ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 3600 @@ -97,7 +116,7 @@ define(['./workbox-9dc17825'], (function (workbox) { 'use strict'; }), 'GET'); workbox.registerRoute(/\/api\//, new workbox.NetworkFirst({ "cacheName": "api-cache-local", - "networkTimeoutSeconds": 10, + "networkTimeoutSeconds": 3, plugins: [new workbox.ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 86400 diff --git a/src/styles/style.css b/src/styles/style.css index 32f9652..e071cf4 100644 --- a/src/styles/style.css +++ b/src/styles/style.css @@ -118,7 +118,6 @@ body { button { -webkit-tap-highlight-color: transparent; -moz-tap-highlight-color: transparent; - tap-highlight-color: transparent; outline: none; box-shadow: none; } @@ -137,7 +136,6 @@ a, div[role="button"] { -webkit-tap-highlight-color: transparent; -moz-tap-highlight-color: transparent; - tap-highlight-color: transparent; outline: none; box-shadow: none; } diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts index 6f5f4df..ce2c9b8 100644 --- a/src/utils/markdown.ts +++ b/src/utils/markdown.ts @@ -24,50 +24,58 @@ const spoilerExtension = { }; // Кастомный renderer для внешних ссылок и чекбоксов -const renderer: any = { - link(token: any) { - const href = token.href; - const title = token.title; - const text = token.text; +const renderer = new marked.Renderer(); +// Переопределяем link для внешних ссылок +const originalLink = renderer.link.bind(renderer); +renderer.link = function(token: any) { + const href = token.href; + const title = token.title; + const text = token.text; + + try { + const url = new URL(href, window.location.href); + const isExternal = url.origin !== window.location.origin; + + if (isExternal) { + return `${text}`; + } + } catch {} + + return originalLink(token); +}; + +// Переопределяем listitem для поддержки чекбоксов +renderer.listitem = function(token: any) { + const task = token.task; + const checked = token.checked; + + // Получаем токены для обработки + const tokens = token.tokens || []; + let text: string; + + // Используем this.parser.parseInline для обработки токенов (this указывает на renderer) + if (tokens.length > 0) { try { - const url = new URL(href, window.location.href); - const isExternal = url.origin !== window.location.origin; - - if (isExternal) { - return `${text}`; - } - } catch {} - - return `${text}`; - }, - // Кастомный renderer для элементов списка с чекбоксами - listitem(token: any) { - const task = token.task; - const checked = token.checked; - - // Используем tokens для правильной обработки форматирования внутри элементов списка - // token.tokens содержит массив токенов для вложенного содержимого - const tokens = token.tokens || []; - let text: string; - - if (tokens.length > 0) { - // Используем this.parser.parseInline для правильной обработки вложенного форматирования - // this указывает на экземпляр Parser в контексте renderer text = this.parser.parseInline(tokens); - } else { - // Fallback на token.text, если tokens отсутствуют + } catch (error) { + // Если ошибка, используем fallback + console.warn('Error parsing list item tokens:', error); text = token.text || ''; } - - if (task) { - const checkbox = ``; - return `
  • ${checkbox} ${text}
  • \n`; - } - return `
  • ${text}
  • \n`; - }, + } else { + text = token.text || ''; + } + + // Если это задача (чекбокс), добавляем чекбокс + if (task) { + const checkbox = ``; + return `
  • ${checkbox} ${text}
  • \n`; + } + + return `
  • ${text}
  • \n`; }; // Настройка marked