PageSourceSearch

https://discours.netlify.app/scripts/ctrl-misc-838160e4.js

js discours.netlify.app collected 2026-10-03 10:41:01 UTC 1,305,726 bytes, 1 lines download raw bytes

1(globalThis.webpackChunkdiscours_io_frontend=globalThis.webpackChunkdiscours_io_frontend||[]).push([[981],{9803(){angular.module("discours.components").component("donationFaq",{restrict:"E",bindings:{},templateUrl:"/components/donationFaq/donationFaq.html",controller:["$scope","config","localeService",function(a,t,s){return this.isEnglish=s.isEnglish(),a.toDonateBlock=()=>{const a=document.getElementById("donate-form");if(a){const s=a.getBoundingClientRect().top+window.pageYOffset-t.headerHeight-270;window.scrollTo({top:s,behavior:"smooth"})}},this}]})},10961(){"use strict";var a=[{mapId:"discours-main",name:"Карта Дискурса — общая",subtitle:"экосистема смыслов открытой редакции"}];angular.module("discours.components").component("mapsListPage",{templateUrl:"/pages/maps/maps-list.html",controller:["apiClient","loginService","$location","$scope","Notification","ConfirmDialog","eventBus",function(t,s,n,e,l,g,o){function c(){i.loading=!0,i.canCreate=s.isAdmin()||s.isAuthor(),i.currentUserId=s.userId(),t.getList("concept-maps").then(function(a){i.maps=Array.isArray(a)?a:[]}).catch(function(t){i.maps=a}).finally(function(){i.loading=!1})}var i=this,d={title:"Войдите для доступа к картам",onSuccess:function(){i.requiresAuth=!1,c()},onCancel:function(){n.path("/")}};i.openLoginModal=function(){s.openAuthModal(d)},o.onScope(e,o.EVENTS.USER_LOGGED_OUT,function(){i.requiresAuth=!0}),o.onScope(e,o.EVENTS.USER_LOGGED_IN,function(){i.requiresAuth=!1,c()}),i.$onInit=function(){i.maps=[],s.isLoggedIn()?c():i.requiresAuth=!0},i.createMap=function(){if(i.canCreate){var a=window.prompt("Название новой карты:");if(null!==a&&(a=a.trim())){var s=function(a){var t=String(a||"").toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"");return t||(t="map-"+Date.now().toString(36)),t}(a),e=/^map-[0-9a-z]+$/.test(s)&&!/[a-z0-9]/.test(a.toLowerCase());t.post("concept-maps",{mapId:s,meta:{name:a,subtitle:""},activeView:"main",views:{main:{label:"Общая карта",subtitle:"",clusters:[],nodes:[],links:[]}}}).then(function(a){var t=a&&a.mapId||s;l.success({message:"Карта создана"}),e&&l.info({message:"Адрес карты сгенерирован автоматически: "+t,delay:5e3}),n.path("/maps/"+t)}).catch(function(a){l.error({message:t.getErrorMessage(a,"Не удалось создать карту"),delay:4e3})})}}},i.canDeleteMap=function(a){return!!(a&&a.createdBy&&a.createdBy._id&&i.currentUserId&&String(a.createdBy._id)===String(i.currentUserId))},i.deleteMap=function(a){if(a&&a.mapId&&i.canDeleteMap(a)){var s=a.mapId;g.open({title:"Удалить карту?",message:"Карта «"+(a.name||s)+"» будет удалена навсегда. Действие необратимо.",confirmText:"Удалить",type:"danger"}).then(function(){t.remove("concept-maps",s).then(function(){i.maps=i.maps.filter(function(a){return a.mapId!==s})}).catch(function(a){l.error({message:t.getErrorMessage(a,"Не удалось удалить карту"),delay:4e3})})},g.onDismissed("[mapsCtrl.deleteMap]"))}}}]}),angular.module("discours.components").component("mapsDetailPage",{templateUrl:"/pages/maps/maps-detail.html",controller:["$routeParams","$sce","loginService","$location","$scope","$window","eventBus",function(a,t,s,n,e,l,g){function o(){var s=a&&a.mapId||"discours-main";i.mapId=s;var e=window.discours&&window.discours.API_SERVER||"",l=window.discours&&window.discours.HOCUSPOCUS_SERVER||"",g=(n.hash()||"").trim();i.graphUrl=t.trustAsResourceUrl("/maps-graph/index.html?mapId="+encodeURIComponent(s)+"&api="+encodeURIComponent(e)+"&collab="+encodeURIComponent(l)+(g?"&view="+encodeURIComponent(g):""))}function c(a){if(a.origin===l.location.origin){var t=a.data;t&&"discours-map-view"===t.type&&t.view&&e.$applyAsync(function(){(n.hash()||"")!==t.view&&n.hash(t.view).replace()})}}var i=this,d={title:"Войдите для доступа к карте",onSuccess:function(){i.requiresAuth=!1,o()},onCancel:function(){n.path("/")}};i.openLoginModal=function(){s.openAuthModal(d)},g.onScope(e,g.EVENTS.USER_LOGGED_OUT,function(){i.requiresAuth=!0}),g.onScope(e,g.EVENTS.USER_LOGGED_IN,function(){i.requiresAuth=!1,o()}),i.$onInit=function(){document.body.classList.add("maps-graph-page"),s.isLoggedIn()?o():i.requiresAuth=!0},l.addEventListener("message",c),e.$on("$locationChangeSuccess",function(){var a=document.getElementById("maps-graph-frame");a&&a.contentWindow&&a.contentWindow.postMessage({type:"discours-set-view",view:(n.hash()||"").trim()},l.location.origin)}),e.$on("$destroy",function(){l.removeEventListener("message",c),document.body.classList.remove("maps-graph-page")})}]})},13185(){var a={email_subscriptions:"Рассылка Дискурса",almanac_subscriptions:"Альманах",all_subscriptions:"Все рассылки Дискурса",readers:"Лучшие публикации сезона",readers_platform:"Лучшие публикации сезона",authors:"Рассылка для авторов самиздата",donors:"Письма для друзей Дискурса",donors_stripe:"Письма для друзей Дискурса",donors_payp
1al:"Письма для друзей Дискурса",donors_patreon:"Письма для друзей Дискурса",editors:"Письма редакции",partners:"Письма для партнёров самиздата",vpn:"VPN-рассылка"};angular.module("discours.components").component("newsletterSubscriptionsPage",{templateUrl:"/pages/newsletter/subscriptions.html",controller:["apiClient","loginService","$location","Notification","$scope",function(t,s,n,e,l){function g(a){d.options=a&&a.options||[],d.prefs={},d.options.forEach(function(a){a.label||(a.label=d.audienceName(a.slug)),d.prefs[a.slug]=!!a.subscribed}),d.originalPrefs=angular.copy(d.prefs)}function o(a,s){return t.getErrorMessage?t.getErrorMessage(a,s)||s:a&&a.data&&a.data.data&&a.data.data.message||a&&a.data&&a.data.message||s}function c(a,t,s){d.retryAction=s||"",d.errorMessage=o(a,t),d.mode="error"}function i(){return d.token?(d.retryAction="",d.mode="loading",t.getList("newsletter/unsubscribe/by-token",{token:d.token}).then(function(a){d.preview=a||{},g(a),d.mode="confirm"}).catch(function(a){t.isCanceled&&t.isCanceled(a)||(function(a){var t=function(a){return a&&a.status||a&&a.response&&a.response.status||a&&a.data&&a.data.status||0}(a);return 400===t||410===t}(a)?function(a){d.retryAction="",d.errorMessage=o(a,"Ссылка устарела — откройте её заново из письма."),d.mode="expired"}(a):c(a,"Не удалось загрузить страницу отписки. Попробуйте снова или откройте ссылку заново из письма.","loadPreview"))})):Promise.resolve()}var d=this;d.mode="loading",d.token="",d.submitting=!1,d.saving=!1,d.preview=null,d.result=null,d.status=null,d.errorMessage="",d.retryAction="",d.options=[],d.prefs={},d.originalPrefs={},d.audienceName=function(t){return a[t]||t},d.previewAudiences=function(){return d.preview&&d.preview.audiences||[]},d.resultAudiences=function(){return d.result&&d.result.audiences||[]},d.umbrellaOption=function(){for(var a=0;a<d.options.length;a++)if("umbrella"===d.options[a].group)return d.options[a];return null},d.listOptions=function(){return d.options.filter(function(a){return"umbrella"!==a.group})},d.umbrellaOn=function(){var a=d.umbrellaOption();return!a||!!d.prefs[a.slug]},d.selectedSlugs=function(){var a=d.umbrellaOption(),t=a?a.slug:null;return Object.keys(d.prefs).filter(function(a){return d.prefs[a]&&a!==t})},d.hasPrefChanges=function(){return!angular.equals(d.prefs,d.originalPrefs)},d.isTargetOption=function(a){for(var t=d.previewAudiences(),s=0;s<t.length;s++)if(t[s]&&t[s].slug===a)return!0;return!1},d.savedSubscribedLabels=function(){return(d.result&&d.result.options||[]).filter(function(a){return"umbrella"!==a.group&&a.subscribed}).map(function(a){return a.label})},d.$onInit=function(){return d.token=String(n.search().token||"").trim(),d.token?i():s.isLoggedIn()?(d.mode="loading",t.getList("newsletter/subscriptions").then(function(a){d.status=a||{},g(a),d.mode="status"}).catch(function(a){t.isCanceled&&t.isCanceled(a)||(d.mode="info")})):void(d.mode="info")},d.confirmUnsubscribe=function(){if(!d.submitting&&d.token)return d.submitting=!0,d.errorMessage="",d.retryAction="",t.post("newsletter/unsubscribe/by-token",{token:d.token}).then(function(a){d.result=a||{},d.mode="done"}).catch(function(a){t.isCanceled&&t.isCanceled(a)||c(a,"Не удалось отписаться. Попробуйте снова или откройте ссылку заново из письма.","confirmUnsubscribe")}).finally(function(){d.submitting=!1})},d.saveByToken=function(){if(!d.submitting&&d.token)return d.submitting=!0,d.errorMessage="",d.retryAction="",t.post("newsletter/unsubscribe/by-token",{token:d.token,lists:d.selectedSlugs(),importantUpdates:d.umbrellaOn()}).then(function(a){d.result=a||{},d.originalPrefs=angular.copy(d.prefs),d.mode="done"}).catch(function(a){t.isCanceled&&t.isCanceled(a)||c(a,"Не удалось сохранить настройки. Попробуйте снова или откройте ссылку заново из письма.","saveByToken")}).finally(function(){d.submitting=!1})},d.retryTokenAction=function(){if(!d.submitting)switch(d.retryAction){case"loadPreview":return i();case"saveByToken":return d.saveByToken();case"confirmUnsubscribe":return d.confirmUnsubscribe();default:return}},d.saveAuthorized=function(){d.saving||(d.saving=!0,t.post("newsletter/subscriptions",{subscriptions:{lists:d.selectedSlugs(),importantUpdates:d.umbrellaOn()}}).then(function(a){a&&a.options?g(a):d.originalPrefs=angular.copy(d.prefs),e.success("Настройки подписок сохранены")}).catch(function(a){t.isCanceled&&t.isCanceled(a)||(e.error(a&&a.data&&a.data.message||"Не удалось сохранить настройки"),l.$applyAsync())}).finally(function(){d.saving=!1}))}}]})},13484(){!function a(t){try{var s=document.querySelector("[ng-app]")||document.body,n=angular.element(s).injector();if(!n)return t<5?void setTimeout(function(){a(t+1)},50*Math.pow(2,t)):void 0;var e=n.get("$templateCache");e.put("/components/donateBestArticle/donateBestArticle.html",'<a class="donate-best-article" ng-href="{{ $ctrl.articleData.link || $ctrl.articleData.externalLink }}" target="_blank" rel="noopener noreferrer" > <div class="donate-best-article__image"> <img ng-src="{{ $ctrl.articleData.imageURL }}" alt="" loading="lazy" decoding="async" width="300" height="200" /> </div> <div class="donate-best-article__category">{{ $ctrl.articleData.category | t }}</div> <div> <span class="donate-best-article__title"> {{ $ctrl.articleData.title | t }} </span> </div> <div class="donate-best-article__description" ng-bind-html="($ctrl.articleData.description | t) | trustedHtml" post-images="post-images" ></div> </a>'),e.put("/components/donationFaq/donationFaq.html",'<details> <summary>{{ \'На что пойдут пожертвования\' | t }}</summary>
1 <div ng-if="!$ctrl.isEnglish"> <p> Все пожертвованные средства пойдут на&nbsp;развитие проекта, подготовку новых материалов, техническую поддержку платформы, оплату серверов, труда редакторов, IT-специалистов, дизайнеров, премирование авторов лучших материалов, бухгалтерию, юридическую поддержку редакции, а&nbsp;также на&nbsp;разработку сервисов для журналистского сотрудничества и&nbsp;новых инструментов для создания мультимедийных публикаций —&nbsp;от&nbsp;тестов, галерей и&nbsp;подкастов до&nbsp;спецпроектов, видеоформатов и коллективных историй авторов и&nbsp;читателей. Чем больше ресурсов у&nbsp;нас будет, тем больше новых проектов и&nbsp;мощных публикаций самиздат сможет выпускать. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> Every donation goes toward developing the project, preparing new publications, maintaining the platform, paying for servers and the work of editors, IT specialists, and designers, rewarding the authors of the best pieces, accounting, and legal support for the editorial team — as well as building services for journalistic collaboration and new tools for creating multimedia publications, from quizzes, galleries, and podcasts to special projects, video formats, and collective stories by authors and readers. The more resources we have, the more new projects and powerful publications Discours will be able to release. </p> </div> </details> <details> <summary>{{ \'Что самиздату удалось с вашей помощью\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> С вашей помощью за десять лет работы самиздата мы <a href="https://discours.io/best" rel="noopener noreferrer" target="_blank">создали</a> более четырех тысяч крутейших материалов на самые разные темы — от бизнеса и экологии до философии и поэзии, записали <a href="https://discours.io/podcasts" rel="noopener noreferrer" target="_blank" >более сотни подкастов</a >, помогли сотням авторов и исследователей поделиться <a href="https://discours.io/articles/all" rel="noopener noreferrer" target="_blank" >своими историями</a >, уникальной экспертизой, опытом и знаниями, <a href="https://discours.io/tags/interview" rel="noopener noreferrer" target="_blank" >беседовали</a > с учеными, культурными деятелями и активистами, первыми делились новыми <a href="https://discours.io/articles/theory" rel="noopener noreferrer" target="_blank" >идеями</a >, рассказывали о ключевых <a href="https://discours.io/tags/chapters" rel="noopener noreferrer" target="_blank" >новинках</a > нон-фикшена, <a href="https://discours.io/tags/research" rel="noopener noreferrer" target="_blank" >анализировали</a > глобальные социальные проблемы, следили за изменениями в <a href="https://discours.io/articles/social" rel="noopener noreferrer" target="_blank" >обществе</a >, представляли остросоциальную <a href="https://discours.io/expo/image" rel="noopener noreferrer" target="_blank">графику</a >, публиковали независимую <a href="https://discours.io/expo/music" rel="noopener noreferrer" target="_blank">музыку</a> и новаторскую современную <a href="https://discours.io/expo/literature" rel="noopener noreferrer" target="_blank" >прозу</a >. </p> <p> Мы проводили <a href="https://www.facebook.com/events/%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D1%8B%D0%B9-%D0%BA%D0%BB%D1%83%D0%B1/%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D0%B8-%D0%BC%D0%B8%D1%85%D0%B0%D0%B8%D0%BB-%D0%B0%D0%B9%D0%B7%D0%B5%D0%BD%D0%B1%D0%B5%D1%80%D0%B3-%D0%BC%D0%B0%D1%80%D0%B8%D1%8F-%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD%D0%BE%D0%B2%D0%B0/1919034395030476/" rel="noopener noreferrer" target="_blank" >чтения</a >, <a href="https://discours.io/events/kino/premera-filma-virunga" rel="noopener noreferrer" target="_blank" >кинопоказы</a >, <a href="https://vk.com/wall-151325891_83" rel="noopener noreferrer" target="_blank" >открытые дискуссии</a > и <a href="https://vk.com/event123738166" rel="noopener noreferrer" target="_blank" >журналистские мастерские</a >, <a href="https://discours.io/events/diskussiya/diskussiya-kak-sdelat-sovremennyy-samizdat" rel="noopener noreferrer" target="_blank" >дебаты</a > и <a href="https://discours.io/events/lectures/lektsiya-mediaeksperta-vinsenta-daffi-feykovye-novosti-ili-alternativnye-fakty-kak-menyayutsya-otnosheniya-media-s-chitatelyami-v-epohu-postpravdy" rel="noopener noreferrer" target="_blank" >лекции</a >, выпустили <a href="https://vk.com/discoursio?w=wall-89236419_13256" rel="noopener noreferrer" target="_blank" >мерч</a > со слоганами «Не молчи» и «Слово может изменить мир», нарисовали <a href="https://t.me/addstickers/Discours" rel="noopener noreferrer" target="_blank" >стикеры «Дорогая редакция»</a >, которыми пользуются <a href="https://i.imgur.com/N1tbn9e.png" rel="noopener noreferrer" target="_blank">тысячи</a> участников сообщества, запустили подкаст <a href="https://discours.io/podcasts" rel="noopener noreferrer" target="_blank" >«Мир в историях»</a >, создали телеграм-бота для авторов и читателей, ведем дайджест лучших материалов сезона, дарим читателям книги, билеты на кинопоказы и планируем еще много всего. Участники открытой редакции выигрывали <a href="https://hack-wsf.equalit.ie/" rel="noopener noreferrer" target="_blank" >социально-ориентированные</a > и <a href="https://te-st.ru/reports/results-projector2022/" rel="noopener noreferrer" target="_blank" >
1дата-исследовательские</a > хакатоны, наших иллюстраторов приглашали на работу в международные издания, выходившая в самиздате нон-фикшн и художественная литература издавалась книгами, опубликованные Дискурсом пьесы ставились в театрах, а авторов журнала многократно номинировали на <a href="https://redkollegia.org" rel="noopener noreferrer" target="_blank">журналистские</a> и <a href="https://discours.io/expo/literature/prose/konstantin-kupriyanov-rasskaz-netolerantnyy-patsient" rel="noopener noreferrer" target="_blank" >литературные</a > премии. </p> <p> Вместе с вами мы помогли сотням людей рассказать на страницах самиздата потрясающие истории и быть услышанными миллионами думающих людей со всего мира. Это только начало — у нас с вами огромные планы, которые мы строим вместе. Мы знаем, что сообща сможем продолжать работу, развивать платформу для свободной журналистики и сделаем очень-очень многое, чтобы изменить мир к лучшему. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> With your help, over Discours\'s ten years of work, we have <a href="https://discours.io/best" rel="noopener noreferrer" target="_blank">created</a> more than four thousand outstanding pieces on the widest range of subjects — from business and ecology to philosophy and poetry; recorded <a href="https://discours.io/podcasts" rel="noopener noreferrer" target="_blank" >over a hundred podcasts</a >; helped hundreds of authors and researchers share <a href="https://discours.io/articles/all" rel="noopener noreferrer" target="_blank" >their stories</a >, unique expertise, experience, and knowledge; <a href="https://discours.io/tags/interview" rel="noopener noreferrer" target="_blank" >talked</a > with scholars, cultural figures, and activists; were the first to share new <a href="https://discours.io/articles/theory" rel="noopener noreferrer" target="_blank" >ideas</a >; covered key <a href="https://discours.io/tags/chapters" rel="noopener noreferrer" target="_blank" >releases</a > in non-fiction; <a href="https://discours.io/tags/research" rel="noopener noreferrer" target="_blank" >analyzed</a > global social problems; followed changes in <a href="https://discours.io/articles/social" rel="noopener noreferrer" target="_blank" >society</a >; showcased sharply social <a href="https://discours.io/expo/image" rel="noopener noreferrer" target="_blank" >graphic art</a >; published independent <a href="https://discours.io/expo/music" rel="noopener noreferrer" target="_blank">music</a> and innovative contemporary <a href="https://discours.io/expo/literature" rel="noopener noreferrer" target="_blank" >prose</a >. </p> <p> We have held <a href="https://www.facebook.com/events/%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D1%8B%D0%B9-%D0%BA%D0%BB%D1%83%D0%B1/%D0%BF%D0%BE%D0%B7%D0%B8%D1%86%D0%B8%D0%B8-%D0%BC%D0%B8%D1%85%D0%B0%D0%B8%D0%BB-%D0%B0%D0%B9%D0%B7%D0%B5%D0%BD%D0%B1%D0%B5%D1%80%D0%B3-%D0%BC%D0%B0%D1%80%D0%B8%D1%8F-%D1%81%D1%82%D0%B5%D0%BF%D0%B0%D0%BD%D0%BE%D0%B2%D0%B0/1919034395030476/" rel="noopener noreferrer" target="_blank" >readings</a >, <a href="https://discours.io/events/kino/premera-filma-virunga" rel="noopener noreferrer" target="_blank" >film screenings</a >, <a href="https://vk.com/wall-151325891_83" rel="noopener noreferrer" target="_blank" >open discussions</a >, and <a href="https://vk.com/event123738166" rel="noopener noreferrer" target="_blank" >journalism workshops</a >, <a href="https://discours.io/events/diskussiya/diskussiya-kak-sdelat-sovremennyy-samizdat" rel="noopener noreferrer" target="_blank" >debates</a > and <a href="https://discours.io/events/lectures/lektsiya-mediaeksperta-vinsenta-daffi-feykovye-novosti-ili-alternativnye-fakty-kak-menyayutsya-otnosheniya-media-s-chitatelyami-v-epohu-postpravdy" rel="noopener noreferrer" target="_blank" >lectures</a >; released <a href="https://vk.com/discoursio?w=wall-89236419_13256" rel="noopener noreferrer" target="_blank" >merch</a > with the slogans “Don\'t stay silent” and “A word can change the world”; drew <a href="https://t.me/addstickers/Discours" rel="noopener noreferrer" target="_blank" >“Dear Editors” stickers</a > used by <a href="https://i.imgur.com/N1tbn9e.png" rel="noopener noreferrer" target="_blank" >thousands</a > of community members; launched the podcast <a href="https://discours.io/podcasts" rel="noopener noreferrer" target="_blank" >“World in Stories”</a >; built a Telegram bot for authors and readers; run a digest of the season\'s best pieces; give readers books and tickets to film screenings; and have plenty more planned. Members of the open editorial team have won <a href="https://hack-wsf.equalit.ie/" rel="noopener noreferrer" target="_blank" >socially oriented</a > and <a href="https://te-st.ru/reports/results-projector2022/" rel="noopener noreferrer" target="_blank" >data-research</a > hackathons; our illustrators have been hired by international publications; the non-fiction and literary fiction first published at Discours has been released as books; plays published by Discours have been staged in theaters; and the journal\'s authors have repeatedly been nominated for <a href="https://redkollegia.org" rel="noopener noreferrer" target="_blank">journalism</a> and <a href="https://discours.io/expo/literature/prose/konstantin-kupriyanov-rasskaz-netolerantnyy-patsient" rel="noopener noreferrer" target="_blank" >literary</a > awards. </p> <p> Together with you, we have helped hundreds of people tell remarkable stories on the pages of Discours and be heard by millions of thinking people around the world. This is only the beginning — we have enormous plans that we are building together. We know that, side by side, we can keep working, keep developing the platform for free journalism, and do a great deal to change the world for the better. </p> </div> </details> <details> <summary>{{ \'Почему поддерживать Дискурс безопасно\' | t }}</summary>
1 <div ng-if="!$ctrl.isEnglish"> <p> У самиздата нет никаких опасных статусов. Мы не иноагенты и не экстремисты. Дискурс можно безопасно поддерживать из России и любых других стран. Для приема пожертвований мы&nbsp;пользуемся безопасным и&nbsp;надежными платежными сервисами Stripe и&nbsp;CloudPayments. Сайт работает через защищенный протокол https, данные вашей карты передаются только в&nbsp;зашифрованном виде и&nbsp;не&nbsp;хранятся на&nbsp;наших серверах. Все сведения остаются строго конфиденциальными. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> Discours carries no dangerous legal labels. We are not “foreign agents” and not extremists. Discours can be safely supported from Russia and any other country. To accept donations we use the secure and reliable payment services Stripe and CloudPayments. The site runs over the secure https protocol; your card details are transmitted only in encrypted form and are not stored on our servers. All information remains strictly confidential. </p> </div> </details> <details> <summary>{{ \'Почему Дискурс не вводит платный доступ\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> Платный доступ к&nbsp;материалам противоречит самой сути просветительского издания, поэтому публикации самиздата всегда останутся бесплатными для всех. Наши материалы&nbsp;— собрание уникальных знаний о&nbsp;современной науке, философии, культуре и&nbsp;искусстве на&nbsp;русском языке. Мы&nbsp;убеждены, что знания должны оставаться свободными и&nbsp;открытыми, и&nbsp;хотим, чтобы все труды авторов сообщества были доступны каждому. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> Charging for access to publications runs against the very nature of an educational journal, so Discours\'s publications will always remain free for everyone. Our work is a collection of unique knowledge about contemporary science, philosophy, culture, and art in the Russian language. We are convinced that knowledge should stay free and open, and we want every work by our community\'s authors to be available to all. </p> </div> </details> <details> <summary>{{ \'Почему самиздат отказывается от рекламы\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> Мы&nbsp;не&nbsp;продаем внимание читателей и&nbsp;не&nbsp;хотим отвлекать вас визуальным шумом. Мы&nbsp;убеждены, что интерес и&nbsp;уважение читателей заслуживаются прежде всего качественными материалами, которые мы&nbsp;создаем, не&nbsp;оглядываясь на&nbsp;интересы рекламодателей, государства и&nbsp;бизнеса. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> We do not sell our readers\' attention and have no wish to distract you with visual noise. We believe that readers\' interest and respect are earned, above all, through the quality of the work we create — without an eye to the interests of advertisers, the state, or business. </p> </div> </details> <details> <summary>{{ \'Почему лучше жертвовать регулярно\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> Подписка на&nbsp;ежемесячные переводы на&nbsp;развитие Дискурса даже на&nbsp;самую небольшую сумму помогает нам больше всего. Благодаря постоянным платежам мы&nbsp;сможем прогнозировать бюджет, планировать работу и&nbsp;развитие журнала, запускать спецпроекты и&nbsp;чаще публиковать новые интервью, расследования, репортажи, аналитические обзоры. Даже небольшая сумма позволит нам сделать многое, если мы&nbsp;будем знать, что сможем рассчитывать на&nbsp;нее и&nbsp;в&nbsp;следующем месяце. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> A monthly subscription supporting Discours\'s growth — even for the smallest amount — helps us most of all. Thanks to regular payments we can forecast our budget, plan the journal\'s work and development, launch special projects, and publish new interviews, investigations, reports, and analytical reviews more often. Even a small amount lets us do a great deal, as long as we know 
1we can count on it next month too. </p> </div> </details> <details> <summary>{{ \'Как настроить или отключить подписку\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> Вы&nbsp;всегда можете поменять способ оплаты, размер ежемесячного пожертвования или отменить подписку. По&nbsp;любым вопросам просто напишите на&nbsp;<a href="mailto:[email protected]" rel="noopener noreferrer" target="_blank" >[email protected]</a >&nbsp;— мы&nbsp;поможем настроить всё так, как вам удобно. По&nbsp;вашей просьбе мы&nbsp;также в&nbsp;любой момент отменим подписку и&nbsp;пришлем вам подтверждение. </p> </div> <div ng-if="$ctrl.isEnglish"> <p> You can always change your payment method, the size of your monthly donation, or cancel your subscription. For any questions, just write to&nbsp;<a href="mailto:[email protected]" rel="noopener noreferrer" target="_blank" >[email protected]</a >&nbsp;— we will help you set everything up the way that suits you. At your request we will also cancel the subscription at any time and send you a confirmation. </p> </div> </details> <details> <summary>{{ \'Как еще можно помочь самиздату\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> Если у вас нет возможности поддержать журнал финансово, но вы&nbsp;хотите помочь, есть <a href="https://discours.io/manifest#participation" rel="noopener noreferrer" target="_blank" >много других</a > способов участвовать в&nbsp;Дискурсе. Например, можно рассказывать о&nbsp;проекте друзьям, делиться статьями самиздата в&nbsp;соцсетях, обсуждать их&nbsp;в&nbsp;беседах со&nbsp;знакомыми, предлагать редакции темы, публиковать свои статьи или творчество, и, конечно, советовать самиздат тем, кто хочет писать и&nbsp;читать интересные тексты. Если вы&nbsp;хотите помочь или предложить сотрудничество&nbsp;— просто <a href="https://discours.io/#idea" rel="noopener noreferrer" target="_blank">напишите нам</a >. </p> <p> Если вы хотите помогать с редактурой, корректурой, иллюстрациями, переводами, версткой, подкастами, мероприятиями, фандрайзингом или как-то еще — <a href="https://forms.gle/GJ3Muqf1UjeCzyDXA" rel="noopener noreferrer" target="_blank" >присоединяйтесь к открытой редакции</a >. Мы всегда рады единомышленникам и обязательно поможем найти полезное для самиздата дело по душе. </p> <p> Если вы разработчик и хотите помогать с развитием сайта Дискурса, присоединяйтесь к IT-команде самиздата. Открытый код платформы для независимой журналистики, а также всех наших спецпроектов и медиаинструментов находится в <a href="https://github.com/Discours" rel="noopener noreferrer" target="_blank" >свободном доступе на GitHub</a >. </p> <p> Если вы представляете некоммерческую организацию и хотите сделать с нами совместный проект, получить информационную поддержку или предложить другую форму сотрудничества, пишите на <a href="mailto:[email protected]" rel="noopener noreferrer" target="_blank" >[email protected]</a >. </p> </div> <div ng-if="$ctrl.isEnglish"> <p>
1 If you are not able to support the journal financially but would still like to help, there are <a href="https://discours.io/manifest#participation" rel="noopener noreferrer" target="_blank" >many other</a > ways to take part in Discours. For example, you can tell friends about the project, share Discours\'s articles on social media, discuss them with people you know, suggest topics to the editorial team, publish your own articles or creative work, and, of course, recommend Discours to anyone who wants to write and read interesting texts. If you would like to help or propose a collaboration, simply <a href="https://discours.io/#idea" rel="noopener noreferrer" target="_blank">write to us</a>. </p> <p> If you would like to help with editing, proofreading, illustration, translation, layout, podcasts, events, fundraising, or anything else — <a href="https://forms.gle/GJ3Muqf1UjeCzyDXA" rel="noopener noreferrer" target="_blank" >join the open editorial team</a >. We always welcome like-minded people and will gladly help you find something useful and rewarding to do for Discours. </p> <p> If you are a developer and would like to help develop the Discours website, join the project\'s IT team. The open-source code of our platform for independent journalism, along with all of our special projects and media tools, is <a href="https://github.com/Discours" rel="noopener noreferrer" target="_blank" >freely available on GitHub</a >. </p> <p> If you represent a non-profit organization and would like to run a joint project with us, receive media support, or propose another form of cooperation, write to <a href="mailto:[email protected]" rel="noopener noreferrer" target="_blank" >[email protected]</a >. </p> </div> </details> <details> <summary>{{ \'Остались вопросы? Просто напишите нам\' | t }}</summary> <div ng-if="!$ctrl.isEnglish"> <p> Если у&nbsp;вас остались вопросы, вы&nbsp;хотели&nbsp;бы сотрудничать с&nbsp;самиздатом, предложить тему для публикации, стать волонтером, помогать редакции с&nbsp;ИТ-разработкой, дизайном или как-то еще поддержать проект&nbsp;— можно написать нам на&nbsp;почту <a href="mailto:[email protected]">[email protected]</a>. Мы&nbsp;обязательно ответим! </p> </div> <div ng-if="$ctrl.isEnglish"> <p> If you still have questions, would like to collaborate with Discours, suggest a topic for publication, become a volunteer, help the editorial team with IT development, design, or support the project in any other way — you can write to us at <a href="mailto:[email protected]">[email protected]</a>. We will be sure to reply! </p> </div> </details>'),e.put("/pages/about/ai-ethics.html",'<update-meta class="black-link" title="{{ \'Кодекс использования ИИ в открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" name="description" content="{{ \'Как открытая редакция Дискурса использует нейросети: исследование, перевод, авторство, источники, безопасность — и где всегда остаётся человек.\' | t }}" ></update-meta> <update-meta class="black-link" name="keywords" content="{{ \'Discours.io, ИИ, нейросети, кодекс, редакционные принципы, этика ИИ\' | t }}" ></update-meta> <update-meta class="black-link" property="og:title" content="{{ \'Кодекс использования ИИ в открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" property="og:description" content="{{ \'Как открытая редакция Дискурса использует нейросети: исследование, перевод, авторство, источники, безопасность — и где всегда остаётся человек.\' | t }}" ></update-meta> <update-meta property="og:image" content="/images/ai-ethics-v2.jpg"></update-meta>
1 <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="630"></update-meta> <div class="ds-container open-post about-post tw:mt-5"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12"> <h1 class="h2 black-bg-heading black-bg-heading-margin"> <span class="wrapped">{{ \'Кодекс использования ИИ\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top"> <p>{{ \'Нейросети стали частью редакционной работы: с их помощью расшифровывают интервью, собирают источники, проверяют орфографию, переводят тексты и работают с данными. Мы открыты к прогрессивным изменениям, которые упрощают работу человека, делая её более продуктивной. Но нам не менее важно, что за каждой опубликованной работой всё ещё стоит человек.\' | t }}</p> <p>{{ \'Это — общие правила сообщества самиздата об использовании ИИ и договорённость о том, как пользоваться нейросетями. Мы будем пересматривать их по мере того, как меняются инструменты, редакционная практика и понимание рисков.\' | t }}</p> <ol style="padding-inline-start: 15px"> <li> <p><span class="lead">{{ \'ИИ можно использовать как инструмент, облегчающий работу.\' | t }}</span> <span ng-bind-html="(\'Нейросети допустимо применять для исследования темы, составления краткого саммари текстов, аудио- и видео, расшифровки записей, проверки орфографии, чернового перевода<a class=\\\'footnote-tooltip\\\' href=\\\'#\\\' data-original-title=\\\'Относитесь с большой осторожностью к переводам ИИ с языка, которым вы не владеете и не сможете провести полноценный фактчек.\\\'>1</a>, помощи в составлении плана статьи и других задач — всего, что может облегчить жизнь автора при подготовке материала. Однако цифры, факты и утверждения необходимо перепроверять и анализировать.\' | t) | trustedHtml" footnote-text="footnote-text"></span></p> </li> <li> <p><span class="lead">{{ \'ИИ не пишет материалы за нас.\' | t }}</span> {{ \'Нейросеть может помочь со структурой, заголовками и технической вычиткой, но смысловая рамка, отбор фактов, финальные формулировки и ответственность должны оставаться за автором.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Ответ нейросети — это не источник.\' | t }}</span> {{ \'ИИ-модели постоянно совершенствуются, но всё же не стоит доверять им как истине в последней инстанции. Значимые сведения должны быть подтверждены интервью, цитатами из документов, надёжными медиа, профильными специалистами и другими надёжными источниками информации.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'ИИ в литературе и искусстве может быть только частью метода или предметом исследования.\' | t }}</span> <span ng-bind-html="(\'Нейрокомпиляции не следует выдавать за прозу, поэзию, фотографию или арт, созданные традиционным способом. Если работа сгенерирована — стоит дать читателю понять, где проходит граница между трудом человека и моделью<a class=\\\'footnote-tooltip\\\' href=\\\'#\\\' data-original-title=\\\'Отдельно отметим, что мы работаем с широкой сетью русскоязычных художников и художниц и в подавляющем большинстве случаев стараемся использовать иллюстрации, выполненные живыми людьми. Исключения составляют технические случаи (картинки-подложки для соцсетей, перекрытые текстом; материалы, в которых ИИ-иллюстрация подходит концептуально; экстренные ситуации, когда материал требует срочного выпуска и проиллюстрировать его иначе не получается).\\\'>2</a>.\' | t) | trustedHtml" footnote-text="footnote-text"></span></p> <p><em>{{ \'Пример: В материале используется иллюстрация, созданная с помощью нейросети.\' | t }}</em></p> <p>{{ \'Изображения, аудио и видео, которые сгенерированы или существенно видоизменены при помощи ИИ, также должны быть обозначены. Не следует выдавать результаты работы нейросети за фотографию, документальную съёмку или запись реального события, человека или места.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Использование нейросети не отменяет авторских прав.\' | t }}</span> {{ \'Не следует загружать в сторонние сервисы чужие неопубликованные тексты, изображения, базы данных и другие материалы, если вы не обладаете правами на них. Если в публикации использованы чужие материалы (фото, видео и т. д.), читатель должен понимать их происхождение, поэтому всегда следует указ
1ывать авторство.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Не стоит забывать о правилах безопасности при использовании нейросетей.\' | t }}</span> {{ \'Пользовательские аккаунты, боты и сторонние сервисы не являются защищённым пространством. Не вводите в ИИ-сервисы пароли, персональные данные, конфиденциальную информацию и чувствительные юридические и медицинские материалы, полные имена, контакты героев, их документы. С осторожностью следует расшифровывать и «сырые» интервью: если вы обсуждали чувствительные данные — лучше расшифровать аудио без использования нейросетей.\' | t }}</p> <p><em><span ng-bind-html="(\'Подробнее о безопасности при использовании ИИ — <a href=\\\'/articles/social/ai-security\\\' target=\\\'_self\\\'>в материале Дискурса</a>.\' | t) | trustedHtml"></span></em></p> </li> <li> <p><span class="lead">{{ \'Автор предупреждает редакцию о значимом использовании ИИ.\' | t }}</span> {{ \'Автор информирует редакционное сообщество, если создавал или существенно перерабатывал при помощи ИИ фрагменты текста, вошедшие в финальную версию; использовал ИИ для заметного чернового перевода; генерировал или редактировал изображения, аудио или видео; обрабатывал большие массивы данных, документов или источников; использовал ИИ для чувствительных данных; делал художественный или исследовательский эксперимент с участием модели.\' | t }}</p> <p>{{ \'Это необходимо для того, чтобы вместе подобрать верный уровень открытости перед читателем. ИИ-проверка орфографии, техническая корректура и другие действия, не меняющие сути текста, отдельного предупреждения и дисклеймера не требуют.\' | t }}</p> <p><span ng-bind-html="(\'Спорные случаи требуют диалога. Участники редакции могут попросить автора поделиться источниками, объяснить метод работы или уточнить степень участия ИИ в создании публикации<a class=\\\'footnote-tooltip\\\' href=\\\'#\\\' data-original-title=\\\'Если после публикации выяснится, что использование ИИ было скрыто, материал вводит читателей в заблуждение или создаёт угрозу безопасности, самиздат может добавить маркировку, внести исправления и пересмотреть сотрудничество с автором.\\\'>3</a>.\' | t) | trustedHtml" footnote-text="footnote-text"></span></p> </li> </ol> <div class="tw:mt-section"> <p><em><span ng-bind-html="(\'Этот кодекс открыт для обсуждения и будет пересматриваться по мере появления новых инструментов, практик и рисков. Сообщить об ошибке, нераскрытом использовании ИИ или возможном нарушении прав всегда можно по адресу <a href=\\\'mailto:[email protected]\\\' rel=\\\'noopener noreferrer\\\' target=\\\'_blank\\\'>[email protected]</a>.\' | t) | trustedHtml"></span></em></p> </div> </div> </div> </div>'),e.put("/pages/about/almanac.html",'<update-meta title="{{ \'Альманах\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Голосуем за лучшие статьи на Дискурсе.\' | t }}" ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, альманах, голосование\' | t }}"></update-meta> <update-meta property="og:title" content="{{ \'Альманах\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Голосуем за лучшие статьи на Дискурсе.\' | t }}" ></update-meta> <div class="almanac-wrapper"> <div class="ds-container"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:md:w-9/12"> <h1 class="h2 black-bg-heading tw:mt-content-top almanac-hero-title"> <span>{{ \'Ура! Дискурс будет на бумаге. Уже скоро\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:sm:mx-auto"> <p class="italic tw:mt-10">{{ \'Художественно-аналитический журнал Discours.io существует уже не один год. За это время у нас вышло много прекрасных текстов, которые будут интересны не только завтра, но и через десятки лет. Поэтому мы решили издать их на бумаге. Что именно войдет в альманах «Дискурс», решать вам. Голосуйте!\' | t }}</p> </div> </div> </div> <div class="ds-container"> <div class="tw:gutter-half tw:w-full tw:md:w-9/12"> <h2 class="h2 black-bg-heading tw:mt-content-top almanac-section-title"> <span>{{ \'Что такое альманах «Дискурс»\' | t }}</span> </h2> </div> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 tw:md:w-3/12 almanac-item"> <img class="tw:block tw:max-w-full tw:h-auto" src="/images/almanac/icon_lend1.svg" alt="{{ \'Голосование сообщества\' | t }}" width="85" height="85" loading="lazy" decoding="async" /> <p>{{ \'Содержание альманаха прямым голосованием определит глобаÐ
1»ÑŒÐ½Ð¾Ðµ сообщество интеллектуалов. Таких изданий еще не было\' | t }}</p> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 tw:md:w-3/12 almanac-item"> <img class="tw:block tw:max-w-full tw:h-auto" src="/images/almanac/icon_lend2.svg" alt="{{ \'33 текста с иллюстрациями\' | t }}" width="85" height="85" loading="lazy" decoding="async" /> <p>{{ \'33 потрясающих текста, проиллюстрированные 33 разными художниками\' | t }}</p> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 tw:md:w-3/12 almanac-item"> <img class="tw:block tw:max-w-full tw:h-auto" src="/images/almanac/icon_lend3.svg" alt="{{ \'Многоголосое издание\' | t }}" width="85" height="85" loading="lazy" decoding="async" /> <p>{{ \'Необыкновенное многоголосое издание с удивительными историями о самых разных вещах\' | t }}</p> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 tw:md:w-3/12 almanac-item"> <img class="tw:block tw:max-w-full tw:h-auto" src="/images/almanac/icon_lend4.svg" alt="{{ \'Книга на полку\' | t }}" width="85" height="85" loading="lazy" decoding="async" /> <p>{{ \'Книга, которую захочется поставить на полку, перечитать и через год, и через десять лет\' | t }}</p> </div> </div> </div> <div class="ds-container" id="voting"> <div class="tw:gutter-half tw:w-full tw:md:w-9/12 tw:mt-content-top"> <h2 class="h2 black-bg-heading almanac-section-title"> <span>{{ \'Голосуйте за лучшие статьи Дискурса\' | t }}</span> </h2> <p class="tw:mt-5">{{ \'Ваш выбор определит содержание альманаха\' | t }}</p> </div> <div class="tw:row-flex tw:mt-content-top"> <div class="tw:gutter-half tw:w-full tw:md:w-8/12"> <div class="almanac-list-state almanac-list-state--loading" ng-if="$ctrl.loadingInitial" role="status" aria-live="polite" > <i class="fas fa-spin fa-sync-alt" aria-hidden="true"></i> <span>{{ \'Загружаем материалы…\' | t }}</span> </div> <div class="tw:row-flex almanac-article" ng-repeat="article in $ctrl.articles track by article._id" > <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 tw:lg:w-7/12"> <p> <span class="almanac-article-index">{{ $index + 1 }}.</span ><a class="almanac-article-title" ng-href="{{ article.link }}">{{ article.title }}</a ><a class="almanac-article-author" ng-if="article.author" ng-href="{{ article.authorLink }}" >{{ article.author }}</a > </p> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 tw:lg:w-5/12"> <rating-buttons type="ContentItem" item="article" popover-class="rating-popover--open-material" root-class="post-rating" ></rating-buttons> </div> <div ng-if="$index === 32"> <hr class="big-black" /> </div> </div> <div class="almanac-list-state almanac-list-state--error" ng-if="$ctrl.loadError && !$ctrl.loadingMore" role="alert" > <p> {{ \'Не удалось загрузить материалы полностью. Проверьте соединение и попробуйте снова.\' | t }} </p> <button class="paper-button light almanac-retry-button" type="button" ng-click="$ctrl.retryLoad()" > {{ \'Повторить\' | t }} </button> </div> <div class="almanac-list-state" ng-if="!$ctrl.loadingInitial && !$ctrl.loadError && !$ctrl.articles.length" role="status" > <p>{{ \'Пока нет материалов для голосования.\' | t }}</p> </div> <div> <button class="paper-button big corner-bottom-left invert-hover" type="button" ng-click="$ctrl.loadMore()" ng-disabled="$ctrl.loadingInitial || $ctrl.loadingMore" ng-hide="$ctrl.loadError || !$ctrl.hasMore" ng-attr-aria-busy="{{ $ctrl.loadingMore }}" aria-label="{{ \'Показать ещё материалы\' | t }}" > <span ng-hide="$ctrl.loadingMore">{{ \'Показать ещё\' | t }}</span ><i class="fas fa-sync-alt fa-spin" ng-show="$ctrl.loadingMore" aria-hidden="true"></i ><span class="tw:sr-only" ng-if="$ctrl.loadingMore" >{{ \'Загружаем материалы…\' | t }}</span > </button> </div> </div> <div class="tw:gutter-half tw:w-full tw:md:w-4/12">
1 <div class="almanac-faq-item"> <h2 class="almanac-faq-title">F.A.Q.</h2> <dl> <dt>{{ \'Что такое альманах «Дискурс»?\' | t }}</dt> <dd>{{ \'В первый номер альманаха войдут 33 лучших, по мнению авторов Дискурса, текста, опубликованных на сайте.\' | t }}</dd> <dt>{{ \'Что будет в альманахе?\' | t }}</dt> <dd>{{ \'Это репортажи, исследования, исторические очерки, аналитические статьи, рецензии, интервью с удивительными людьми и первоклассная литература. Какие именно материалы войдут в альманах, решит авторское сообщество.\' | t }}</dd> <dt>{{ \'Как попасть в альманах?\' | t }}</dt> <dd ng-bind-html="(\'Надо до февраля 2017 года <a class=\\\'black-link\\\' href=\\\'https://discours.io/#post\\\'>написать</a> очень хорошую статью и дождаться, пока ее оценит по достоинству сообщество из более чем 200 человек со всего мира.\' | t) | trustedHtml" ></dd> </dl> </div> </div> </div> <div class="tw:gutter-half tw:w-full tw:md:w-9/12"> <h2 class="h2 black-bg-heading tw:mt-content-top almanac-section-title"> <span>{{ \'Заказать альманах\' | t }}</span> </h2> </div> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-7/12"> <p class="italic tw:mt-10 tw:text-xl tw:font-bold">{{ \'Оставьте email, чтобы предзаказать бумажный альманах и поддержать его выпуск\' | t }}</p> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-5/12"> <div class="subscribe-form"> <div class="subscription-input-wrapper"> <span ng-show="$ctrl.subscribeFormSent" role="status" aria-live="polite" >{{ \'Спасибо!\' | t }}</span > <div class="loading" ng-show="$ctrl.subscribeFormLoading" role="status" aria-live="polite" > <i class="fas fa-spin fa-sync-alt" aria-hidden="true"></i> <span class="tw:sr-only">{{ \'Отправляем…\' | t }}</span> </div> <form ng-submit="$ctrl.subscribeForm($event)" ng-hide="$ctrl.subscribeFormSent || $ctrl.subscribeFormLoading" > <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:md:w-9/12"> <label class="tw:sr-only" for="almanac-subscribe-email" >{{ \'Оставьте email\' | t }}</label > <input id="almanac-subscribe-email" class="subscription-input" type="email" inputmode="email" autocomplete="email" required="required" placeholder="{{ \'Оставьте email\' | t }}" ng-model="$ctrl.subscribeObj.email" ng-disabled="$ctrl.subscribeFormLoading" /> </div> <div class="tw:gutter-half tw:w-full tw:md:w-3/12"> <button class="paper-button light" type="submit" aria-label="{{ \'Отправить email\' | t }}" > OK </button> </div> </div> </form> </div> </div> </div> </div> </div> </div>'),e.put("/pages/about/changelog.html",'<update-meta title="Changelog Дискурса — обновления платформы"></update-meta> <update-meta name="description" content="Открытая история обновлений свободной платформы: новые функции, исправления и улучшения"></update-meta> <update-meta name="keywords" content="Discours.io, changelog, обновления, история версий, релизы"></update-meta> <update-meta property="og:title" content="Changelog — обновления платформы Дискурса"></update-meta> <update-meta property="og:description" content="Открытая история обновлений свободной платформы: новые функции, исправления и улучшения"></update-meta> <update-meta property="og:type" content="website"></update-meta> <update-meta property="og:image" content="https://discours.io/images/participation.png"></update-meta>
1 <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="630"></update-meta> <update-meta name="twitter:card" content="summary_large_image"></update-meta> <update-meta name="twitter:image" content="https://discours.io/images/participation.png"></update-meta> <script src="/scripts/vendor-changelog-filter.js" defer><\/script> <div class="container open-post tw:mt-5"> <div class="tw:row-flex changelog-layout"> <div class="tw:gutter-half tw:w-full tw:md:w-2/12"> <h1 class="h2 black-bg-heading black-bg-heading-margin"> <span class="wrapped">Changelog Дискурса: обновления платформы</span> </h1> <nav class="changelog-filter" aria-label="Фильтр по типам изменений"> <span class="changelog-filter__btn changelog-filter__btn--active" role="button" tabindex="0" data-filter="all"><span class="changelog-filter__label">Все</span><span class="changelog-filter__count" data-count="all"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="NEW" title="новая фича">NEW <span class="changelog-filter__count" data-count="NEW"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="FIX" title="баг-фикс">FIX <span class="changelog-filter__count" data-count="FIX"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="UX" title="взаимодействие">UX <span class="changelog-filter__count" data-count="UX"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="PERF" title="скорость">PERF <span class="changelog-filter__count" data-count="PERF"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="DESIGN" title="визуал">DESIGN <span class="changelog-filter__count" data-count="DESIGN"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="REFACTOR" title="рефакторинг">REFACTOR <span class="changelog-filter__count" data-count="REFACTOR"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="A11Y" title="доступность">A11Y <span class="changelog-filter__count" data-count="A11Y"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="SEC" title="безопасность">SEC <span class="changelog-filter__count" data-count="SEC"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="SEO" title="SEO">SEO <span class="changelog-filter__count" data-count="SEO"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="DOCS" title="документация">DOCS <span class="changelog-filter__count" data-count="DOCS"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="QA" title="тестирование">QA <span class="changelog-filter__count" data-count="QA"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="DEVOPS" title="инфраструктура">DEVOPS <span class="changelog-filter__count" data-count="DEVOPS"></span></span> <span class="changelog-filter__btn" role="button" tabindex="0" data-filter="DX" title="DX">DX <span class="changelog-filter__count" data-count="DX"></span></span> </nav> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top changelog-content" ng-non-bindable> <details class="changelog-section" open> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.177</span><span class="changelog-meta__date">28-09-2026</span></span><h2 id="v9177-28-09-2026--политика-конфиденциальности-своя-страница-и-понятный-раздел-о-входе-через-другие-сервисы">Политика конфиденциальности: своя страница и понятный раздел о входе через другие сервисы</h2></summary> <div class="changelog-section__body"> <p>Политика конфиденциальности была разделом в конце правил сайта. Теперь у неё свой адрес, discours.io/privacy-policy, а в тексте прямо сказано, что сайт получает при входе через Google, VK, Facebook или GitHub и чего не получает.
1</p> <ul> <li data-num="1746"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Страница «Политика конфиденциальности»</strong> — отдельно от правил сайта; ведёт на неё знак © в подвале сайта, а в правилах на месте прежнего раздела осталась отсылка.</li> <li data-num="1747"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Вход через другие сервисы</strong> — политика перечисляет, что приходит от сервиса входа (имя, адрес почты, фото профиля и идентификатор аккаунта), зачем это нужно и как удалить эти данные. К письмам, контактам и документам аккаунта у сайта доступа нет.</li> <li data-num="1748"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Текст политики читается без JavaScript</strong> — поисковики и проверка приложения в Google видят его сразу, а не пустую оболочку страницы.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.176</span><span class="changelog-meta__date">28-09-2026</span></span><h2 id="v9176-28-09-2026--подпись-автора-без-имени-адрес-его-страницы">Подпись автора без имени: адрес его страницы</h2></summary> <div class="changelog-section__body"> <p>Если автор не заполнил имя в профиле, его подпись пропадала: под материалом имени не было вовсе, в списке авторов и подписчиков темы стояла пустая строка, в уведомлениях не было видно, кто действовал, а страница самого автора не открывалась. Теперь вместо имени показывается адрес страницы автора — тот же, что в ссылке на его профиль. Почта в подпись не попадает, даже если её вписали в поле имени.</p> <ul> <li data-num="1742"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница автора</strong> — открывается и без имени в профиле; в поиске и в превью ссылки подписана адресом страницы.</li> <li data-num="1743"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Подпись под материалом</strong> — вверху и внизу статьи, для читателей и для вошедших. В разметке для поисковиков такой материал больше не приписан редакции.</li> <li data-num="1744"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авторы и подписчики темы</strong> — строка без имени снова подписана, и её находит поиск по списку.</li> <li data-num="1745"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уведомления</strong> — фраза начинается с адреса страницы автора, ссылкой на его профиль. Если показать нечего, уведомление безличное: «Новый комментарий к …».</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.175</span><span class="changelog-meta__date">27-09-2026</span></span><h2 id="v9175-27-09-2026--правки-рецензента-остаются-в-редакторе-после-публикации">Правки рецензента остаются в редакторе после публикации<
1/h2></summary> <div class="changelog-section__body"> <p>Предложения и комментарии рецензента, которые автор не успел разобрать до публикации, пропадали из редактора: материал выходил на сайт, а правки оставались только в истории версий. Теперь они остаются в черновике на своих местах, и после публикации их можно принять или отклонить, как прежде. На сайт, как и раньше, не попадают предложенные вставки и комментарии, а текст, который предлагали удалить, остаётся на месте.</p> <ul> <li data-num="1737"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Неразобранные правки остаются в редакторе после публикации</strong> — предложения и открытые комментарии рецензента снова видны на полях при следующем открытии материала.</li> <li data-num="1738"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Непринятый новый абзац не разрезает опубликованный текст</strong>
1 — пока автор его не принял, абзац на сайте остаётся целым. Раньше при публикации пометка снималась, а разрез оставался.</li> <li data-num="1739"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Публикация дожидается сохранения черновика</strong> — в том числе того, что уже идёт. Если сохранить черновик не удалось, материал не публикуется, редактор с правками остаётся открытым, а на экране появляется предупреждение: текст остаётся в редакторе, опубликовать можно ещё раз чуть позже.</li> <li data-num="1740"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Повторное нажатие «Опубликовать» не выпускает материал дважды</strong> — пока сохраняется черновик, второе нажатие ничего не отправляет.</li> <li data-num="1741"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Публикация без изменений больше не создаёт новую версию</strong> — у материалов со сносками каждое нажатие «Опубликовать» записывало версию заново и вытесняло из истории более ранние.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.174</span><span class="changelog-meta__date">26-09-2026</span></span><h2 id="v9174-26-09-2026--правки-рецензента-новый-абзац-и-история-версий">Правки рецензента: новый абзац и история версий</h2></summary> <div class="changelog-section__body"> <p>Рецензент в режиме «Комментирование» правит текст предложениями: каждое видно на полях, и автор сам решает, принять его или отклонить. Одно действие шло мимо этого порядка — новый абзац по клавише Enter сразу разрезал текст автора, на полях не появлялся и при публикации уходил на сайт. А правки, которые до публикации не успели разобрать, из редактора пропадали: они сохранялись в истории версий, но ни увидеть, ни вернуть их оттуда было нельзя.</p> <ul> <li data-num="1733"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Новый абзац рецензента стал предложением</strong> — он появляется на полях рядом с остальными правками, и автор принимает или отклоняет его так же. Отклонённый абзац склеивается с соседним обратно. На мобильных клавиатурах разрыв пока может пройти без пометки — это исправим отдельно.</li> <li data-num="1734"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Свой новый абзац рецензент отменяет обычными клавишами</strong> — Backspace и Delete убирают пустую строку и склеивают разрезанный абзац, как в любом тексте. Чужой новый абзац клавиши не трогают: его отклоняют на полях.</li> <li data-num="1735"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>История версий показывает правки и комментарии</strong> — у версии, где они есть, в просмотре появился переключатель «Без правок» и «С правками». По умолчанию видны правки: предложения и комментарии подсвечены так же, как в редакторе.</li> <li data-num="1736"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Восстановить» возвращает версию в том виде, в каком она открыта</strong> — с правками и открытыми комментариями или без них. Так в работу можно вернуть предложения, которые не успели разобрать до публикации: они хранятся в версии «Сохранение при публикации».</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.173</span><span class="changelog-meta__date">14-09-2026</span></span>
1<h2 id="v9173-14-09-2026--страница-подтверждения-почты-перестала-говорить-одно-и-то-же-на-все-случаи">Страница подтверждения почты перестала говорить одно и то же на все случаи</h2></summary> <div class="changelog-section__body"> <p>Ссылку из письма нередко открывает не человек, а антивирус, корпоративный прокси или превью почтового клиента. Ссылка срабатывает, почта оказывается подтверждена — а человек, открыв письмо следом, читал «ссылка устарела» и получал форму запроса новой, хотя подтверждать уже нечего. Теперь страница различает случаи и предлагает то, что поможет.</p> <ul> <li data-num="1730"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Эта ссылка уже использована» вместо «устарела»</strong> — и рядом кнопка «Войти», потому что чаще всего почта в этот момент уже подтверждена. Запросить новую ссылку по-прежнему можно, но это второй шаг, а не единственный.</li> <li data-num="1731"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Истёкший срок назван своими словами</strong> — там форма новой ссылки как раз уместна.</li> <li data-num="1732"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Оборванная ссылка опознаётся отдельно</strong> — почтовые клиенты переносят длинные адреса на новую строку, и до сайта доезжает огрызок. Страница просит открыть ссылку целиком или прислать новую.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.172</span><span class="changelog-meta__date">11-09-2026</span></span><h2 id="v9172-11-09-2026--иконки-пропавшие-из-набора-вернулись-на-кнопки">Иконки, пропавшие из набора, вернулись на кнопки</h2></summary> <div class="changelog-section__body"> <p>Набор иконок сайт загружает не целиком, а собранным подмножеством — так страница открывается быстрее. Иконка, которую забыли внести в набор, не показывала ни ошибки, ни заглушки: на её месте оставалась пустота, и кнопка выглядела безымянной. Двадцать три такие иконки вернулись на места, а сборка сайта теперь останавливается, если в разметке появилась иконка, которой в наборе нет.</p> <ul> <li data-num="1723"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопки в групповом диалоге снова подписаны иконками</strong> — «Покинуть группу» и отметка владельца группы были пустыми местами.</li> <li data-num="1724"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Переключатель звука уведомлений видно</strong> — кнопка «Включить звук» и «Выключить звук» в центре уведомлений не показывала своего состояния, и подсказка про свайп карточки открывалась без значка.</li> <li data-num="1725"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Экран входа на смысловых картах показывает свой значок</strong> — раньше над приглашением войти было пустое место.</li> <li data-num="1726"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Иконки сервисов при вставке ссылки в редакторе на месте</strong> — TikTok, Twitch, Reddit, Bandcamp, Apple Podcasts, Flickr, Pinterest, подкасты, музыка, карты, презентации и книги опознаются по значку, а не по пустому квадрату.</li> <li data-num="1727"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопки карточки ссылки подписаны</strong> — «Открыть ссылку» и переключение вида карточки в редакторе.</li> <li data-num="1728"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Применить ко всем» в подписи к изображению получила свой значок</strong>, а выбор версии для сравнения — иконку текущего текста.</li> <li data-num="1729"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Сборка падает, если иконки нет в наборе</strong> — проверка сверяет разметку, список стилей и собранный шрифт между собой, поэтому пустое место вместо иконки больше не доедет до сайта незамеченным.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.171</span><span class="changelog-meta__date">06-09-2026</span></span>
1<h2 id="v9171-06-09-2026--ответы-сайта-несут-политику-использования-а-llmstxt-ведёт-агентов-к-тексту">Ответы сайта несут политику использования, а llms.txt ведёт агентов к тексту</h2></summary> <div class="changelog-section__body"> <p>Редакция ещё в августе записала в robots.txt своё решение: искать и цитировать материалы со ссылкой можно, забирать их в обучающие корпуса нельзя. Но текстовая версия страниц, которую сайт отдаёт ИИ-агентам по запросу, приходила с противоположной пометкой — её ставил по умолчанию промежуточный слой, не находя нашей. Теперь политика стоит на каждом ответе сайта, и текстовая версия её наследует.</p> <ul> <li data-num="1718"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Политика использования материалов стоит в заголовке каждого ответа</strong> — в той же формулировке, что и в robots.txt: поиск и цитирование разрешены, обучение моделей нет. Текстовая версия страниц для агентов больше не объявляет обратного.</li> <li data-num="1719"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Путеводитель для ИИ-агентов (llms.txt) ведёт прямо к разделам</strong> — вместо адресов с перенаправлением, и рассказывает, как получить материал в компактном текстовом виде и где прочитать политику использования.</li> <li data-num="1720"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Страницы «О проекте» читаются без JavaScript</strong> — манифест, гид, догма, принципы и страница поддержки отдают полный текст поисковым роботам и ИИ-агентам, а не пустую оболочку: раньше без исполнения скриптов там было полтора десятка слов.</li> <li data-num="1721"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Текстовая версия страниц для агентов начинается с материала</strong> — служебный блок с разделами и подпиской больше не открывает каждую страницу.</li> <li data-num="1722"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Запрос несуществующей полной версии путеводителя получает честный ответ «нет»</strong> — раньше вместо него уходила пустая оболочка страницы, и агент считал, что файл есть.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.170</span><span class="changelog-meta__date">05-09-2026</span></span><h2 id="v9170-05-09-2026--веб-версия-письма-открывается-по-любой-из-своих-ссылок">Веб-версия письма открывается по любой из своих ссылок</h2></summary> <div class="changelog-section__body"> <p>Ссылка «открыть в браузере» из рассылки ведёт на отдельный адрес, и до сих пор тот же адрес, набранный на самом сайте, показывал экран «страница не существует». Теперь он открывает письмо, а не ошибку — при этом ссылки в уже отправленных письмах работают как работали.</p> <ul> <li data-num="1715"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Адрес веб-версии письма открывается и на сайте</strong> — прежде он приводил на экран несуществующей страницы, хотя письмо было Ð
1½Ð° месте. Ссылки из отправленных писем при этом не изменились и продолжают открываться по-старому.</li> <li data-num="1716"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ссылка на письмо разворачивается в мессенджерах темой выпуска</strong> — при пересылке в телеграм или соцсеть видно тему письма и подводку, а не общее описание журнала. Раньше карточку показывала только одна из двух ссылок на одно и то же письмо.</li> <li data-num="1717"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Переход на веб-версию письма идёт одним шагом</strong> — адрес сразу приводится к каноническому виду, без промежуточной остановки.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.169</span><span class="changelog-meta__date">04-09-2026</span></span><h2 id="v9169-04-09-2026--материал-из-уведомления-открывается-с-первого-раза">Материал из уведомления открывается с первого раза</h2></summary> <div class="changelog-section__body"> <p>Ссылка на материал в центре уведомлений встречала словами «Материал не загрузился» — и тут же открывала редактор, стоило нажать «Повторить» или обновить страницу. Материал всё это время был на месте: короткая ссылка из уведомления по дороге превращается в полный адрес с рубрикой, и эта смена адреса обрывала запрос, который сама же и запустила. Второй заход шёл уже по готовому адресу и поэтому работал.</p> <ul> <li data-num="1707"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Материал из уведомления открывается сразу, без «Повторить»</strong> — смена адреса в строке браузера больше не обрывает загрузку страницы, которая никуда не уходит. Это касается и выбора рубрики в открытом редакторе, и первого сохранения нового черновика: их запросы тоже обрывались на ровном месте.</li> <li data-num="1708"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Оборванная загрузка материала повторяется сама</strong> — если запрос всё же прервётся, редактор молча попробует ещё раз, и только неудача второй попытки покажет экран с объяснением.</li> <li data-num="1709"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Материал, открытый по короткой ссылке, показывает свой текст</strong> — раньше редактор на таком адресе считал материал новым, не подключался к общему документу и открывался пустым. Пустым он и сохранялся: автосохранение не отличало «человек стёр текст» от «текст не успел загрузиться».</li> <li data-num="1710"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Автосохранение больше не может обнулить материал</strong> — пустое тело оно отправляет, только если до этого видело в редакторе непустое. Стереть текст по-прежнему можно, а вот незагрузившийся документ теперь не превращается в пустой.</li> <li data-num="1711"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Текст встаёт на место раньше, чем редактор показывает пустое поле</strong> — при первом открытии материала экран загрузки уходил на долю секунды раньше самого текста, и в этот момент поле выглядело пустым, с приглашением начать историю. Теперь загрузка держится до конца.</li> <li data-num="1712"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Материал, стёртый целиком, сохраняется как обычно</strong> — защита от обнуления умеет отличать пустоту, которую оставил человек, от пустоты недогрузившегося документа: как только редактор показал текст, любая последующая правка, включая полную очистку, сохраняется штатно.</li> <li data-num="1713"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Пустой материал открывается сразу</strong> — на черновике, в котором ещё ничего нет, экран загрузки держался лишние пару секунд в ожидании текста, которого не существует.</li> <li data-num="1714"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Прежний текст остаётся в истории версий, даже если что-то пошло Ð
1½Ðµ так</strong> — если материал вдруг обнулится или сожмётся до пары слов, редакция найдёт предыдущую версию в истории под понятным именем и вернёт её в один клик.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.168</span><span class="changelog-meta__date">03-09-2026</span></span><h2 id="v9168-03-09-2026--предложение-войти-больше-не-исчезает-само">Предложение войти больше не исчезает само</h2></summary> <div class="changelog-section__body"> <p>Предыдущее обновление научило редактор говорить «войдите» прямо на адресе материала. Проверка на живом сайте показала, что сказанное держится не всегда: страница успевала показать предложение войти и через пару секунд сама уходила на главную. Человек, пришедший по ссылке из письма, оставался без адреса, к которому шёл.</p> <ul> <li data-num="1705"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница недоступного материала остаётся на своём адресе, даже когда сессия истекает прямо на ней</strong> — раньше её сменяла главная, и ссылку из письма приходилось искать заново. После входа открывается то, ради чего вы пришли.</li> <li data-num="1706"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уведомление о комментарии ни при каких данных не открывается в редакторе вместо страницы материала</strong> — тип уведомления сверялся так, что отдельные служебные значения принимались за приглашение.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.167</span><span class="changelog-meta__date">03-09-2026</span></span><h2 id="v9167-03-09-2026--название-в-уведомлении-открывает-ровно-то-же-что-и-вся-строка">Название в уведомлении открывает ровно то же, что и вся строка</h2></summary> <div class="changelog-section__body"> <p>Прошлое обновление обещало, что клик по названию материала и клик по строке уведомления ведут в одно место. На живой ленте оказалось иначе: строка открывала материал в нужной рубрике, с якорем на ту самую ремарку и с раскрытой панелью правок, а название — тот же материал, но без рубрики, без якоря и без панели. Разница незаметна на глаз и обнаруживается, только когда сравниваешь адреса.</p> <ul> <li data-num="1704"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Название ведёт по тому же адресу, что и строка целиком</strong> — с рубрикой материала, якорем на нужную ремарку и открытой панелью правок, если уведомление именно о них. Раньше этот адрес строился заново и терял всё перечисленное.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.166</span><span class="changelog-meta__date">03-09-2026</span></span><h2 id="v9166-03-09-2026--название-материала-в-уведомлениях-ведёт-куда-надо">Название материала в уведомлениях ведёт куда надо</h2></summary> <div class="changelog-section__body"> <p>В уведомлении о приглашении к соавторству вся строка карточки вела в редактор, а клик именно по наÐ
1·Ð²Ð°Ð½Ð¸ÑŽ материала в тексте фразы — на публичную страницу черновика или, пока категория материала ещё не подгрузилась, вовсе на главную. Две ссылки в одной карточке считались независимо и расходились местами назначения.</p> <ul> <li data-num="1701"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Название материала в уведомлении о приглашении больше не ведёт на главную</strong> — ссылка строилась в обход адреса, который прислал сервер, и при не успевшей подгрузиться рубрике материала превращалась в пустую, а такая безобидно выглядящая ссылка уводит браузер на корень сайта.</li> <li data-num="1702"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Клик по названию и клик по остальной части строки уведомления теперь ведут в одно и то же место</strong> — для приглашений и предложенных правок это редактор, для комментариев и ремарок — страница материала.</li> <li data-num="1703"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Если адреса нет вовсе, название остаётся просто текстом</strong> — вид ссылки больше не обещает перехода, которого не будет, и ни один пропуск в данных не уводит с уведомлений на главную.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.165</span><span class="changelog-meta__date">03-09-2026</span></span><h2 id="v9165-03-09-2026--редактор-перестал-путать-истёкшую-сессию-с-закрытым-доступом">Редактор перестал путать истёкшую сессию с закрытым доступом</h2></summary> <div class="changelog-section__body"> <p>Ссылка из письма-приглашения приводила к замку и словам «Возможно, страницы не существует или к ней нет доступа». Причина чаще была совсем другой: пока письмо ждало своего часа, сессия успевала истечь, и журнал переставал узнавать вошедшего. Сказать об этом прямо он не умел — закрытый черновик и незнакомого гостя сервер намеренно встречает одинаково, чтобы не выдавать чужие тексты.</p> <ul> <li data-num="1695"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Истёкшая сессия больше не выглядит как отказ в доступе</strong> — редактор переспрашивает журнал, узнаёт ли он вас, и предлагает войти заново вместо тупика с замком.</li> <li data-num="1696"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Предложение войти появляется на адресе материала, а не после выброса на главную</strong> — сам вопрос «узнаёт ли меня журнал» считался ещё одной неудачей входа и добивал счётчик до автоматического выхода из аккаунта. Теперь после входа вы оказываетесь там, куда шли по ссылке из письма.</li> <li data-num="1697"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Короткая ссылка на черновик разбирается так же, как полный адрес</strong> — раньше она одна оставалась при прежнем «черновик не найден», хотя причина была та же.</li> <li data-num="1698"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>На этом экране вернулась обычная шапка с меню</strong> — раньше от неё оставались логотип и аватар: навигация пряталась, как при открытом редакторе, и уйти со страницы было Ð
1½ÐµÐºÑƒÐ´Ð°.</li> <li data-num="1699"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Временный сбой связи больше не выдаётся за исчезнувший материал</strong> — при обрыве или перегрузке редактор так и говорит и показывает кнопку «Повторить», а не отправляет искать пропажу.</li> <li data-num="1700"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопка «Запросить доступ» показывается только когда дело действительно в доступе</strong> — при сбое связи она сбивала с толку: человек писал автору, хотя материал его и так ждал.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.164</span><span class="changelog-meta__date">27-08-2026</span></span><h2 id="v9164-27-08-2026--комментарии-и-лента-материалов-больше-не-исчезают">Комментарии и лента материалов больше не исчезают</h2></summary> <div class="changelog-section__body"> <p>Чтобы страница открывалась быстро, читатель сначала видит готовую заготовку, а через мгновение её сменяет живая страница. Иногда эта передача застревала — и человек оставался с полустраницей, не понимая, что происходит.</p> <ul> <li data-num="1691"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Комментарии, ремарки и блок поддержки под статьёй иногда не появлялись вовсе</strong> — если вкладка была свёрнута или перекрыта другим окном, передача застревала навсегда. Открыли статью в фоне, вернулись — и половины страницы нет.</li> <li data-num="1692"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Лента материалов на странице темы не пропадает после сбоя связи</strong> — сорвавшаяся и переигранная загрузка могла оставить пустоту там, где список уже был виден.</li> <li data-num="1693"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Техническая карточка «Медленное соединение» больше не подменяет собой готовую страницу</strong> — при заминке сети она вылезала поверх уже отрисованного материала, и её же видел поисковик вместо статьи.</li> <li data-num="1694"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Повтор после обрыва больше не перезагружает страницу целиком</strong> — журнал переигрывает только неудавшийся шаг, не выбрасывая то, что вы уже читаете, и не теряя место прокрутки.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.163</span><span class="changelog-meta__date">26-08-2026</span></span><h2 id="v9163-26-08-2026--страница-автора-и-видео-показываем-ровно-то-что-есть">Страница автора и видео: показываем ровно то, что есть</h2></summary> <div class="changelog-section__body"> <p>Профили авторов и видеоматериалы получили ощутимую правку — и в том, что видит человек, и в том, что видит поиск.</p> <ul> <li data-num="1687"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>В ленте автора больше не мелькают материалы, ещё не вышедшие в журнале</strong> — гость на секунду видел отправленное в редакцию, а затем оно исчезало. Теперь гостю показывается ровно то, что опубликовано.</li> <li data-num="1688"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Плеер на странице музыки работает до первого нажатия</strong> — дорожка проигрывания видна сразу, а клик по Ð
1½ÐµÐ¹ включает трек с выбранного места. Раньше до старта её просто не было, а блок подпрыгивал при первом нажатии.</li> <li data-num="1689"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Видео Дискурса стало находиться в поиске как видео</strong> — прежде поисковик не видел на странице плеера и относил ролики к обычным текстам. Теперь плеер, превью и описание уходят в поиск вместе со страницей.</li> <li data-num="1690"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>У каждого профиля своё описание в выдаче</strong> — раньше сотням авторов доставалась одна шаблонная строка, и поиск собирал сниппет как придётся.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.162</span><span class="changelog-meta__date">25-08-2026</span></span><h2 id="v9162-25-08-2026--обсуждения-правок-в-редакторе-не-запираются-намертво">Обсуждения правок в редакторе не запираются намертво</h2></summary> <div class="changelog-section__body"> <p>Работа над чужим текстом идёт часами, и за это время многое успевает случиться: истекает сессия, кто-то удаляет обсуждаемый комментарий. Раньше после такого обсуждение переставало отвечать, и вылечить это можно было только перезагрузкой страницы — вместе с несохранённым.</p> <ul> <li data-num="1683"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Истёкшая сессия больше не блокирует обсуждение</strong> — редактор различает «сервер отказал» и «связь пропала неизвестно на чём», и зря не запирает переписку.</li> <li data-num="1684"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Удалённый кем-то комментарий не запирает соседние обсуждения</strong> — прежде исчезнувшая реплика могла заблокировать целую ветку правки, включая кнопки «принять» и «отклонить».</li> <li data-num="1685"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Системные плашки в панели правок читаются в ночной теме</strong> — сообщение, объясняющее, почему кнопки не работают, выводилось чёрным по тёмному, то есть было невидимым именно тогда, когда нужнее всего.</li> <li data-num="1686"><span class="changelog-tag changelog-tag--DESIGN" data-tag-alias="DESIGN">DESIGN</span> <strong>Плашки-предупреждения перестали занимать полкарточки</strong> — они набирались размером основного текста и вытесняли саму реплику; теперь это компактное системное сообщение с иконкой.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.161</span><span class="changelog-meta__date">22-08-2026</span></span><h2 id="v9161-22-08-2026--музыка-часовые-записи-форматы-диктофона-и-понятные-отказы">Музыка: часовые записи, форматы диктофона и понятные отказы</h2></summary> <div class="changelog-section__body"> <p>Загрузить в журнал часовой концерт, лекцию или интервью раньше было почти невозможно: предел стоял один на все записи, а файл с айфона браузер отбивал ещё до отправки. Разобрали всю цепочку загрузки.</p> <ul> <li data-num="1678"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Предел размера зависит от длительности записи</strong> — короткому треку прежние 50 МБ, длинной записи до 128 МБ. Часовая лекция или концерт теперь проходят.</li> <li data-num="1679"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Принимаются m4a, ogg и flac — не только mp3 и wav</strong> — запись с диктофона айфона больше не отвергается со словами «неподдерживаемый формат», и подсказка под кнопкой наконец перечисляет все форматы.</li> <li data-num="1680"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Большой файл больше не обрывается на середине загрузки</strong> — внутренний предел ожидания отмерялся так, будто передача занимает секунды, и тяжёлая запись не доезжала никогда, сколько ни пробуй.</li> <li data-num="1681"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>
1Отказ говорит про музыку, а не про картинку</strong> — при слишком большом файле совет звучал как «уменьшите размер изображения». Теперь называется и причина, и действующий предел, а у пустого или повреждённого файла своя формулировка.</li> <li data-num="1682"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Материал из одного трека не задваивает название</strong> — плашка плеера и список треков печатали одно и то же; список остался только у альбомов, а в альбоме играющая строка теперь подсвечена.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.160</span><span class="changelog-meta__date">20-08-2026</span></span><h2 id="v9160-20-08-2026--ссылка-из-письма-поддержки-открывает-сам-разговор">Ссылка из письма поддержки открывает сам разговор</h2></summary> <div class="changelog-section__body"> <p>Написали в поддержку VPN, получили ответ на почту — и по ссылке из письма попадали на «страница не существует». Теперь ссылка открывает вашу переписку, причём с любого устройства: письмо можно прочитать на телефоне, даже если обращение вы оставляли с компьютера.</p> <ul> <li data-num="1676"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ссылка «Открыть диалог» больше не ведёт в никуда</strong> — адрес в письмах вёл на страницу, которой на сайте нет.</li> <li data-num="1677"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Разговор находится по ссылке, а не только по памяти браузера</strong> — раньше переписка была видна лишь в том браузере, где её начали: на другом устройстве вкладка «Мои обращения» оказывалась пустой.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.159</span><span class="changelog-meta__date">19-08-2026</span></span><h2 id="v9159-19-08-2026--портал-доступа-черновики-и-скорость-повторных-заходов">Портал доступа, черновики и скорость повторных заходов</h2></summary> <div class="changelog-section__body"> <p>Несколько разных исправлений, объединённых одним: каждое чинит место, где человек упирался в стену и не понимал, что сделал не так.</p> <ul> <li data-num="1672"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Повторная попытка получить ключ доступа больше не создаёт лишний ключ</strong> — при обрыве связи запрос на сервере продолжал работать, и второе нажатие забирало ещё один слот из вашего лимита. Теперь повтор возвращает уже созданный ключ.</li> <li data-num="1673"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница «Черновики» перестала падать у авторов, вошедших через всплывающее окно</strong> — открывалась пустая страница с ошибкой вместо списка работ.</li> <li data-num="1674"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Служебный раздел редакции закрыт по-настоящему</strong> — прежде его содержимое видел любой зарегистрированный читатель, а не участник редакции.</li> <li data-num="1675"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Повторные заходы на статьи и в раздел «Искусство» стали быстрее</strong> — готовая копия страницы живёт на серверах у края сети втрое дольше, а свежесть материала при этом не страдает: после правки в редакции копия сбрасывается сразу.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.158.1</span><span class="changelog-meta__date">18-08-2026</span></span><h2 id="v91581-18-08-2026--черновики-музыка-и-понятные-отказы-редактора">Черновики, музыка и понятные отказы редактора</h2></summary> <div class="changelog-section__body"> <p>Разбор всех актуальных issue проекта <code>discours-frontend</code> (окно 30 дней, 25 issue). Настоящих бага окаÐ
1·Ð°Ð»Ð¾ÑÑŒ три, остальное — шум чужих сред, который вытеснял их из инбокса.</p> <ul> <li data-num="1666"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница «Мои материалы» падала у тех, кто вошёл через модалку.</strong> Если открыть <code>/drafts</code> неавторизованным и войти прямо на странице, список материалов не загружался — приложение падало. Ломалась любая вкладка, не только «Совместные», и вместе со списком терялись фильтры из адреса страницы: поиск, статус, сортировка, номер страницы.</li> <li data-num="1667"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Отмена выбора файла роняла загрузку музыки.</strong> На <code>/post/music</code> достаточно было открыть диалог выбора файла и закрыть его — страница переставала отвечать. Тот же путь закрыт и для видео, где отмена превращалась в невнятный отказ загрузки.</li> <li data-num="1668"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Занятый адрес материала объясняется по-человечески.</strong> Раньше при совпадении адреса с уже существующим материалом автор получал «Не удалось создать материал» — это читается как поломка сервера, и люди отправляли форму снова. Теперь сообщение наз
1ывает занятый адрес и говорит, что менять, а бесполезная кнопка «Повторить» рядом с ним больше не показывается — при обрыве сети она остаётся на месте.</li> <li data-num="1669"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кадрирование обложки больше не спотыкается при быстром закрытии.</strong> Если закрыть окно обрезки, пока изображение ещё догружается, в редакторе возникала ошибка.</li> <li data-num="1670">[CHORE] <strong>Инбокс мониторинга очищен от чужого шума</strong>, который мешал видеть настоящие поломки: аналитика Cloudflare на старых Android- и Yandex-браузерах, мосты встроенных браузеров Telegram и подобных, расширения браузера. Это чужой код, и починить его нельзя — теперь он просто не засоряет отчёты.</li> <li data-num="1671"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Причина сбоя в отчётах называется, а не скрывается за «[object Object]».</strong> Отказ, не являющийся ошибкой, приезжал без заголовка и стектрейса, и разные сбои слипались в одну кучу. Теперь заголовок опознаваем, а подробности едут отдельным компактным слепком — с ограничением по числу полей и длине значений, чтобы в отчёт не попадало лишнее.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.152</span><span class="changelog-meta__date">18-08-2026</span></span><h2 id="v9152-18-08-2026--журнал-подписан-в-поиске-своим-именем">Журнал подписан в поиске своим именем</h2></summary> <div class="changelog-section__body"> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.158</span><span class="changelog-meta__date">18-08-2026</span></span><h2 id="v9158-18-08-2026--измерения-скорости-стали-пригодны-для-работы">Измерения скорости стали пригодны для работы</h2></summary> <div class="changelog-section__body"> <p>Служебная запись: читатель этих изменений не заметит, скорость журнала пока не менялась. Мы починили измерительные приборы, без которых нельзя доказать, что будущая оптимизация помогла.</p> <ul> <li data-num="1661"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Скорость загрузки теперь измеряется с долями, а не только жалобами.</strong> Раньше в отчёты попадали лишь плохие загрузки, поэтому «столько-то медленных открытий» не отвечало на вопрос, это редкость или норма — и после любой оптимизации число могло упасть просто из-за спада посещаемости. Теперь рядом собирается небольшая выборка обычных загрузок, и доля считается честно.</li> <li data-num="1662"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>
1Отчёт называет виновника, а не только время.</strong> К измерению добавлено, какой элемент страницы сдвинул вёрстку, что именно оказалось самым большим на экране и на что нажал читатель перед задержкой. Раньше отчёт сообщал «медленно», не говоря, из-за чего.</li> <li data-num="1663"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Измерение больше не приписывается чужой странице.</strong> Часть метрик снимается в момент ухода со страницы, и адрес в отчёте подменялся на тот, куда читатель успел перейти. Адрес измерения теперь хранится отдельно.</li> <li data-num="1664"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Убрана настройка, которая выглядела включённой и ничего не делала</strong> — сбор подробных трасс производительности требует части библиотеки, которую мы намеренно не берём в сборку, чтобы не утяжелять страницы.</li> <li data-num="1665"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сбойное измерение больше не выдаётся за медленную загрузку.</strong> Если браузер возвращал бессмысленное значение, прибор записывал его как худший возможный результат — то есть ошибка измерения превращалась в ложную тревогу и портила статистику. Теперь такие случаи помечаются отдельно и в подсчёт не идут.</li> </ul> <p>Следующий шаг — ускорение самих страниц: измерения показали, что дольше всего открываются материалы, которые давно никто не читал, потому что их готовая копия не успевает задержаться в кэше.</p> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.157</span><span class="changelog-meta__date">18-08-2026</span></span><h2 id="v9157-18-08-2026--журнал-подписан-в-поиске-своим-именем">Журнал подписан в поиске своим именем</h2></summary> <div class="changelog-section__body"> <p>В результатах Google над заголовком стоял адрес сайта вместо названия. Причина оказалась не в отсутствии имени, а в разнобое: поисковик собирает название из нескольких мест страницы сразу и, когда они расходятся, показывает адрес как единственный бесспорный вариант. Теперь все места говорят одно и то же.</p> <ul> <li data-num="1659"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>«Дискурс» вместо адреса в выдаче</strong> — название, превью для соцсетей и разметка страницы приведены к одной форме, а адрес главной записан всюду одинаково. Латинское написание осталось для англоязычной версии.</li> <li data-num="1660"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Служебная копия сайта убрана из поиска</strong> — предрелизная версия платформы отвечала поисковым роботам противоречиво, и её страницы попадали в выдачу рядом с настоящими. Читатель мог открыть из поиска не журнал, а стенд разработки.</li> </ul> <p>Обе договорённости теперь проверяются автоматически — при расхождении сборка краснеет, не дожидаясь, пока это заметят в выдаче через несколько недель.</p> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.156</span><span class="changelog-meta__date">15-08-2026</span></span><h2 id="v9156-15-08-2026--страницы-журнала-снова-открываются-из-кэша">Страницы журнала снова открываются из кэша</h2></summary> <div class="changelog-section__body"> <p>Готовые копии страниц должны храниться на серверах у края сети — тогда следующий читатель получает страницу почти мгновенно. Оказалось, что этот механизм у нас месяцами молча не работал: девять из десяти попыток сохранить копию отвергались из-за одной служебной строки в заголовках, и каждый читатель платил полным ожиданием.</p> <ul> <li data-num="1658"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Кэш готовых страниц заработал</strong> — повторные заходы на популярные материалы стали заметно быстрее, а нагрузка на серверы журнала ниже. Отказ был беззвучным: в интерфейсе всё выглядело исправным.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.155</span><span class="changelog-meta__date">13-08-2026</span></span><h2 id="v9155-13-08-2026--материал-не-подменяется-черновиком-карточки-без-обложки-ожили">Материал не подменяется черновиком, карточки без обложки ожили</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="1655"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Открытие материала на правку больше не подменяет его Ð
1½Ð¾Ð²Ñ‹Ð¼ черновиком</strong> — из-за залипшего следа предпросмотра редактор мог увести автора в создание черновика вместо нужного материала, да ещё и от чужого имени.</li> <li data-num="1656"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Карточка материала без обложки больше не выглядит зависшей</strong> — вместо вечного серого прямоугольника, который не открывался по клику, теперь узнаваемый орнамент издания и обычная рабочая карточка.</li> <li data-num="1657"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Заглушка обложки одна и та же при первой отрисовке и при догрузке</strong> — раньше картинка успевала смениться на другую прямо на глазах.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.154</span><span class="changelog-meta__date">09-08-2026</span></span><h2 id="v9154-09-08-2026--ленты-и-обложки-перестали-тянуть-лишние-мегабайты">Ленты и обложки перестали тянуть лишние мегабайты</h2></summary> <div class="changelog-section__body"> <p>Списки материалов скачивали полные тексты статей, которых в карточках всё равно не видно. На ленте из десяти карточек это сотни лишних килобайт, на большой теме — мегабайты, и всё это оплачивал мобильный трафик читателя.</p> <ul> <li data-num="1653"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ленты материалов, разделы и страницы тем стали в разы легче</strong> — на трёх маршрутах разом ушло больше мегабайта на выдачу, а на крупной теме — до трёх мегабайт.</li> <li data-num="1654"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Обложка больше не растягивается сверх своего оригинала</strong> — раньше на широком экране браузер скачивал раздутую копию, тяжелее исходника и без единого нового пикселя. У двух третей материалов картинка теперь легче при той же чёткости.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.153</span><span class="changelog-meta__date">07-08-2026</span></span><h2 id="v9153-07-08-2026--анимированные-обложки-открываются-пределы-загрузки-понятны-заранее">Анимированные обложки открываются, пределы загрузки понятны заранее</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="1650"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Анимированные GIF-обложки снова видны</strong> — при подготовке к показу они распухали в разы и не открывались вовсе: вместо обложки читатель видел пустоту, а в мессенджерах ломалось превью ссылки. Теперь такие картинки отдаются как есть.</li> <li data-num="1651"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>PDF от автора без аккаунта принимается наравне со всеми</strong>
1 — заявку с крупным документом отбивало ещё до отправки, хотя сервер её принял бы.</li> <li data-num="1652"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Предел размера файла виден до загрузки, а не после</strong> — раньше можно было несколько минут ждать конца выгрузки, чтобы получить отказ.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.152</span><span class="changelog-meta__date">05-08-2026</span></span><h2 id="v9152-05-08-2026--настройка-«уменьшить-движение»-наконец-работает">Настройка «уменьшить движение» наконец работает</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="1646"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Системная настройка «уменьшить движение» снова действует</strong> — из-за ошибки, возникавшей только при сборке боевой версии, у людей, попросивших меньше анимации, всё продолжало двигаться в полную силу. На сайте это никак не проявлялось внешне, и потому жило незамеченным.</li> <li data-num="1647"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Ссылка «перейти к содержимому» починена</strong> — она нужна тем, кто ходит по сайту с клавиатуры и через экранные читалки.</li> <li data-num="1648"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>У страниц-разделов появился нормальный заголовок в исходном коде</strong> — его не видели ИИ-ассистенты, боты превью и внешние аудиторы, работающие без исполнения скриптов.</li> <li data-num="1649"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Откат к предыдущей версии материала сохраняет язык</strong> — при восстановлении версии язык материала сбрасывался.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.151</span><span class="changelog-meta__date">26-07-2026</span></span><h2 id="v9151-26-07-2026--английская-версия-описания-для-поиска-типографика-и-починенные-склонения">Английская версия: описания для поиска, типографика и починенные склонения</h2></summary> <div class="changelog-section__body"> <p>Ссылка на англоязычную страницу теперь и в поиске, и в мессенджере выглядит по-английски: переведены описания и превью для соцсетей. Попутно вылечены три случая, когда перевод в словаре был, но на страницу не попадал.</p> <ul> <li data-num="1640"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Описания для поиска и шаринга на английском</strong> — <code>description</code>, <code>keywords</code> и Open Graph на 30 страницах. Раньше англоязычная ссылка в выдаче и в мессенджере оставалась с русским описанием.</li> <li data-num="1641"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Как устроен Дискурс» — вводная часть и рассказ о темах</strong> переведены вместе со всеми внутренними ссылками.</li> <li data-num="1642"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Рассылки, экраны блокировок и служебные подписи</strong> — управление подписками, веб-версия письма, «Заблокировано» и «Запрещено цензурой», а также «Skip to content», «Back to top» и подписи иллюстраций.</li> <li data-num="1643"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Счётчики правок в черновиках говорили по-русски</strong> — на <code>/en</code> рядом с числом стояла русская форма «новых». Счётчики переведены на общую локализованную механику склонений.</li> <li data-num="1644"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Неразрывные пробелы больше не ломают перевод</strong> — из-за них не находились переводы девиза на главной и подсказок в профиле: текст в разметке и ключ в словаре расходились на невидимый символ. Теперь совпадение ищется независимо от расстановки, а русская типографика при переводе не страдает.</li> <li data-num="1645"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Язык страницы объявляется сразу</strong> — на <code>/en</code> атрибут языка и заголовок вкладки английские с первой отрисовки, не после загрузки приложения. Это важно для скринридеров и поисковых роботов.</li> </ul> <p>Целостность словаря теперь проверяется автоматически: строку, обёрнутую в перевод без самого перевода, больше нельзя случайно выпустить в прод.</p> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.150</span><span class="changelog-meta__date">14-07-2026</span></span><h2 id="v9150-14-07-2026--скорость-журнал-открывается-быстрее-и-расходует-меньше-трафика">Скорость: журнал открывается быстрее и расходует меньше трафика</h2></summary> <div class="changelog-section__body"> <p>Выпуск целиком про скорость. Мы прошлись по всему пути загрузки страницы — стили, шрифты, картинки, скрипты — и облегчили каждый шаг. Особенно это заметно на телефонах и медленном интернете.</p> <ul> <li data-num="1633"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Страницы начинают отрисовываться раньше</strong> — стили редких разделов (подкасты, цитаты, личный кабинет и другие) больше не тормозят каждую страницу: в критическом пути загрузки стало на треть меньше стилей, остальное догружается незаметно в фоне.</li> <li data-num="1634"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Картинки в статьях — по размеру экрана</strong> — иллюстрации в тексте приходят в подходящем для устройства размере вместо максимального: на телефонах это в разы меньше мегабайт при той же чёткости. Заодно исправлена скрытая неэффективность, из-за которой одна и та же картинка могла скачиваться дважды.</li> <li data-num="1635"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Шрифты полегчали и подключаются раньше</strong> — фирменные шрифты избавились от неиспользуемых алфавитов, а заголовки статей больше не ждут полной загрузки стилей, чтобы отрисоваться фирменной гарнитурой.</li> <li data-num="1636"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Меньше фоновой работы у читателей</strong> — служебное соединение для мгновенных уведомлений теперь устанавливается только у вошедших в аккаунт: гостям оно не нужно и лишь занимало ресурсы устройства.</li> <li data-num="1637"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Обложка статьи не скачивается дважды на retina-экранах</strong> — предзагрузка обложки и её финальная версия наконец сошлись в размерах на дисплеях высокой чёткости.</li> <li data-num="1638"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Строже с безопасностью соединения</strong> — браузеру теперь явно предписано ходить в журнал только по защищённому протоколу.</li> <li data-num="1639"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Журнал представился ИИ-ассистентам</strong> — по стандартному адресу появилась карта журнала для ИИ-поисковиков: ChatGPT, Perplexity и другие ассистенты теперь корректно узнают о разделах и принципах Дискурса.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.149</span><span class="changelog-meta__date">11-07-2026</span></span><h2 id="v9149-11-07-2026--совместный-редактор-у-правок-появились-авторы-фильтры-стали-компактнее">Совместный редактор: у правок появились авторы, фильтры стали компактнее</h2></summary> <div class="changelog-section__body"> <p>Выпуск про совместную работу над текстами: карточки правок обрели человеческие лица, замены принимаются одним нажатием, а панель комментариев стала аккуратнее.</p> <ul> <li data-num="1629"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>У правок видно автора</strong> — предложенные изменения в тексте теперь подписаны: имя, аватар и ссылка на профиль автора правки видны всем участникам документа, как это давно устроено у комментариев. Больше не нужно угадывать, кто предложил замену.</li> <li data-num="1630"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Замена принимается одним нажатием</strong> — правка «заменить одно на другое» применяется целиком: зачёркнутый текст удаляется, предложенный — остаётся, и всё это одним кликом и одним шагом отмены.<
1/li> <li data-num="1631"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Фильтры комментариев — в компактной кнопке</strong> — два ряда переключателей в панели комментариев переехали в аккуратную кнопку рядом с крестиком. Внутри — фильтры по статусу и источнику, а точка-индикатор подсказывает, что фильтр активен.</li> <li data-num="1632"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Зачёркивание в карточках правок — по длине текста</strong> — красная полоса удаляемого фрагмента больше не растягивается на всю ширину карточки.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.148</span><span class="changelog-meta__date">07-07-2026</span></span><h2 id="v9148-07-07-2026--чёткие-картинки-на-любых-экранах-надёжная-доставка-медиа-и-анонимная-поддержка">Чёткие картинки на любых экранах, надёжная доставка медиа и анонимная поддержка</h2></summary> <div class="changelog-section__body"> <p>Июльский выпуск — про изображения и про тех, кто пишет нам, не называя себя. Фотографии стали чётче на телефонах и надёжнее доходят до читателя, а помощь с доступом теперь можно получить анонимно.</p> <ul> <li data-num="1623"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Картинки и аудио доходят надёжнее</strong> — медиафайлы журнала переехали на новую схему доставки: иллюстрации и подкасты открываются в том числе там, где прежние адреса блокировались. Заходите вы с основного адреса или с зеркала — изображения на месте.</li> <li data-num="1624"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Чёткость на retina и мобильных</strong> — экраны с высокой плотностью пикселей теперь получают картинку подходящего размера. Фотографии перестали выглядеть замыленными на телефонах и современных дисплеях.</li> <li data-num="1625"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Материал не сохранится без картинок</strong> — если вставленные изображения ещё не догрузились, редактор дождётся их, прежде чем отправить материал на сервер. Публикации с «дырами» вместо иллюстраций остались в прошлом.</li> <li data-num="1626"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Анонимное обращение в поддержку</strong> — помощь с доступом к журналу теперь можно получить, не называя себя. Экран с ключом стал проще и понятнее.</li> <li data-num="1627"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница Конституции приведена в порядок</strong> — заголовок и нумерация выровнены по сетке, стыки блоков починены, лишний баннер убран. Основополагающий документ журнала снова выглядит достойно.</li> <li data-num="1628"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мелкая полировка</strong> — плавающие баннеры аккуратно останавливаются у отбивки, список подписок в кабинете выровнен по левому краю, блок «Темы» в мобильном редакторе встал вровень с рубрикой и кнопками.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.147</span><span class="changelog-meta__date">26-06-2026</span></span><h2 id="v9147-26-06-2026--карты-смыслов-совместное-редактирование-в-реальном-времени-и-полный-английский-интерфейс">Карты смыслов: совместное редактирование в реальном времени. И полный английский интерфейс</h2></summary> <div class="changelog-section__body"> <p>В июне у Дискурса появился новый инструмент — «Карты смыслов»: живой граф идей и понятий, который сообщество редактирует вместе, в реальном времени, как общий документ. А англоязычная версия журнала дозрела: переведено всё — от манифеста до личного кабинета.</p> <h3 id="карты-смыслов">Карты смыслов</h3> <ul> <li data-num="1610"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Новый раздел <code>/maps</code></strong> — граф идей сообщества: узлы-понятия, связи, кластеры и отдельные виды для разных сюжетов. Сложные темы, которые не помещаются в линейный текст, теперь можно раскладывать в пространстве — и видеть целиком.</li> <li data-num="1611"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Редактируем карту вместе и одновременно</strong> — видно, кто сейчас на карте, курсоры коллег двигаются в реальном времени, раскладка у всех одинаковая. Клик по аватару участника — и вы смотрите туда же, куда смотрит он.</li> <li data-num="1612"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Обсуждения прямо на узлах</strong> — к каждому понятию можно оставить комментарий или реакцию. Спор о формулировке живёт рядом с самой формулировкой, а не теряется в стороннем чате.</li> <li data-num="1613"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>История версий, отмена шагов и экспорт</strong> — любое состояние карты можно откатить, случайное действие — отменить, а готовую карту выгрузить картинкой в PNG или SVG.</li> <li data-num="1614"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Быстрая работа с большим графом</strong> — горячие клавиши со шпаргалкой по <code>?</code>, мультивыбор рамкой, групповое перетаскивание узлов, сворачивание кластеров. Крупные карты перестали быть неповоротливыми.</li> </ul> <h3 id="дискурс-на-английском">Дискурс на английском</h3> <ul> <li data-num="1615"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Интерфейс <code>/en</code> переведён целиком</strong> — не только публичные страницы, но и личный кабинет, черновики и уведомления; числа и формы слов — по правилам английского. Начатое в прошлом выпуске доведено до конца.</li> <li data-num="1616"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Манифест, FAQ и страницы «О журнале» — на английском</strong> — философия открытой редакции читается теперь на двух языках, включая спецпроекты и базу знаний.</li> </ul> <h3 id="авторам">Авторам</h3> <ul> <li data-num="1617"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Материал на рассмотрении можно забрать</strong> — передумали публиковать? Отправленный в редакцию материал автор теперь удаляет сам, в том числе с телефона, — без письма редакции и ожидания.</li> <li data-num="1618"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Адрес профиля — сам собой</strong> — кириллическое имя автоматически превращается в аккуратный латинский адрес страницы. Красивая ссылка на профиль без ручной транслитерации.</li> <li data-num="1619"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Тонкая настройка рассылки</strong> — страница подписок с тумблерами: отключайте отдельные типы писем, не отписываясь от всего. Общий переключатель — для тех, кому нужно всё или ничего.</li> </ul> <h3 id="доступ-и-надёжность">Доступ и надёжность</h3> <ul> <li data-num="1620"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Вход через Google — с зеркал</strong> — вслед за ВКонтакте: когда основной адрес недоступен, войти в журнал через Google-аккаунт можно с любого зеркала.</li> <li data-num="1621"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Иконки соцсетей видны в Safari</strong> — техническая особенность Safari делала иконки в шапке невидимыми. Починили — а заодно навели порядок в мобильном бургер-меню.</li> <li data-num="1622"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Ещё две волны обновлений безопасности</strong> — закрыты уязвимости в зависимостях, а в CI появился автоматический шлагбаум: сборка с известными дырами просто не пройдёт.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.146</span><span class="changelog-meta__date">04-06-2026</span></span><h2 id="v9146-04-06-2026--обновлённая-поддержка-ин
1струменты-статей-в-экспо-редакторах-и-дружелюбные-экраны-сбоев">Обновлённая поддержка, инструменты статей в экспо-редакторах и дружелюбные экраны сбоев</h2></summary> <div class="changelog-section__body"> <p>Выпуск о заботе: о тех, кому нужны помощь и доступ, о художниках и поэтах — и о том, как журнал разговаривает с вами, даже когда что-то пошло не так.</p> <h3 id="поддержка-и-доступ">Поддержка и доступ</h3> <ul> <li data-num="1598"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Служба поддержки повзрослела</strong> — в окне поддержки появились вкладки, несколько обращений сразу и обязательное поле контакта, а ошибки объясняются человеческим языком. FAQ пополнился ответами под свежую волну блокировок.</li> <li data-num="1599"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Получить VPN» — на виду</strong> — кнопка помощи с доступом видна в шапке сразу, памятка свёрнута до востребования. Тем, кому нужен свободный интернет, не приходится его искать по сайту.</li> </ul> <h3 id="экспо-редакторы">Экспо-редакторы</h3> <ul> <li data-num="1600"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Экспо-редакторы догнали статьи</strong> — спойлер, оглавление, «читать также», сноски и история версий теперь есть и в редакторах литературы, галерей, музыки и видео. Художественным материалам — тот же инструментарий, что и статьям.</li> <li data-num="1601"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Хоткей для поэтов</strong> — Ctrl/Cmd+Shift+Enter склеивает абзацы в стихотворную строфу. Стихи верстаются с клавиатуры, без плясок с форматированием.</li> </ul> <h3 id="журнал-и-кабинет">Журнал и кабинет</h3> <ul> <li data-num="1602"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница авторов показывает всех</strong> — список авторов обрывался и оставлял пустые буквенные рамки. Теперь на месте каждый, и вёрстка ровная.</li> <li data-num="1603"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Лента Редакции без дна</strong> — во вкладках «Все» и «Чулан» появилась кнопка «показать ещё» за всё время, а страницы ленты стали легче и грузятся быстрее.</li> <li data-num="1604"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Письма рассылки обрели веб-версию</strong> — каждое письмо можно открыть в браузере, а отписка ведёт на аккуратную страницу с понятными названиями рассылок вместо технических кодов.</li> <li data-num="1605"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уведомления перестали пропадать</strong> — редкая гонка при входе оставляла колокольчик пустым. Теперь фоновое соединение устанавливается надёжно, и уведомления всегда на месте.</li> <li data-num="1606"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Черновик можно удалить только свой</strong> — защита от случайного удаления чужого черновика в совместной работе.</li> <li data-num="1607"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Экраны сбоев говорят по-человечески</strong> — вместо тревожного «Сервер недоступен» — спокойное объяснение в фирменном чёрно-белом стиле. Даже когда что-то сломалось, Дискурс остаётся собой.</li> </ul> <h3 id="невидимая-работа">Невидимая работа</h3> <ul> <li data-num="1608"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Большая уборка для поисковиков</strong> — точнее разметка страниц и подкастов, устойчивее сборка карт сайта. Материалы быстрее и правильнее попадают в выдачу.</li> <li data-num="1609"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Закрыта волна уязвимостей</strong> — обновлён десяток зависимостей с известными проблемами и усилены внутренние проверки. Читателю это не видно — и это лучшая новость.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.145</span><span class="changelog-meta__date">17-05-2026</span></span><h2 id="v9145-17-05-2026--дискурс-на-английском-полностью-переведены-все-тексты-интерфейса">Дискурс на английском: полностью переведены все тексты интерфейса</h2></summary> <div class="changelog-section__body"> <p>Сотни строк, которые на <code>discours.io/en</code> ещё оставались русскими, теперь на английском: главная, теги, подкасты, страница поддержки, профиль, мои публикации, редактор статей.</p> <ul> <li data-num="1593"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Интерфейс /en полностью на английском</strong> — заголовки разделов, описания, FAQ, пустые состояния, туÐ
1»Ñ‚ипы и подсказки переведены. Англоязычный читатель видит интерфейс целиком на родном языке.</li> <li data-num="1594"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Редактор — на английском от первой до последней кнопки</strong> — все модалки переведены: вставка ссылки и видео, сноски, импорт/экспорт, настройки публикации, горячие клавиши, приглашение соавторов, репорт о баге. Создавать статью на <code>/en</code> теперь можно без словаря под рукой.</li> <li data-num="1595"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Страница поддержки переведена полностью</strong> — на <code>/help</code> на английском теперь и вступление, и философия журнала, и FAQ-блоки. Тон — литературный, как и у русской версии.</li> <li data-num="1596"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Приглашение соавторов переключает язык на лету</strong> — открыли модалку, переключили локаль в браузере — роли, статусы и обратный отсчёт «3 days left» обновятся без перезагрузки.</li> <li data-num="1597"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Telegram-бот в настройках говорит на двух языках</strong> — англоязычный автор видит в профиле, что @DiscoursPostBot пришлёт статусы материалов и редакционные уведомления. Меньше пропущенных писем из-за языкового барьера.</li> </ul> <p>На очереди — переводы описаний подкастов на стороне CMS (сейчас сами описания остаются русскими; UI-обёртка «Listen wherever you prefer» — уже на английском).</p> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.144</span><span class="changelog-meta__date">16-05-2026</span></span><h2 id="v9144-16-05-2026--обложки-дышат-мобильный-интерфейс-плотнее-карта-сайта-до-края-журнала">Обложки дышат, мобильный интерфейс плотнее, карта сайта до края журнала</h2></summary> <div class="changelog-section__body"> <p>Авторскую фотографию больше не приходится подгонять под формат — обложка статьи сама ловит её пропорции. Шапка перестала дёргаться при прокрутке, кнопки точнее под пальцем, чтение с телефона стало плотнее и спокойнее. А поисковикам открылись все материалы, принятые сообществом — каждая новая публикация быстрее доходит до читателя.</p> <h3 id="обложки-и-видимость-в-поиске">Обложки и видимость в поиске</h3> <ul> <li data-num="1579"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Обложка дышит под картинку</strong> — раньше обложка статьи была жёстко 16:9 или 4:3, и непропорциональные снимки обрезались уродливо. Теперь пропорции плавно перетекают между 4:3 и 16:9 в зависимости от исходного изображения. Авторская фотография живёт в той форме, в которой её снял автор.</li> <li data-num="1580"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Карта сайта дотягивается до всех принятых материалов</strong> — раньше в sitemap попадали только опубликованные. Теперь — и принятые сообществом, и отложенные. Каждая публикация быстрее находит читателя через поисковую выдачу.</li> </ul>
1 <h3 id="мобильный-интерфейс-и-шапка">Мобильный интерфейс и шапка</h3> <ul> <li data-num="1581"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопка «наверх» — компактнее и говорящая</strong> — стрелка по центру, при наведении инвертирует цвет, тултип объясняет, что произойдёт. Не загадка из угла, а понятный инструмент.</li> <li data-num="1582"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Шапка перестала дёргаться при прокрутке</strong> — раньше при медленной прокрутке скрытая шапка могла прыгать вверх-вниз. Теперь — устойчивый плавный переход, и взгляд не теряется в материале.</li> <li data-num="1583"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильное меню не вызывает случайную перезагрузку</strong> — на iOS жест открытия меню мог сработать как pull-to-refresh и сбросить страницу. Больше нет — открытие меню больше не уносит вас обратно к ленте.</li> <li data-num="1584"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Иконки и панель поиска в редакторе — на одной линии</strong> — шапка и инструменты редактора выровнены визуально, без «прыжков» при переключении режимов. Глазу спокойнее, рукам точнее.</li> <li data-num="1585"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопки оценки в подвале — компактнее на телефонах</strong> — занимают меньше места, не перекрывают финал текста. Дочитать статью теперь можно без визуального шума.</li> <li data-num="1586"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Жанры и рубрики — «Эссе», «Репортажи», «Интервью»</strong> — журнальная вёрстка вместо строчных букв в боковом меню. Мелочь, но Дискурс выглядит как журнал, а не как лог файловой системы.</li> </ul> <h3 id="чтение-и-редактор">Чтение и редактор</h3> <ul> <li data-num="1587"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сноски не уводят на главную из Telegram-Android</strong> — нажатие на сноску внутри WebView Telegram-Android раньше открывало главную discours.io вместо прокрутки к примечанию. Теперь сноска ведёт туда, куда обещает.</li> <li data-num="1588"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Чистка заголовка при сохранении</strong> — лишние теги в заголовке статьи (после вставки из браузера или Word) снимаются автоматически. Заголовок остаётся заголовком, без мусора из исходного буфера.</li> <li data-num="1589"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Осмысленное превью профиля автора</strong> — если у автора пустое bio, при шеринге профиля в превью появится внятный fallback вместо технической пустоты. Первое впечатление о коллеге — не «нет данных».</li> </ul> <h3 id="вход-и-админка">Вход и админка</h3> <ul> <li data-num="1590"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Вход через ВКонтакте работает на зеркалах</strong> — раньше OAuth через VK был только на основном домене. Теперь — на любом зеркале. Когда основной сайт временно недоступен, дверь в журнал остаётся открытой.</li> <li data-num="1591"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Счётчик материалов у тегов в админке</strong> — у каждого тега виден счётчик публикаций сразу в списке, без открытия его страницы. Удобно, когда нужно навести порядок в темах.</li> </ul> <h3 id="наблюдаемость">Наблюдаемость</h3> <ul> <li data-num="1592"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Тише и точнее логи ошибок</strong> — поток мусорных событий (расширения браузера, сетевые таймауты у читателей, фоновые таб-метрики) перестал заглушать реальные проблемы. Инциденты ловятся за минуты, а не за часы — значит и чинятся раньше, чем кто-то из вас успеет о них написать.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.143</span><span class="changelog-meta__date">02-05-2026</span></span><h2 id="v9143-02-05-2026--манифест-страница-голос-дискурса-п
1ротив-цензуры">Манифест-страница: голос Дискурса против цензуры</h2></summary> <div class="changelog-section__body"> <p><code>/stop-censorship</code> — это наше прямое обращение к тем, через чьи руки идут блокировки. Не угроза, не упрёк — разговор. После доводки страница стала такой, какой и задумывалась: понятной, человеческой, тёплой. Если вы не цензор — заходите, мы рады собеседнику.</p> <ul> <li data-num="1575"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Фирменная типография Дискурса</strong> — Конституция и акценты набраны Muller, тело текста — Spectral. Тот же визуальный голос, что у журнала: страница не «дашборд блокировок», а часть Дискурса.</li> <li data-num="1576"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Тёплое обращение в конце</strong> — «Если вы тоже не цензор — давайте разберёмся вместе». Не канцелярское «свяжитесь с нами», а прямой человеческий разговор. Дискурс открывает дверь, а не строит ещё одну стену.</li> <li data-num="1577"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Конституция собрана воедино</strong> — статья 29 (основа всего щита) подана цельным фрагментом, без визуального разделения. Смысловые акценты — на смысле, не на оформлении.</li> <li data-num="1578"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Минимум хрома, максимум смысла</strong> — лишние UI-элементы (временный счётчик блокировок, технические разделители) убраны. На странице — только то, ради чего она существует.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.142</span><span class="changelog-meta__date">29-04-2026</span></span><h2 id="v9142-29-04-2026--ссылки-которые-не-«протухают»-новые-видео-и-большой-seo-апдейт">Ссылки, которые не «протухают», новые видео и большой SEO-апдейт</h2></summary> <div class="changelog-section__body"> <p>Переименовали статью — старая ссылка продолжает работать. Хочется вставить видео не с YouTube, а с Мейл.ру или Одноклассников — пожалуйста. А для тех, кто приходит к нам из поисковиков, целый пакет улучшений: подробные метаданные, голосовое чтение статей, фотографии в индексе картинок.</p> <h3 id="ссылки-которые-живут">Ссылки, которые живут</h3> <ul> <li data-num="1566"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Адресная история статей: старые ссылки навсегда</strong> — сменили заголовок, поправили URL, заменили слаг — все старые ссылки в Telegram, ВКонтакте, рассылках и поиске автоматически ведут на актуальный адрес. Никаких 404 после редизайна заголовка, никаких потерянных читателей.</li> <li data-num="1567"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Прямая ссылка на черновик</strong> — соавтор получает <code>/draft/&lt;код&gt;</code> и попадает прямо в нужный черновик, без поиска в списке. Совместная работа начинается с одного клика.</li> </ul> <h3 id="новые-видеоэмбеды">Новые видеоэмбеды</h3> <ul> <li data-num="1568"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Видео с my.mail.ru, ok.ru и vkvideo.ru</strong> — ролики с этих сервисов вставляются в материал так же легко, как YouTube или Vimeo. Хорошая новость для авторов, чьи источники живут только там.</li> </ul> <h3 id="контент-в-поиске">Контент в поиске</h3> <ul> <li data-num="1569"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Профили авторов связаны с их соцсетями</strong> — Google понимает, что Telegram-канал, ВКонтакте и Twitter автора — это один и тот же человек, и подтягивает все его публикации в одну страницу выдачи. Автор становится виднее не только в Дискурсе, но и за его пределами.</li> <li data-num="1570"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Голосовые помощники зачитывают статьи</strong> — Speakable-разметка добавлена в публикации: Google Assistant и Siri Ð
1·Ð½Ð°ÑŽÑ‚, какую часть статьи проговорить вслух. Слушать Дискурс на пробежке — теперь не метафора.</li> <li data-num="1571"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Иллюстрации статей — в индексе картинок Google</strong> — обложки и фотографии материалов собираются в отдельный image sitemap. Теперь они находятся через поиск по картинкам и приводят читателей обратно к статье.</li> <li data-num="1572"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Свежие материалы в Google News</strong> — отдельный sitemap для публикаций последних 48 часов даёт им шанс попасть в новостную выдачу, не дожидаясь обычной индексации.</li> <li data-num="1573"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Robots.txt без устаревшего шума</strong> — старые запреты для UTM-параметров убраны (поисковики и так их игнорируют). Чистая конфигурация, удобная для аудита.</li> </ul> <h3 id="редактор">Редактор</h3> <ul> <li data-num="1574"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авто-выравнивание при вставке из Word</strong> — раньше при копи-пасте из Word текст мог сохранить «justify» и ломать вёрстку. Теперь нормализуется до стандартной типографики Дискурса.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.141</span><span class="changelog-meta__date">27-04-2026</span></span><h2 id="v9141-27-04-2026--первая-публикация-всё-происходит-само">Первая публикация: всё происходит само</h2></summary> <div class="changelog-section__body"> <p>Сообщество поддержало вашу первую публикацию — поздравляем, вы автор. Без задержки, без писем редактору, без «когда же мне дадут роль». А подкастеры получили отдельный приятный шов: эпизоды теперь шерятся со своей обложкой, а не с общей картинкой канала.</p> <ul> <li data-num="1562"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Принят первый материал — стал автором мгновенно</strong> — раньше после первой публикации читатель ещё долго числился «читателем» в системе, пока куратор вручную не повышал его. Теперь повышение происходит автоматически в момент принятия материала редакцией. Журнал не заставляет ждать своих.</li> <li data-num="1563"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Уведомление «вы теперь автор»</strong> — отдельный тёплый тип уведомления с поздравлением и подсказкой, что делать дальше: где ваш профиль, как голосовать за коллег, куда написать редакции.</li> <li data-num="1564"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Своё OG-превью для каждого эпизода подкаста</strong> — при шеринге конкретного эпизода в Telegram, ВКонтакте или Facebook появл
1яется обложка именно этого эпизода с названием, а не общая картинка канала. Подкастеры, ваши репосты теперь работают на каждый выпуск отдельно.</li> <li data-num="1565"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Серая подложка под обложками музыкальных материалов</strong> — обложки разного формата (квадратные, вертикальные, тёмные) перестали «висеть в воздухе»: лента выглядит ровнее, музыка визуально не теряется среди статей.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.140</span><span class="changelog-meta__date">25-04-2026</span></span><h2 id="v9140-25-04-2026--невидимое-усиление-фундамент-в-поиске-и-устойчивость-к-сбоям">Невидимое усиление: фундамент в поиске и устойчивость к сбоям</h2></summary> <div class="changelog-section__body"> <p>Невидимая половина работы — то, что читатель напрямую не заметит, но без чего платформа была бы хрупче. Поисковики получают подробную карту сайта в современном формате. А редактор стал умнее: «сеть моргнула» — это одно, «у вас отозвали доступ» — совсем другое. Авторы перестали видеть пугающее «нет доступа» там, где просто моргнула связь.</p> <h3 id="видимость-в-поиске">Видимость в поиске</h3> <ul> <li data-num="1555"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Хлебные крошки в Google</strong> — структурная разметка добавлена для всех типов страниц: в результатах поиска видно «Дискурс › Темы › Кино» вместо голой ссылки. Читатель сразу понимает, куда попадёт.</li> <li data-num="1556"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Hreflang ru + x-default</strong> — Google и Yandex знают, что основная аудитория Дискурса русскоязычная, а зеркала — её альтернативные доступы. Никакой путаницы в выдаче.</li> <li data-num="1557"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Sitemap чище</strong> — фильтр исключает черновики, тестовые аккаунты и системные страницы. Поисковикам достаётся только то, что вы хотели бы видеть в выдаче.</li> <li data-num="1558"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>404 категоризируются правильно</strong> — поисковик понимает, что страница исчезла, а не что сервер сломал. Репутация Дискурса в выдаче не страдает от случайных таймаутов.</li> </ul> <h3 id="редактор-и-устойчивость">Редактор и устойчивость</h3> <ul> <li data-num="1559"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сноски в редакторе не перепрыгивают каретку</strong> — раньше при работе со сносками курсор мог уехать в начало пустого абзаца. Теперь стоит ровно там, где поставили.</li> <li data-num="1560"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Редактор отличает «сеть моргнула» от «нет доступа»</strong> — когда бэкенд занят или недоступен, фронт это понимает: декоративные запросы (превью, метаданные) перестают дёргать сервер, пока он не вернётся, а реальная авторизация остаётся живой. Авторы больше не видят пугающее «нет доступа» там, где просто моргнула связь.</li> <li data-num="1561"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>+153 теста для критичного ядра</strong> — санитайзеры HTML, SEO-генераторы и логика редиректов теперь под покрытием. Регрессии ловятся до релиза, не пользователями — а значит, не вами.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.139</span><span class="changelog-meta__date">23-04-2026</span></span><h2 id="v9139-23-04-2026--цензурный-щит-точнее-только-государственные-ведомства">Цензурный щит точнее: только государственные ведомства</h2></summary> <div class="changelog-section__body"> <p>Щит перестал ошибаться адресом. Раньше он отгораживался сразу от всей российской сети — а вместе с ней и от обычных читателей. Теперь — точечно: только сети 46 государственных ведомств. Билайн, МТС, Йота, домашние провайдеры — заходите свободно.</p> <ul> <li data-num="1553"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Точно по адресу, без коллатерального ущерба</strong> — раньше щит блокировал больше двух тысяч российских автономных систем, среди которых были и крупные провайдеры. Теперь источник списка — gov-курированный набор из 46 ASN, реально принадлежащих государственным ведомствам (РКН, ФСБ, МВД, прокуратура). Обычные читатели больше не натыкаются на 451 без вины.</li> <li data-num="1554"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Манифест-страница 451</strong> — у <code>/stop-censorship</code> появилась полноценная публичная страница, объясняющая что и почему мы делаем: статья 29 Конституции, обращение, контекст. Это не «error», а позиция.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.138</span><span class="changelog-meta__date">22-04-2026</span></span><h2 id="v9138-22-04-2026--связка-с-telegram-в-один-тап-и-тёплый-редактор">Связка с Telegram в один тап и тёплый редактор</h2></summary> <div class="changelog-section__body"> <p>Привязать Telegram — теперь дело одного нажатия, без квестов с кодами. Заодно прошлись по редактору: поиск ловит подписи к иллюстрациям, тёмная тема перестала оставлять белые пятна, а режим совместных правок больше не оставляет «призраков» удалённых вставок.</p> <h3 id="telegram">Telegram</h3> <ul> <li data-num="1545"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Связка с Telegram в один тап</strong> — кнопка «Привязать Telegram» в настройках профиля: нажали → команда <code>/link</code> сама скопировалась в буфер → отправили боту → готово. @DiscoursPostBot становится живым каналом уведомлений — про комментарии к вашим статьям, ремарки, приглашения.</li> <li data-num="1546"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>В буфер копируется команда целиком</strong> — раньше попадал только короткий код, и читатели терялись, что с ним делать. Теперь — готовая к отправке команда <code>/link &lt;код&gt;</code>. Никаких догадок.</li> </ul> <h3 id="редактор-1">Редактор</h3> <ul> <li data-num="1547"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Локализация интерфейса и паритет тёмной темы</strong> — все строки внутри редактора переведены, тёмная тема перестала оставлять белые пятна на тулбаре и подсказках. Тёмная сессиÑ
1 — действительно тёмная, и взгляд на длинной статье не устаёт.</li> <li data-num="1548"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Поиск работает по подписям иллюстраций</strong> — Ctrl+F теперь видит текст подписей, а не только основной текст статьи. Особенно ценно в длинных репортажах с десятками фотографий.</li> <li data-num="1549"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Вставка в подпись больше не ломает каретку</strong> — IME-композиция (китайский, японский, русский с заглавной) сохраняется при вставке, курсор не прыгает.</li> <li data-num="1550"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Удаление своей вставки в режиме совместных правок</strong> — раньше удаление свежего фрагмента в режиме треккинга оставляло «призрак»; теперь правка чистится полностью, история коллективной работы остаётся прозрачной.</li> </ul> <h3 id="архив-и-общение">Архив и общение</h3> <ul> <li data-num="1551"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Фильтр материалов через границу года</strong> — Archive Date Picker теперь принимает интервал «декабрь—январь», а не падает на смене года. Архив Дискурса стал доступнее на стыке лет.</li> <li data-num="1552"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Маскированный email не утекает в инвайт</strong> — поиск пользователей возвращает email с маской ради приватности; теперь сервер игнорирует эту маску, если ID собеседника известен, и приглашение уходит на реальный адрес. Никаких потерянных инвайтов из-за лишних звёздочек.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.137</span><span class="changelog-meta__date">19-04-2026</span></span><h2 id="v9137-19-04-2026--донаты-без-загадок-код-без-eol">Донаты без загадок, код без EOL</h2></summary> <div class="changelog-section__body"> <p>Донору — человечные ошибки вместо технического ребуса Stripe. Проекту — ноль открытых уязвимостей в зависимостях после прощания с застрявшим в 2021-м legacy-стеком.</p> <h3 id="поддержать-самиздат--с-комфортом">Поддержать самиздат — с комфортом</h3> <ul> <li data-num="1535"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Загадочные английские сообщения Stripe в прошлом</strong> — на месте технического «The string did not match the expected pattern» теперь двенадцать переведённых подсказок: «Проверьте адрес почты», «Платёжный сервис временно недоступен», «Слишком много попыток, подождите минуту». Донор сразу понимает что делать вместо того, чтобы закрыть форму.</li> <li data-num="1536"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Причина загадочных сбоев найдена</strong> — Stripe Checkout иногда падал не у нас, а на стороне читателя, когда блокировщик рекламы выключаÐ
1» его антифрод-скрипты. До сегодня мы этого не видели; теперь логи платформы прилетают вживую, и любому донору мы можем помочь точечно.</li> <li data-num="1537"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Английский интерфейс подхватывает сообщения автоматически</strong> — контракт между фронтом и платёжным сервисом перестроен на стабильные коды. EN-версия получает свои переводы без дубликатов в коде.</li> </ul> <h3 id="безопасность-18-→-0-открытых-уязвимостей">Безопасность: 18 → 0 открытых уязвимостей</h3> <ul> <li data-num="1538"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Восемнадцать Dependabot-алертов закрыты</strong> — четырнадцать в самом AngularJS (поддержка завершилась ещё в 2021-м, патчей не будет) задокументированы как принятый риск с перечнем активных защит. Остальные четыре закрыты удалением устаревших пакетов. Единая карта рисков живёт в <code>SECURITY-POSTURE.md</code> — точка правды для аудита и новых участников.</li> <li data-num="1539"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Санитайзер HTML переехал на DOMPurify v3</strong> — защита от XSS-атак обновлена с библиотеки, зависшей в 2021-м, на активно поддерживаемую. Ловит больше приёмов обхода — через SVG, data-URL и inline-обработчики событий. Шаблоны статей и комментариев не менялись, всё работает прозрачно.</li> <li data-num="1540"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Vimeo-встройка чуть крепче</strong> — путь отображения ошибок плеера переведён на безопасный рендер. Инъекция через потенциальный динамический текст ошибки невозможна даже гипотетически.</li> </ul> <h3 id="прощание-с-legacy">Прощание с legacy</h3> <ul> <li data-num="1541"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Нативный плеер браузера вместо Videogular</strong> — плеер 2015 года, тянувший за собой пять npm-пакетов и уязвимую версию Bootstrap, удалён. Видеофайлы в материалах и экспозициях теперь проигрывает собственный плеер браузера: быстрее, легче, без стороннего JS. YouTube и Vimeo в материалах работают как и работали.</li> <li data-num="1542"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Toast-уведомления переписаны своими силами</strong> — всплывающие сообщения («Сохранено», «Ошибка сети», «Черновик создан») переехали на 250 строк чистого кода: тот же интерфейс, те же анимации, та же доступность — но без XSS-уязвимости из 2017 года. Все пятьсот с лишним вызовов в проекте продолжают работать без единой правки.</li> <li data-num="1543"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Минус полторы тысячи строк legacy-кода</strong> — выметены vendor-скрипты, забытые шрифты, мёртвые CSS-селекторы и webpack-алиасы, указ
1ывавшие в пустоту. Сборка чище, PR-ревью короче, новичку читать кодовую базу проще.</li> </ul> <h3 id="наблюдаемость-1">Наблюдаемость</h3> <ul> <li data-num="1544"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Живые логи платёжного воркера</strong> — включён встроенный мониторинг Cloudflare. Платёжные ошибки теперь пишутся полностью, а не обрывком строки. Следующий неожиданный инцидент на форме пожертвований разберётся за минуты вместо часов догадок.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.136</span><span class="changelog-meta__date">17-04-2026</span></span><h2 id="v9136-17-04-2026--слайдшоу-в-редакторе-подписи-не-теряются-и-одна--на-всех">Слайдшоу в редакторе: подписи не теряются и одна — на всех</h2></summary> <div class="changelog-section__body"> <p>Мелкая, но обидная беда редактора галерей побеждена: подпись одного слайда больше не «съедает» соседей. И теперь одним кликом можно расставить одну и ту же подпись по всем слайдам — удобно, когда это серия кадров из фильма, цикл одного автора или один источник.</p> <ul> <li data-num="1532"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Подписи соседних слайдов не затираются</strong> — раньше при быстром добавлении подписей (особенно при соавторстве в реальном времени) подпись предыдущего слайда могла молча исчезнуть. Теперь каждая живёт независимо, соавтор за соседним слайдом ничего не ломает.</li> <li data-num="1533"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Одна подпись на всю галерею — один клик</strong> — в окне подписи появилась иконка-дубликат. Нажали, подтвердили — и все слайды получили одинаковый текст. Меньше рутинного копипаста, особенно при вставке 5–10 изображений разом.</li> <li data-num="1534"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Чище окно подписи</strong> — лишняя кнопка «Отменить» убрана: крестик, Escape и клик мимо всё равно закрывают окно. Больше места под сам текст, меньше визуального шума.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.135</span><span class="changelog-meta__date">17-04-2026</span></span><h2 id="v9135-17-04-2026--почта-дискурса-на-броне-автоочистка-шифрование-защита-от-подделок">Почта Дискурса на броне: автоочистка, шифрование, защита от подделок</h2></summary> <div class="changelog-section__body"> <p>Новый слой брони — теперь у почты. Мёртвые адреса сами исчезают из списков, жалобы на спам тут же отписывают, письма к получателям идут по принудительно шифрованному каналу, а подделка «от @discours.io» становится шумно-заметной. Авторы получают надёжную доставку, читатели — чистый inbox.</p> <h3 id="рассылки-сами-заботятся-о-своём-здоровье">Рассылки сами заботятся о своём здоровье</h3> <ul> <li data-num="1525"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Мёртвые адреса исчезают автоматически</strong> — если письмо возвращается с пометкой «такого адреса не существует», адрес тут же помечается как неактивный и выключается из будущих рассылок. Никаких ручных чисток, и репутация отправителя не страдает от накопленных отказов.</li> <li data-num="1526"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Жалобы на спам — мгновенная отписка</strong> — читатель нажал «в спам» в Gmail или Outlook? Адрес сразу выключается. Уважение к читателю, защита доставки для всех остальных.</li> <li data-num="1527"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Живая картина доставки</strong> — каждое отправленное письмо, каждый отказ и каждая жалоба фиксируются в базе платформы в реальном времени. Видно, куда реально доходят рассылки и что происходит с каждым адресом.</li> </ul> <h3 id="письма-под-защитой">Письма под защитой</h3> <ul> <li data-num="1528"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Принудительное шифрование на пути до получателя</strong> — Gmail, Outlook, Yandex и другие серверы проверяют нашу MTA-STS-политику и обязаны использовать TLS при доставке писем от Дискурса. Перехват содержимого недоступен даже при атаке на провайдера.</li> <li data-num="1529"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Мониторинг проблем с шифрованием</strong> — если чей-то сервер не смог установить защищённый канал с нами, узнаём об этом в отчёте. Видимость там, где раньше была слепая зона.</li> <li data-num="1530"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Усиленная защита от подделки писем</strong> — правила DMARC расширены: поддомены под той же защитой, алгоритмы выравнивания зафиксированы явно, подозрительные провалы шлют детальные отчёты. Попытка спуфиÐ
1½Ð³Ð° от @discours.io становится шумной и логируется.</li> </ul> <h3 id="чистка">Чистка</h3> <ul> <li data-num="1531"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Удалена забытая DNS-запись MailChimp</strong> — след давно закрытого эксперимента. Минус одна запись в DNS, минус одна точка уязвимости, яснее картина почтовой инфраструктуры.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.134</span><span class="changelog-meta__date">16-04-2026</span></span><h2 id="v9134-16-04-2026--открытый-код-для-соучастия-гайд-шаблоны-манифест-разработки">Открытый код для соучастия: гайд, шаблоны, манифест разработки</h2></summary> <div class="changelog-section__body"> <p>Дискурс всегда был открытым проектом — теперь и код тоже. Переписали README, добавили манифест разработки, гайд для контрибьюторов, кодекс сообщества и шаблоны issue/PR. Новый разработчик — от джуна до желающего исправить опечатку — может присоединиться за час, без вопроса «с чего начать».</p> <h3 id="витрина-проекта">Витрина проекта</h3> <ul> <li data-num="1517"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>README с двумя вкладками и манифестом</strong> — русский и английский варианты, историческая обложка-коллаж, бейджи CI и статуса, ссылки на живой сайт. Репозиторий теперь рассказывает о себе первой же страницей.</li> <li data-num="1518"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Манифест разработки на GitHub</strong> — девять принципов, по которым мы строим код: открытость, равенство, без цензуры, без привязки к вендорам. В пункте 8 — прямая ссылка на edit-форму: любой может предложить правки манифеста одним кликом.</li> </ul> <h3 id="путь-для-нового-разработчика">Путь для нового разработчика</h3> <ul> <li data-num="1519"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Гайд для контрибьюторов</strong> — CONTRIBUTING.md на двух языках: как запустить локально, какой стиль кода, как оформить Pull Request. «С чего начать» больше не вопрос.</li> <li data-num="1520"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кодекс сообщества</strong> — Contributor Covenant 2.1 (ru/en), контакты через Telegram-бот. Уважение и конструктив закреплены правилом — для всех, кто пишет код, делает ревью или отвечает на issue.</li> <li data-num="1521"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Шаблоны issue и PR</strong> — «Нашли ошибку», «Идея», готовый PR-чеклист. Сообщить о баге или предложить фичу — за минуту, без вопроса «как правильно оформить».</li> <li data-num="1522"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопка Sponsor в шапке репо</strong> — Patreon и donate-страница Discours.io. Поддержать проект финансово можно прямо из GitHub в один клик.</li> <li data-num="1523"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>LICENSE (ISC) и обложка для шеринга</strong> — лицензия автоматически распознана GitHub, снимает вопрос «можно ли форкнуть». Превью-картинка делает ссылку на репо узнаваемой в Telegram и Twitter.</li> </ul> <h3 id="починка">Починка</h3> <ul> <li data-num="1524"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Censor Shield drift-чек снова зелёный</strong> — баг в GitHub Actions workflow (<code>grep -c || echo &quot;0&quot;</code> ломал формат <code>$GITHUB_OUTPUT</code> и ронял job за 10 секунд) устранён. Еженедельная проверка, что список госцензорских ASN/CIDR не разошёлся с upstream, снова работает.
1</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.133</span><span class="changelog-meta__date">16-04-2026</span></span><h2 id="v9133-16-04-2026--переезд-на-github-быстрее-надёжнее-удобнее-для-соучастия">Переезд на GitHub: быстрее, надёжнее, удобнее для соучастия</h2></summary> <div class="changelog-section__body"> <p>Дискурс полностью переехал с Bitbucket на GitHub. Это не просто смена адреса: каждое изменение в коде теперь проходит через автоматические проверки качества, AI-ревью и визуальное тестирование. Баги ловятся до того, как попадут на сайт.</p> <h3 id="качество-кода--на-автопилоте">Качество кода — на автопилоте</h3> <ul> <li data-num="1509"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>AI-ревьюер читает каждый PR</strong> — CodeRabbit подключён к репозиторию и автоматически проверяет изменения: ищет баги, утечки памяти, XSS-уязвимости и нарушения паттернов AngularJS. Как второй разработчик, который никогда не устаёт и не пропускает мелочей.</li> <li data-num="1510"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Линтер и тесты теперь блокируют PR</strong> — раньше ошибки линтера и падающие тесты показывались, но не мешали мержить. Теперь — мешают. Сломанный код не попадёт в production.</li> <li data-num="1511"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Визуальная регрессия с самовосстановлением</strong> — Playwright делает скриншоты ключевых страниц и сравнивает с эталоном. Если эталона для новой среды нет — генерирует автоматически и коммитит сам. Ни одно визуальное изменение не проскочит незамеченным.</li> </ul> <h3 id="storybook-снова-работает">Storybook снова работает</h3> <ul> <li data-num="1512"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Storybook собирается на Netlify</strong> — после переезда Storybook падал: Vite (в отличие от webpack) не умеет резолвить модули из <code>resolve.modules</code> и ругается на SCSS-комментарии в стиле <code>//</code>. Добавлены 6 explicit aliases и починен синтаксис.</li> <li data-num="1513"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>SCSS-импорты синхронизированы</strong> — три файла с миксинами (<code>embed-providers</code>, <code>z-index</code>, <code>link-card</code>) были в main.scss, но не в storybook-styles.scss. Накопившийся drift — починен. Дизайн-система в Storybook теперь отображается корректно.</li> </ul> <h3 id="инфраструктура-и-безопасность">Инфраструктура и безопасность</h3> <ul> <li data-num="1514"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопка «Пересобрать сайт»</strong> — новый workflow в GitHub Actions. Один клик — и Netlify пересобирает сайт без нового коммита. Полезно, когда обновился контент на бэкенде или нужно сбросить кэш.</li> <li data-num="1515"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Убран утёкший API-ключ</strong> — hardcoded ключ ipwhois.pro в Netlify-функции <code>geo-service</code> заменён заглушкой (сервис больше не используется). Один из четырёх критических findings закрыт.</li> <li data-num="1516"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>BB-репозитории отключены</strong>
1 — <code>origin</code> теперь указывает на GitHub для всех четырёх проектов Дискурса. Bitbucket remotes удалены, двойной push больше не нужен.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.132</span><span class="changelog-meta__date">15-04-2026</span></span><h2 id="v9132-15-04-2026--цензурный-щит-сайт-невидим-для-госцензоров">Цензурный щит: сайт невидим для госцензоров</h2></summary> <div class="changelog-section__body"> <p>Если цензор не может открыть страницу — он не может составить акт и инициировать блокировку. Дискурс теперь определяет запросы с 2013 государственных автономных систем РФ и 768 IP-диапазонов — и отвечает им статьёй 29 Конституции вместо контента. Щит работает на всех уровнях: основной сайт, зеркало, API и GAE-прокси.</p> <h3 id="невидимость-для-цензора">Невидимость для цензора</h3> <ul> <li data-num="1495"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Двуслойная детекция госсетей</strong> — Cloudflare Worker проверяет ASN каждого запроса за O(1), а CIDR-бинарный поиск за O(log n) ловит госструктуры, сидящие в коммерческих AS. РКН, ФСБ, МВД, Генпрокуратура, ФНС, Следком — 2013 AS-номеров из ежедневно обновляемой базы RIPE.</li> <li data-num="1496"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Хамелеон вместо единого ответа</strong> — 60% запросов получают 451 с цитатой Конституции, 15% — Internal Server Error, 15% — Service Unavailable, 10% — молчаливый обрыв. Автоматический сканер цензора не может отличить блокировку от поломки.</li> <li data-num="1497"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Tarpit выжигает время сканера</strong> — каждый запрос цензора задерживается на 5–15 секунд. Не тратит ресурсы сервера (async wait), но парализует автоматическое сканирование. При DDoS-попытке — мгновенный ответ без задержки (cap 50 одновременных).</li> <li data-num="1498"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Самоуничтожение 451-страницы</strong> — <code>&lt;meta http-equiv=&quot;refresh&quot;&gt;</code> сменяет страницу на пустую через 1 секунду. Headless-браузер цензора не успеет сделать скриншот.</li> </ul> <h3 id="мониторинг-и-реагирование">Мониторинг и реагирование</h3> <ul> <li data-num="1499"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Дашборд «Цензурный щит»</strong> — новая карточка в <code>/_/dashboard</code>: заблокировано всего, по ASN, по CIDR, уникальные IP, топ-10 ASN госведомств, последние 20 блокировок с IP/UA/path. Обновляется в реальном времени.</li> <li data-num="1500"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Алерты при сканировании</strong> — 100+ блокировок за жизнь isolate → жёлтое предупреждение, 500+ → красный алерт на дашборде. Видно сразу без Grafana.</li> <li data-num="1501"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Webhook в Slack/Telegram</strong> — env <code>CENSOR_ALERT_WEBHOOK_URL</code> → JSON-уведомление при каждых 10 блокировках. Дедупликация (не чаще раза в 5 мин), SSRF-защита, timeout.</li> <li data-num="1502"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Prometheus-метрики</strong>
1 — <code>worker_censor_blocked_total</code>, <code>worker_censor_blocked_asn_total</code>, <code>worker_censor_blocked_cidr_total</code>, <code>worker_censor_unique_ips</code>, <code>worker_censor_alerts_sent_total</code>.</li> </ul> <h3 id="зеркала-и-инфраструктура">Зеркала и инфраструктура</h3> <ul> <li data-num="1503"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>API-зеркало тоже защищено</strong> — <code>api.dscrs.site</code> проверяет и ASN, и CIDR (37 крупных госдиапазонов). Отвечает 451 JSON с tarpit.</li> <li data-num="1504"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>GAE-зеркала отдают 451</strong> — Go-прокси на App Engine получил <code>isCensorIP()</code> → полноценная 451-страница с Конституцией вместо голого 429.</li> <li data-num="1505"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>JS-файлы на зеркалах больше не ломаются</strong> — убрана попытка directory listing (Netlify его не поддерживает), вместо этого прямой проксинг. При обновлении бандла — автоматический сброс кэша и redirect.</li> <li data-num="1506"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Бот-зеркальщик обновляется при каждом деплое</strong> — entrypoint.sh теперь всегда копирует свежие main.js и create-mirror.py из Docker-образа, сохраняя venv и node_modules.</li> <li data-num="1507"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>GCP Cloud Build не падает на новых проектах</strong> — добавлен шаг <code>grant-storage-access</code> перед деплоем (roles/storage.admin для App Engine SA), требуемый Google с 2025 года.</li> <li data-num="1508"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Python-зависимости найдены</strong> — create-mirror.py теперь запускается через venv python (где установлен <code>requests</code>), а не через системный.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.131</span><span class="changelog-meta__date">14-04-2026</span></span><h2 id="v9131-14-04-2026--броня-для-платформы-защита-от-атак-ботов-и-блокировок">Броня для платформы: защита от атак, ботов и блокировок</h2></summary> <div class="changelog-section__body"> <p>Дискурс получил многослойную защиту. IP-адрес API-сервера теперь скрыт — ни цензоры, ни сканеры уязвимостей его не видят. Каждый запрос проходит через WAF-фильтры, AI-краулеры получают бесконечный лабиринт вместо контента, а зеркала продолжают работать без перебоев.</p> <h3 id="защита-от-атак">Защита от атак</h3> <ul> <li data-num="1484"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>WAF фильтрует каждый запрос</strong> — на обоих доменах (discours.io и dscrs.site) включены Cloudflare Managed Ruleset и OWASP Core Ruleset. SQL-инъекции, XSS, попытки удалённого выполнения кода — всё отсекается до того, как дойдёт до сервера. Данные авторов и читателей под защитой.</li> <li data-num="1485"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Боты под контролем</strong> — Super Bot Fight Mode активен: сканеры, спам-боты и краулеры отсеиваются на уровне Cloudflare. Поисковики и обычные читатели проходят свободно.</li> </ul> <h3 id="контент-авторов--не-корм-для-нейросетей">Контент авторов — не корм для нейросетей</h3> <ul> <li data-num="1486"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>AI-скрейперы заблокированы</strong> — ChatGPT, Claude, Perplexity и другие AI-краулеры больше не забирают тексты авторов для обучения моделей. Ваши материалы принадлежат вам.</li> <li data-num="1487"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>AI Labyrinth для настырных</strong> — если AI-бот пытается обойти блокировку, Cloudflare подсовывает ему бесконечные фейковые страницы, тратя его ресурсы впустую. Вместо ваших статей он получает лабиринт из ничего.</li> </ul> <h3 id="невидимый-сервер--сильная-защита-от-блокировок">Невидимый сервер — сильная защита от блокировок</h3> <ul> <li data-num="1488"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>IP-адрес сервера скрыт</strong>
1 — <code>api.discours.io</code> теперь за Cloudflare Proxy. Настоящий IP невозможно узнать через DNS — <code>nslookup</code> покажет только адреса Cloudflare. Заблокировать Дискурс стало на порядок сложнее: нужно блокировать весь Cloudflare, а это пол-интернета.</li> <li data-num="1489"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Шифрование без компромиссов</strong> — Full Strict SSL + принудительный HTTPS + HSTS (6 месяцев). Соединение между читателем и сервером невозможно перехватить или подменить. Точка.</li> </ul> <h3 id="свои-сервисы--не-боты">Свои сервисы — не боты</h3> <p>Bot Fight Mode блокирует любой трафик с IP датацентров — а наши сервисы живут именно там. Провели полный аудит 10+ сервисов и сделали так, чтобы защита не мешала своим.</p> <ul> <li data-num="1490"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Зеркала на AppEngine проходят свободно</strong> — WAF Skip-правило по User-Agent пропускает все appspot-зеркала мимо бот-фильтров. Новый адрес зеркала заработает сразу — без ручной настройки.</li> <li data-num="1491"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Astro-фронтенд и коллаб-сервер не блокируются</strong> — Publy, HocusPocus и VPN-бот на Fly.io получили Skip-правило по ASN. Редактор, коллаборативное редактирование и синхронизация подписок работают без перебоев.</li> <li data-num="1492"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Зеркало передаёт реальный IP читателя</strong> — mirror worker пробрасывает <code>CF-Connecting-IP</code> в <code>X-Forwarded-For</code>. Rate limiting на бэкенде считает запросы по-честному, а не валит всё на один IP зеркала.</li> <li data-num="1493"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Сквозной токен для серверных запросов</strong> — Netlify-функции (sitemap, LCP-preload) теперь отправляют секретный заголовок <code>X-Discours-Internal</code>, и WAF пропускает их без вопросов. Любой новый сервис — просто добавляет заголовок и работает. Никаких новых правил в Dashboard.</li> </ul> <h3 id="почтовая-защита">Почтовая защита</h3> <ul> <li data-num="1494"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>DMARC для зеркального домена</strong> — dscrs.site получил DMARC-запись. Почтовые серверы проверяют, что письма от имени зеркала действительно отправлены нами. Фишинг и спуфинг с нашего домена — невозможен.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.130</span><span class="changelog-meta__date">14-04-2026</span></span><h2 id="v9130-14-04-2026--новая-«умная-дверь»-для-обхода-блокировок-и-обновление-старого-зеркала">Новая «умная дверь» для обхода блокировок и обновление старого зеркала</h2></summary> <div class="changelog-section__body"> <h3 id="зеркало">Зеркало</h3> <ul> <li data-num="1477"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Зеркало снова создаётся</strong> — Google прекратила поддержку Go 1.21 на Ap
1p Engine, и все 10 попыток создания зеркала молча падали на деплое. Обновили runtime до Go 1.22 — зеркала создаются за 2 минуты, как раньше.</li> <li data-num="1478"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Деплой бота — одной кнопкой</strong> — настроен GitHub Actions: пуш в <code>main</code> автоматически пересобирает Docker-образ и деплоит на сервер через Dokku. Больше не нужно заходить на сервер вручную.</li> <li data-num="1479"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Docker не ломается при сборке с Windows</strong> — <code>.gitattributes</code> гарантирует Unix-переносы строк в shell-скриптах. Dockerfile дополнительно чистит <code>\\r</code> — деплой работает с любой ОС.</li> <li data-num="1480"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Зеркало передаёт реальный IP</strong> — mirror API worker теперь пробрасывает <code>CF-Connecting-IP</code> в <code>X-Forwarded-For</code> при проксировании на бэкенд. Rate limiting и аналитика видят настоящего читателя, а не IP зеркала.</li> </ul> <h3 id="discours-gate--умная-точка-входа">Discours Gate — умная точка входа</h3> <ul> <li data-num="1481"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Страница-шлюз для читателей из-за блокировки</strong> — открываешь ссылку, видишь логотип Дискурса и спиннер. За 1-2 секунды страница проверяет, доступен ли сайт, и незаметно перенаправляет — на <code>discours.io</code> или зеркало. Никаких инструкций, VPN-ов, телеграм-ботов — просто работает.</li> <li data-num="1482"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Зеркало обновляется само</strong> — при создании нового зеркала через Telegram-бота <code>config.json</code> в GCS-бакете обновляется автоматически. Шлюз всегда знает актуальный адрес.</li> <li data-num="1483"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Fallback для крайних случаев</strong> — если и проверка, и конфиг не загрузились (нет сети, всё заблокировано) — через 5 секунд появятся две кнопки: «Открыть discours.io» и «Открыть зеркало». Страница работает даже без JavaScript.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.129</span><span class="changelog-meta__date">14-04-2026</span></span><h2 id="v9129-14-04-2026--английский-интерфейс-заглушка-с-характером-редактор-без-горизонтального-скролла">Английский интерфейс, заглушка с характером, редактор без горизонтального скролла</h2></summary> <div class="changelog-section__body"> <p>Дискурс заговорил по-английски. Не весь — но шапка, подвал, герой-баннер, форма доната, модалка логина и лоадер страниц теперь переключаются на EN при переходе на <code>/en/…</code>. А заглушка неопубликованных материалов превратилась из невзрачного текста в тёмную карточку, которая приглашает, а не отталкивает.</p> <h3 id="мультиязычность">Мультиязычность</h3> <ul> <li data-num="1468"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Дискурс теперь и по-английски</strong> — переключатель RU/EN в подвале теперь системно меняет интерфейс проекта. Шапка, подвал, герой-баннер, профиль-меню, модалка авторизации, форма доната и экран загрузки — всё переведено. Англоязычный читатель наконец видит <code>Sign In</code>, а не <code>Войти</code>.</li> <li data-num="1469"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>EN-режим запоминается в сессии</strong> — перешли на <code>/en/tags</code>, потом кликнули на ссылку без <code>/en/</code> — localeService поймёт, что вы в EN-режиме, и перенаправит обратно на <code>/en/tags</code>. Никаких внезапных скачков между языками.</li> <li data-num="1470"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Слоганы загрузки — на языке читателя</strong> — пока страница грузится, EN-пользователь увидит «An open journal on culture, science and society» вместо «Открытый журнал о культуре, науке и обществе». Десять EN-слоганов, десять RU — каждый раз новый.</li> <li data-num="1471"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Worker больше не блокирует <code>/en/</code></strong> — Cloudflare Worker раньше ставил 301-редирект с <code>/en/…</code> на <code>/…</code>
1, уничтожая любой шанс на EN-версию. Теперь пропускает — Angular-роутинг разбирается сам.</li> </ul> <h3 id="заглушка-неопубликованных-материалов">Заглушка неопубликованных материалов</h3> <ul> <li data-num="1472"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тёмная карточка вместо скучной заглушки</strong> — «Продолжение доступно участникам» теперь на чёрном фоне (#111) с белым текстом и декоративной <code>✦</code> виньеткой. Текст затухает градиентом, а ниже — элегантная тёмная карточка с мягкой entrance-анимацией. Стоп-сигнал, который хочется нажать, а не проскроллить.</li> <li data-num="1473"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка «Войти» больше не чёрная на синем</strong> — CSS Cascade Layers баг: <code>button { color: inherit }</code> (unlayered) побеждал <code>ds-btn-primary { color: #fff }</code> (@layer). Системный фикс в normalize — теперь все <code>button.ds-btn-primary</code> получают белый текст. Затронуто 5 кнопок на разных страницах.</li> </ul> <h3 id="редактор-2">Редактор</h3> <ul> <li data-num="1474"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Длинные слова не раздувают редактор</strong> — <code>overflow-wrap: anywhere</code> в ProseMirror-контенте: немецкие композиты, URL без пробелов, японский текст — всё переносится, горизонтальный скролл не появляется.</li> <li data-num="1475"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Горизонтальный скролл — на всех экранах, не только мобильных</strong> — <code>overflow-x: hidden</code> убран из <code>@media mobile</code> и применён глобально к контейнеру редактора. Длинный тулбар больше не расширяет страницу и на планшетах.</li> </ul> <h3 id="мелочи">Мелочи</h3> <ul> <li data-num="1476"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Боковые баннеры не обрезаются</strong> — последний баннер в сайдбаре статьи мог исчезать: <code>bottom: 0</code> обрезал его ровно по контейнеру. Теперь <code>bottom: -500px</code> + <code>overflow: clip</code> — видно всё, лишнего не вылезет.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.128</span><span class="changelog-meta__date">05-04-2026</span></span><h2 id="v9128-05-04-2026--зеркала-работают-сообщения-удобнее-теги-с-rss">Зеркала работают, сообщения удобнее, теги с RSS</h2></summary> <div class="changelog-section__body"> <p>Главное в этом релизе — Дискурс теперь полноценно работает на зеркалах. Картинки, обложки, аватарки — всё загружается через незаблокированный домен, причём основной сайт об этом даже не знает. А ещё — личные сообщения стали удобнее, страницы тегов получили RSS-кнопку и OG-картинку.</p> <h3 id="доступность-с-зеркал">Доступность с зеркал</h3> <ul> <li data-num="1457"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Картинки на зеркалах — больше не пустые квадраты</strong> — при открытии Дискурса через зеркало все изображения автоматически загружаются с незаблокированного CDN. Основной сайт продолжает работать как раньше — подмена происходит только там, где нужно.</li> <li data-num="1458"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>LCP-картинка на зеркале грузится мгновенно</strong> — inline-скрипт в <code>&lt;head&gt;</code> определяет домен ассетов до загрузки любого другого кода. Preload-подсказки в Netlify Edge Functions и Cloudflare Worker тоже адаптируются к зеркалу.</li> <li data-num="1459"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Расшаривание с зеркала — с правильными превью</strong> — OG-теги, JSON-LD, Twitter Card — везде ссылки на доступные картинки. Поделились статьёй в Telegram с зеркала? Превью покажется.</li> <li data-num="1460"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Вставленные картинки не перезаливаются зря</strong> — редактор знает оба домена ассетов и не пытается перезалить на S3 изображение, которое и так лежит на нашем CDN.</li> <li data-num="1461"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Fancybox и адаптивный srcset работают на зеркале</strong> — директива postImages теперь распознаёт изображения с обоих доменов. Кликнул на картинку в статье — откроется в полном размере, как и на основном сайте.</li> </ul> <h3 id="сообщения">Сообщения</h3> <ul> <li data-num="1462"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Нет доступа к чужому диалогу — понятный экран</strong> — вместо ошибки или пустоты покажется страница с иконкой замка, объяснением и кнопкой «К сообщениям». Никаких загадок.</li> <li data-num="1463"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Действия с сообщением — по тапу</strong> — на мобильных длинный тап по сообщению открывает popup-меню (копировать, ответить, удалить). Работает даже без мыши.</li> <li data-num="1464"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Подсветка нового сообщения</strong> — при получении нового сообщения оно кратко подсвечивается мягким фоном, чтобы глаз сразу нашёл его в потоке.</li> </ul> <h3 id="страницы-тегов">Страницы тегов</h3> <ul> <li data-num="1465"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>RSS прямо у счётчика</strong> — иконка RSS переехала вплотную к числу материалов в заголовке тега. Раньше стояла отдельно и терялась — теперь видна сразу и логически привязана к содержимому.</li> <li data-num="1466"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>OG-картинка для страниц тегов</strong> — при шеринге ссылки на тег в соцсетях теперь отображается логотип Дискурса вместо пустого превью.</li> <li data-num="1467"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Админская ссылка работает через proxy</strong> — кнопка «Открыть в админке» на страницах тегов больше не ломается, если API работает через прокси.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.127</span><span class="changelog-meta__date">05-04-2026</span></span><h2 id="v9127-05-04-2026--уведомления-чище-компактнее-живее">Уведомления: чище, компактнее, живее</h2></summary> <div class="changelog-section__body"> <p>Страница уведомлений и всплывающие тосты прошли через серьёзную переработку. Меньше визуального шума, больше полезной информации — и всё это выглядит так, будто было задумано с самого начала.</p> <h3 id="страница-уведомлений">Страница уведомлений</h3> <ul> <li data-num="1447"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Аватарки переехали к именам</strong> — громоздкий кружок слева убран. Вместо него — аккуратные мини-аватарки (20px) прямо рядом с именем автора. Несколько участников? До трёх аватарок с мягким перекрытием, как в мессенджерах.</li> <li data-num="1448"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Материал — ссылка, а не карточка</strong> — заголовок публикации теперь стоит прямо в тексте уведомления как «Название» со ссылкой. Никаких вложенных карточек — чистый, читаемый поток.</li> <li data-num="1449"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мёртвые уведомления исчезают</strong> — если комментарий, ремарка или приглашение удалены — уведомление о них тоже пропадёт. Лента больше не засоряется призраками.</li> <li data-num="1450"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Горячие клавиши — не загадка</strong> — иконка клавиатуры рядом с заголовком теперь работает: при наведении показывает подсказку (j/k — навигация, x — прочитать, Delete — удалить). Раньше просто стояла молча.</li> <li data-num="1451"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Цифры в бейджах вкладок — ровно по центру</strong> — переход на flexbox-центрирование убрал микросдвиг влево, который был заметен на крупных экранах.</li> </ul> <h3 id="всплывающие-уведомления-тосты">Всплывающие уведомления (тосты)</h3> <ul> <li data-num="1452"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Компактные, как конфеты</strong> — тосты теперь обтекают текст, а не растягиваются на фиксированную ширину. Короткое «Опубликовано» не тащит за собой 200 пикселей пустоты.</li> <li data-num="1453"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильные тосты — скруглённые и с воздухом</strong> — прямоугольные полоски от края до края заменены на аккуратные скруглённые карточки (14px) с отступами от краёв (12px) и центрированным текстом.</li> <li data-num="1454"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Иконки в потоке текста</strong> — галочка, крестик, треугольник теперь стоят перед первым словом сообщения как его часть, а не висят отдельно в пустоте слева. Компактнее, естественнее.</li> <li data-num="1455"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Текст не оставляет сирот</strong>
1 — <code>text-wrap: balance</code> равномерно распределяет слова по строкам. Одинокое слово на последней строке? Такого больше не будет.</li> <li data-num="1456">[UI] <strong>Мягче, деликатнее</strong> — скругление 12→16px, тени легче, бордеры тоньше и тонированные в цвет типа, фоны чуть теплее. Нижняя цветная полоска убрана — тип и так считывается по иконке и фону.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.126</span><span class="changelog-meta__date">04-04-2026</span></span><h2 id="v9126-04-04-2026--сообщения-удобное-создание-групповых-чатов">Сообщения: удобное создание групповых чатов</h2></summary> <div class="changelog-section__body"> <p>Кнопка «Новая группа» на странице сообщений теперь стоит рядом с заголовком, как в классных мессенджерах, а весь флоу создания чатов стал приятнее.</p> <ul> <li data-num="1441"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Новая группа» переехала в заголовок</strong> — кнопка теперь справа от «Сообщения», ровно там, где её ищет глаз. Поиск остался отдельно — каждый элемент на своём месте.</li> <li data-num="1442"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>На телефоне — компактная иконка</strong> — текст скрывается на экранах до 767px, остаётся аккуратная круглая кнопка с иконкой. Достаточно крупная, чтобы попасть пальцем.</li> <li data-num="1443"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Клавиатурный фокус виден везде</strong> — кнопка создания группы, крестик закрытия модалки и кнопка удаления участника получили <code>:focus-visible</code>. Кто работает с клавиатуры — больше не угадывает, где находится.</li> <li data-num="1444"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Крестик закрытия стал крупнее</strong> — padding увеличен вдвое. На мобильных попадёшь с первого касания.</li> <li data-num="1445"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Аватарки коллажа не сдвигают раскладку</strong> — у мини-аватарок в групповых диалогах появились <code>width</code>/<code>height</code> — браузер резервирует место до загрузки, страница не прыгает.</li> <li data-num="1446"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Модалка не вылезает за экран</strong> — на мобильных создание группы ограничено 80% высоты экрана со скроллом внутри. Больше никаких обрезанных кнопок.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.125.0</span><span class="changelog-meta__date">03-04-2026</span></span><h2 id="v91250-03-04-2026--собственные-рассылки-и-почтовая-инфраструктура">Собственные рассылки и почтовая инфраструктура</h2></summary> <div class="changelog-section__body"> <p>Дискурс больше не зависит от сторонних сервисов для отправки писем. Мы запустили собственный почтовой движок полностью на нашей инфраструктуре: от управления аудиторией до аналитики открытий. А в условиях блокировок это не просто удобство — это надёжный канал связи с читателями, который никто не отключит.</p> <ul> <li data-num="1430"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Письма уходят через Amazon SES — отдельно от системных</strong> — рассылки идут через выделенный SMTP-канал, а уведомления о комментариях и сбросе пароля — через прежний Mailgun. Одно не ломает другое.</li> <li data-num="1431"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Тестовая отправка прошла все проверки</strong> — SPF, DKIM, DMARC = PASS. Письма доходят в Inbox (не спам) на Gmail, Яндекс и Mail.ru — три главных почтовика аудитории.</li> <li data-num="1432"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Фирменный HTML-шаблон</strong> — карточки публикаций с обложками, кнопка CTA, соцсети, адаптивная вёрстка для мобильных. Весит 19 КБ (Gmail обрезает после 102 КБ) — огромный запас.</li> <li data-num="1433"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Статус подписчика обновляется автоматически</strong> — письмо доставлено → <code>deliverable</code>, хард-баунс → <code>bounced</code>, жалоба на спам → <code>complained</code>. При warm-up первый батч — только проверенные адреса.</li> <li data-num="1434"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>SES Webhook</strong> — Amazon SES сообщает о баунсах и жалобах в реальном времени, а не по SMTP-кодам постфактум. Проблемный адрес блокируется мгновенно — до того, как успеет испортить репутацию домена.</li> <li data-num="1435"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Отписка работает всегда</strong> — даже если список рассылки удалён после отправки, ссылка в письме приведёт на страницу управления подписками. CAN-SPAM и GDPR требуют — Дискурс выполняет.</li> <li data-num="1436"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>One-click unsubscribe</strong> — Gmail показывает кнопку «Отписаться» прямо в шапке письма (RFC 8058). Один клик — и готово, без лишних страниц.</li> <li data-num="1437"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Дашборд кампаний</strong> — open rate, click rate, bounce rate для каждой отправленной кампании + средние показатели по всем. Одна кнопка — и видно, что работает, а что нет.</li> <li data-num="1438"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Трекинг открытий и кликов</strong> — пиксель отслеживает открытия (с защитой от ботов), ссылки — переходы. Unique open / unique click считаются корректно, без дубликатов.</li> <li data-num="1439"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>DNS: SPF + DKIM + DMARC</strong> — домен <code>discours.io</code> прошёл полную email-аутентификацию. Письма подписаны криптографически, подделать отправителя невозможно.</li> <li data-num="1440"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Domain throttling</strong> — Gmail, Яндекс, Mail.ru получают письма с контролируемой скоростью. Circuit breaker останавливает отправку после 10 подряд неудачных батчей — защита от блоклиста.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.124.0</span><span class="changelog-meta__date">01-04-2026</span></span><h2 id="v91240-01-04-2026--discours-connect-импорт-из-google-docs-и-множество-других-улучшений">Discours Connect, импорт из Google Docs и множество других улучшений</h2></summary> <div class="changelog-section__body"> <p>На платформе появился полноценный VPN-портал — от лендинга до выдачи ключа и поддержки. Редактор научился импортировать Google Docs со сносками. А под капотом — десятки фиксов, которые делают Дискурс отзывчивее, Ð
1½Ð°Ð´Ñ‘жнее и приятнее в мелочах.</p> <h3 id="discours-connect--vpn-портал">Discours Connect — VPN-портал</h3> <ul> <li data-num="1405"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Собственный VPN-сервис — прямо на платформе</strong> — Discours Connect работает на connect.discours.io: лендинг с описанием, капча, выдача ключа, QR-код для мобильных, кнопка «Открыть в приложении» (deep-link), восстановление ключа и поддержка — всё без перехода на внешние сервисы.</li> <li data-num="1406"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Пошаговый визард</strong> — получение ключа разбито на понятные шаги с заголовками и прогресс-баром. Каждый экран — одно действие, никакой перегрузки. Ошибка на капче? Вопрос автоматически перегенерируется.</li> <li data-num="1407"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Лендинг VPN, доступеый ао ссылке прямо из России</strong> — герой-секция со счётчиками, отзывы пользователей с каруселью, FAQ по категориям, инструкции по настройке, блок поддержки — собранные на единой странице с якорной навигацией.</li> <li data-num="1408"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Модалки с focus-trap и клавиатурным управлением</strong> — результат, поддержка, конфетти при успехе — все модальные окна ловят фокус, закрываются по Escape и возвращают его обратно.</li> <li data-num="1409"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Чувствительные параметры не утекут в редирект</strong> — при перенаправлении connect.discours.io → discours.io/connect из URL вырезаются token, email, password, recovery_code и ещё 6 ключей.</li> </ul> <h3 id="редактор-3">Редактор</h3> <ul> <li data-num="1410"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Импорт из Google Docs</strong> — вставляете публичную ссылку, нажимаете «Импорт» — текст со всей разметкой уже в редакторе. Сноски из GDocs автоматически трансформируются в наши footnote-tooltip.</li> <li data-num="1411"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Создание черновика не тормозит</strong> — автосоздание черновика при открытии редактора теперь идёт с retry и таймаутом. Если сеть медленная — повторная попытка через 1,5 с; если сервер не отвечает 20 с — тихий переход в локальный режим, ничего не зависнет.</li> <li data-num="1412"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Читайте также» стал <code>&lt;nav&gt;</code></strong> — блок рекомендаций получил семантическую разметку и <code>aria-label</code>. Screen reader объявляет его как навигацию, а не как безымянный <code>&lt;div&gt;</code>.</li> <li data-num="1413"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Поиск в «Читайте также» не зависнет</strong>
1 — добавлен 5-секундный таймаут, per-slot abort-контроллеры (поиск в одном слоте не отменяет загрузку в другом) и общий abort при закрытии диалога.</li> <li data-num="1414"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Опасные URL в «Читайте также» отфильтрованы</strong> — <code>javascript:</code>, <code>data:</code> и другие опасные ссылки не пройдут в блок рекомендаций.</li> <li data-num="1415"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Если сноски не обработались — узнаете</strong> — при импорте HTML/DOCX, если трансформация сносок упала, появится предупреждение. Текст сохранится как есть, ничего не потеряется.</li> </ul> <h3 id="стабильность-и-производительность">Стабильность и производительность</h3> <ul> <li data-num="1416"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ожидание Slick-карусели — без polling</strong> — раньше компонент проверял наличие плагина каждые 500 мс (до 10 раз). Теперь — MutationObserver, который реагирует мгновенно при загрузке скрипта и молчит, когда ничего не происходит.</li> <li data-num="1417"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Загрузка тегов — без гонок</strong> — при быстрой навигации между темами старый запрос мог перезаписать свежие данные. Теперь у каждого запроса уникальный ID — устаревший ответ отбрасывается.</li> <li data-num="1418"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>WebSocket — здоровее</strong> — health check Asteroid переведён на WebSocket-сервер вместо HTTP, тайм-аут увеличен. Реконнект стабильнее на медленных мобильных сетях.</li> <li data-num="1419"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Коллаборация не торопит safety-таймаут</strong> — лимит синхронизации Yjs увеличен с 5 до 10 секунд. Авторы с медленным интернетом дольше остаются на связи, прежде чем редактор решит «что-то пошло не так».</li> <li data-num="1420"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Sentry ловит больше, шумит меньше</strong> — новый <code>sentry.js</code> с настроенными фильтрами отсекает шум от WKWebView, Facebook In-App Browser и расширений, но пропускает все реальные ошибки.</li> </ul> <h3 id="uiux-полировка">UI/UX-полировка</h3> <ul> <li data-num="1421"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Уведомления различимы по типу</strong> — error и warning получили мягкие тёплые фоны вместо одинакового чёрного. Цвета не кричат, но тип сообщения считывается за долю секунды.</li> <li data-num="1422"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Карусель не дёргается при ресайзе</strong> — стрелки навигации в экспозиции плавно анимируются между мобильной и десктопной позицией вместо резкого скачка.</li> <li data-num="1423"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильная навигация Connect</strong> — на connect-лендинге в шапке появился горизонтально скроллящийся навбар с якорными ссылками. Активная секция подсвечивается при скролле, навбар автоматически прокручивается к ней.</li> <li data-num="1424"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авторская аватарка не мигает при 404</strong> — смена URL аватара теперь проверяет загрузку через test-Image, прежде чем сбросить ошибку. Если новый URL тоже битый — fallback остаётся.</li> <li data-num="1425"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>LoginModal — убран дубль focus-visible</strong> — кнопка показа пароля имела два конфликтующих правила фокуса. Осталось одно, чистое.</li> </ul> <h3 id="кодовая-база">Кодовая база</h3> <ul> <li data-num="1426"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Connect-контроллер — 1167 строк → 8 модулей</strong> — монолитный connectAccessCtrl разбит на sub-контроллеры: form-state, key-issuance, key-recovery, modals-accessibility, support-ticketing, ui-interactions, content-loading, utilities.</li> <li data-num="1427"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Domain-regex — один источник правды</strong> — проверка <code>connect.discours.io</code> вынесена в <code>connect-domain.js</code> вместо дублирования в 3 файлах.</li> <li data-num="1428"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Keyboard-handling в шаблонах</strong>
1 — <code>$event.preventDefault() || action()</code> заменён на <code>$event.preventDefault(); action()</code> — читабельнее, а поведение то же.</li> <li data-num="1429"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Тесты auto-draft</strong> — 225 строк покрывают retry-логику, классификацию ошибок и граничные случаи (destroyed scope, таймаут).</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.123.0</span><span class="changelog-meta__date">25-03-2026</span></span><h2 id="v91230-25-03-2026--спринт-по-фиксам-ускорение-загрузки-изображений-ясные-ошибки-и-улучшение-ux">Спринт по фиксам: ускорение загрузки изображений, ясные ошибки и улучшение UX</h2></summary> <div class="changelog-section__body"> <p>Починили десяток мест, где можно было сделать лучше: видео зависало без обратной связи, картинки не использовали свой потенциал, ошибки путали пользователей, а Firefox мог показывать вечный спиннер. Каждую замеченную шероховатость устранили — ноль новых фич, сто процентов шлифовки.</p> <ul> <li data-num="1395"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>YouTube API не загрузился? Видео всё равно запустится</strong> — если API не отвечает 8 секунд (блокировка, медленная сеть), плеер автоматически переключается на обычный iframe с autoplay. Раньше зритель видел чёрный прямоугольник и не понимал, что происходит.</li> <li data-num="1396"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Спиннер загрузки видео</strong> — между нажатием Play и появлением плеера теперь вращается белый индикатор. Пропадает, как только iframe готов. Для пользователей с чувствительностью к движению анимация отключена через <code>prefers-reduced-motion</code>.</li> <li data-num="1397"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Firefox больше не показывает вечный спиннер</strong> — вместо CSS-селектора <code>:has()</code>, который старые Firefox не понимают, видео-хелперы добавляют класс <code>.iframe-loaded</code> через JS. Работает во всех браузерах без исключений.</li> <li data-num="1398"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Один клик — один плеер, без вариантов</strong> — guard <code>didExecute</code> гарантирует, что callback создания плеера выполнится ровно один раз, даже если таймаут и API ready сработают одновременно. Никаких дублирующихся iframe.</li> <li data-num="1399"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Hero-изображение статьи грузится умнее</strong> — семь повторных вызовов фильтра в шаблоне заменены одним <code>imageSrcset:&#39;hero&#39;</code>. Браузер получает оптимальный размер картинки: 320px на телефоне, 1920px на Retina-мониторе — без лишнего трафика.</li> <li data-num="1400"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>SEO-счётчик подсказывает цветом</strong> — при редактировании заголовка и описания перед публикацией счётчик символов плавно меняет цвет: серый → жёлтый (приближаетесь к лимиту) → красный (почти всё). Больше не нужно считать в уме, сколько ещё можно написать.</li> <li data-num="1401"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ошибки сети и ошибки кода больше не путаются</strong> — раньше любой сбой показывал «Проверьте соединение», даже если дело было в баге на странице. Теперь определение точное: конкретные паттерны для Chrome, Firefox и Safari. Если проблема в коде — текст честно скажет «Что-то пошло Ð
1½Ðµ так».</li> <li data-num="1402"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>DNS-подготовка к видео</strong> — браузер заранее резолвит домены YouTube и Vimeo (<code>dns-prefetch</code>), пока читатель ещё не нажал Play. Первое воспроизведение стартует на 50–100 мс быстрее — незаметно, но приятно.</li> <li data-num="1403"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Видео-превью не грузятся заранее</strong> — thumbnail&#39;ы YouTube и Vimeo получили <code>loading=&quot;lazy&quot;</code>. Если видео ниже экрана — картинка подождёт, пока читатель до неё доскроллит.</li> <li data-num="1404"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Модалка публикации не упадёт молча</strong> — если Angular не сможет скомпилировать шаблон превью (сбой scope, битый HTML), ошибка попадёт в консоль, а публикация продолжится без модалки — а не просто зависнет.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.122.0</span><span class="changelog-meta__date">24-03-2026</span></span><h2 id="v91220-24-03-2026--красивые-обложки-у-видео-и-запуск-плейров-с-первого-клика">Красивые обложки у видео и запуск плейров с первого клика</h2></summary> <div class="changelog-section__body"> <p>Нажимаешь play на превью видео в экспозиции — и ничего не происходит. Приходится кликать второй раз, уже по плееру YouTube. Знакомо? Это был наш баг. Оказалось, Angular создавал iframe асинхронно, и Chrome терял контекст пользовательского жеста — autoplay блокировался. Мы переписали подход: теперь видео управляется через YouTube IFrame Player API с гарантированным программным запуском. Заодно привели в порядок всё, что касается видео на платформе.</p> <ul> <li data-num="1385"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Видео играет с первого клика</strong> — вместо ненадёжного <code>autoplay=1</code> в URL теперь используется YouTube IFrame Player API. По клику создаётся <code>YT.Player</code>, при готовности вызывается <code>playVideo()</code> — Chrome не может заблокировать то, что запущено самим API. Работает даже при первом визите на сайт.</li> <li data-num="1386"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Vimeo тоже стал ленивым</strong> — раньше Vimeo-iframe грузился сразу при открытии страницы (~1 МБ трафика на каждый embed). Теперь — thumbnail + кнопка play, iframe создаётся по клику с <code>autoplay=1</code>. Тот же lazy-паттерн, что и для YouTube.</li> <li data-num="1387"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Превью без чёрных полос</strong> — картинки-обложки видео теперь заполняют контейнер целиком (<code>object-fit: cover</code> вместо <code>height: auto</code>). Никаких чёрных полей сверху-снизу, даже если thumbnail не строго 16:9.</li> <li data-num="1388"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Плавное появление плеера</strong> — при нажатии play обложка исчезает, а iframe появл
1яется с лёгким fade-in (0,3 с). Нет резкого скачка.</li> <li data-num="1389"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Видео доступно с клавиатуры</strong> — обложки YouTube и Vimeo получили <code>role=&quot;button&quot;</code>, <code>tabindex=&quot;0&quot;</code>, <code>aria-label=&quot;Воспроизвести видео&quot;</code> и реагируют на Enter/Space.</li> <li data-num="1390"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>YouTube API грузится один раз</strong> — скрипт <code>iframe_api</code> подключается при первом клике и кешируется. Повторные клики используют уже загруженный API. Корректно сосуществует с Videogular (callback chain).</li> <li data-num="1391"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Нет утечки памяти при навигации</strong> — при смене слайда карусели или уходе со страницы все YT.Player-инстансы уничтожаются через <code>destroy()</code>, iframe удаляются из DOM.</li> <li data-num="1392"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойной клик не создаёт два плеера</strong> — guard <code>mediaItem.ytPlaying</code> отсекает повторные нажатия до завершения инициализации.</li> <li data-num="1393"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <code>**video-helpers.js</code> — общий модуль** — логика YouTube/Vimeo lazy-load, создания iframe, cleanup вынесена в один файл. Используется и в экспозиции, и в режиме превью редактора — ноль дублирования.</li> <li data-num="1394"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Тесты покрывают оба режима</strong> — добавлены тесты для autoplay в режиме чтения и его отсутствия в редакторе (ProseMirror сбрасывает src при расхождении). 12 тестов, все зелёные.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.121.0</span><span class="changelog-meta__date">24-03-2026</span></span><h2 id="v91210-24-03-2026--lighthouse-аудит-быстрее-доступнее-надёжнее">Lighthouse аудит: быстрее, доступнее, надёжнее</h2></summary> <div class="changelog-section__body"> <p>Прогнали мобильный Lighthouse по живой статье — и увидели, что можно лучше. Два CSS-файла блокировали первую отрисовку на секунду, шрифты выстраивались в очередь за стилями (цепочка до 4,4 с), а навигация в шапке путала скринридеры. Каждую из этих проблем мы решили без потерь для пользователя — страница остаётся такой же, но приходит раньше.</p> <ul> <li data-num="1381"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Tailwind CSS больше не блокирует отрисовку</strong> — 6,4 КБ утилитарных стилей переведены на асинхронную загрузку (<code>media=&quot;print&quot;</code> + <code>onload</code>). Всё, что нужно для первого кадра — сетка, контейнер, адаптивные ширины — заинлайнено прямо в HTML. Страница появл
1яется на ~500 мс раньше, а при повторных визитах разница ещё больше.</li> <li data-num="1382"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Шрифты стартуют параллельно с CSS</strong> — Muller и Spectral Regular получили <code>&lt;link rel=&quot;preload&quot;&gt;</code>. Раньше браузер узнавал о шрифтах только из CSS (который сам грузился из HTML) — двойная очередь, до 4,4 с ожидания. Теперь загрузка начинается одновременно с HTML — текст рендерится нативным шрифтом с первого кадра, подмена почти незаметна.</li> <li data-num="1383"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Навигация в шапке стала семантической</strong> — <code>&lt;ul role=&quot;navigation&quot;&gt;</code> (невалидная комбинация, путавшая скринридеры) заменён на <code>&lt;nav aria-label=&quot;Разделы&quot;&gt;</code>. Теперь ассистивные технологии корректно объявляют навигацию по разделам, а Lighthouse больше не сигнализирует об ошибке ARIA.</li> <li data-num="1384"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Skip-to-content работает до загрузки Tailwind</strong> — ссылка «Перейти к содержанию» в шапке опирается на <code>tw:sr-only</code> из Tailwind. Теперь стили скрытия и показа по фокусу заинлайнены — ссылка доступна с клавиатуры с первого мгновения, даже на медленном соединении.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.120.0</span><span class="changelog-meta__date">24-03-2026</span></span><h2 id="v91200-24-03-2026--итоги-большого-перехода">Итоги большого перехода</h2></summary> <div class="changelog-section__body"> <p>За неделю Дискурс прошёл путь, на который у многих проектов уходят месяцы. 34 релиза, 150+ файлов, ноль даунтайма — и платформа стала другой. Не визуально (снаружи вы почти ничего не заметите), а по ощущениям: быстрее, отзывчивее, доступнее. Вот главное.</p> <h3 id="bootstrap--всё-собственная-дизайн-система--в-деле">Bootstrap — всё. Собственная дизайн-система — в деле</h3> <p>Десять лет Bootstrap 3 диктовал каждый пиксель Дискурса. Теперь — нет. 2544 строки legacy-CSS удалены, сетка <code>.row</code>/<code>.col-*</code> заменена на Tailwind v4 с семантическими утилитами, а <code>ds-btn</code>, <code>ds-container</code>, <code>ds-input</code> — наш собственный язык компонентов. CSS стал легче на ~95 КБ, а каждый новый элемент опирается на единые дизайн-токены: цвета, шрифты, отступы, тени, z-index — всё в одном месте, видно в DevTools, синхронизировано между SCSS и Tailwind.</p> <h3 id="jquery--почти-всё-vanilla-js--везде">jQuery — почти всё. Vanilla JS — везде</h3> <p>15 компонентов (шапка, комментарии, голосование, донаты, баннеры, соцшаринг, модалки…) переписаны на нативные браузерные API. jQuery остался только в vendor-карусели Slick — последнем пассажире, которого высадим при переходе на SolidJS. Модалки, тултипы, аккордеоны — всё на чистом JS в 3–5 КБ вместо 18 КБ Bootbox/Bootstrap-плагинов.</p> <h3 id="для-читателей-быстрее-тише-приятнее">Для читателей: быстрее, тише, приятнее</h3> <ul> <li data-num="1372"><strong>Переходы между страницами мгновенные</strong> — вместо View Transitions API, который замораживал экран на 3 секунды, лёгкий CSS-fade. Страница остаётся живой.</li> <li data-num="1373"><strong>Скелетоны вместо белого экрана</strong> — лента, уведомления, карточки — всё начинается с плавных заглушек, а не с пустоты.</li> <li data-num="1374"><strong>Шапка из матового стекла</strong>, модалки с размытым фоном — интерфейс обрёл глубину.</li> <li data-num="1375"><strong>Баннеры всплывают при скролле</strong>, YouTube грузится только по клику — меньше трафика, больше контекста.</li> <li data-num="1376"><strong>Подписки перемешиваются каждый день</strong> — теперь лента начинается не с одних и тех же тем на «А».</li> </ul> <h3 id="для-авторов-редактор-крепче-формы-честнее">Для авторов: редактор крепче, формы честнее</h3> <ul> <li data-num="1377"><strong>Редактор выживает при сбое расширения</strong> — LanguageTool упал? Работаем дальше, Sentry доложит.</li> <li data-num="1378"><strong>Сноски, замены, поиск — починены</strong>. Квизы отображаются, картинки получили 4 новых позиции, черновик создаётся мгновенно для коллаборации.</li> <li data-num="1379"><strong>Валидация форм — только при нажатии кнопки</strong>, поля не краснеют от случайного blur. Мобильная модалка входа помещается на экран целиком.</li> <li data-num="1380"><strong>3 379 статей автоматически вычищены</strong> — мусор от Word/Google Docs, legacy-оглавления, пустые параграфы — всё убрано.</li> </ul> <h3 id="доступность-a11y-каждый-элемент--с-клавиатуры">Доступность (a11y): каждый элемент — с клавиатуры</h3> <p>2000 строк inline SVG → 18 иконок в спрайте с <code>aria-hidden</code>. Фокус-ловушки в модалках. <code>aria-expanded</code> на аккордеонах. <code>role=&quot;tab&quot;</code> на вкладках. <code>aria-label</code> на 19 textarea. Профиль-меню управляется стрелками ↑↓. «Перейти Ð
1º содержанию» — одним Tab&#39;ом. Кнопки голосования говорят: «Плюс 1», а не молчат.</p> <h3 id="real-time-websocket-работает-как-задумано">Real-time: WebSocket работает как задумано</h3> <p>HTTP/2 молча убивал заголовки Upgrade — DDP деградировал в long-polling. Починили. Exponential backoff с jitter, индикатор соединения у колокольчика, автовосстановление после даунтайма, звук при новом уведомлении, бейдж на вкладке, DDP-подписки на комментарии и сообщения — всё в реальном времени.</p> <h3 id="seo-36-покрытия-авторы-вернулись-в-поиск">SEO: +36% покрытия, авторы вернулись в поиск</h3> <p>Авторов в sitemap — с 50 до 1000+. Подкасты (195 эпизодов) впервые видны для Google. Хлебные крошки в выдаче. Динамический sitemap для свежего контента (индексация за день вместо двух недель). Legacy expo-URL → 301. Общее покрытие: 3 000 → 4 967 URL.</p> <h3 id="безопасность-и-мониторинг">Безопасность и мониторинг</h3> <p>Dashboard метрик Worker&#39;а с sparklines, алертами и Prometheus-экспортом. Rate limiting для API и dashboard. SSRF-защита, ReDoS-фикс, CSP-аудит, regex-якоря, localStorage-фильтрация — 15+ точечных SEC-фиксов.</p> <h3 id="цифры-одним-взглядом">Цифры одним взглядом</h3> <table> <thead> <tr> <th>Что</th> <th>Было</th> <th>Стало</th> </tr> </thead> <tbody><tr> <td>Bootstrap CSS</td> <td>2544 строки</td> <td>0</td> </tr> <tr> <td>jQuery в app-логике</td> <td>15 компонентов</td> <td>0 (только vendor Slick)</td> </tr> <tr> <td>Inline SVG в шаблонах</td> <td>~2000 строк</td> <td>18 иконок в спрайте</td> </tr> <tr> <td>CSS на странице</td> <td>+95 КБ лишнего</td> <td>минус 95 КБ</td> </tr> <tr> <td>Sitemap URL</td> <td>3 000</td> <td>4 967</td> </tr> <tr> <td>Авторы в поиске</td> <td>50</td> <td>1 000+</td> </tr> <tr> <td>a11y-исправления</td> <td>—</td> <td>60+</td> </tr> <tr> <td>Хардкодных rgba()</td> <td>311</td> <td>30+ токенов</td> </tr> <tr> <td>Магических font-size</td> <td>65</td> <td>14 CSS-переменных</td> </tr> </tbody></table> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.119.0</span><span class="changelog-meta__date">24-03-2026</span></span><h2 id="v91190-24-03-2026--подписки-вразнобой-донаты-с-клавиатуры-и-тишина-вместо-мерцания">Подписки вразнобой, донаты с клавиатуры и тишина вместо мерцания</h2></summary> <div class="changelog-section__body"> <p>Открываешь подписки — а там одни темы на «А». Переключаешь валюту в форме доната — а Tab улетает мимо. Страница грузится — а overlay мигает белым на пол-фрейма. Мелочи, но из них складывается ощущение незаконченности. В этом релизе мы взялись за россыпь таких мелочей — и каждую довели до щелчка.</p> <ul> <li data-num="1355"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Подписки перемешиваются каждый день</strong> — вкладка «Все» в модалке подписок больше не начинается с одних и тех же тем на «А». Авторы и темы перемешиваются псевдослучайно (Fisher-Yates shuffle с сидом от даты) — порядок выглядит хаотичным, но стабилен в пределах дня. Завтра — другой. Вся магия на клиенте, 0 запросов к серверу.</li> <li data-num="1356"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Уведомление о лимите — внизу, а не поверх списка</strong> — плашка «Показаны первые 500 пунктов» переехала в самый низ списка подписок. Видишь её только доскроллив до конца — не отвлекает, не вылезает за колонку.</li> <li data-num="1357"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Донаты управляются с клавиатуры</strong> — вкладки «Рубли / Доллары / Евро / Крипта» сменились с <code>&lt;div&gt;</code> на <code>&lt;button role=&quot;tab&quot;&gt;</code>. Стрелки ←→ переключают валюту, Home/End — к крайним, <code>aria-selected</code> объявляет активную вкладку.</li> <li data-num="1358"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойной клик по «Поддержать» не уведёт на Stripe дважды</strong> — guard <code>stripeSubmitting</code> блокирует повторную отправку. Кнопка показывает «Перенаправление…» и получает <code>aria-busy</code>, пока создаётся Checkout Session.</li> <li data-num="1359"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Overlay загрузки больше не мерцает</strong> — на быстрых переходах (когда данные приходят до 200ms) оверлей даже не появится: CSS <code>ng-enter</code> с задержкой + <code>ng-leave</code> с fade-out убирают белую вспышку. <code>prefers-reduced-motion</code> — без анимаций вообще.</li> <li data-num="1360"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Длинные ветки комментариев не тормозят скролл</strong> — <code>content-visibility: auto</code> на каждом <code>&lt;comment&gt;</code> и <code>&lt;remark&gt;</code>. Браузер пропускает layout для элементов за экраном — на статьях с 50+ комментариями прокрутка стала заметно легче.</li> <li data-num="1361"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>«Перейти к содержанию»</strong> — первая ссылка в шапке, невидимая до фокуса. Клавиатурные пользователи перепрыгивают навигацию одним Tab&#39;ом.</li> <li data-num="1362"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Collapse синхронизирует <code>aria-expanded</code></strong> — при раскрытии/сворачивании аккордеона все связанные триггеры обновляют <code>aria-expanded</code>. Screen reader всегда знает, открыто или закрыто.</li> <li data-num="1363"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Dialog-lite стал доступнее</strong> — <code>aria-modal=&quot;true&quot;</code>, заголовок привязан через <code>aria-labelledby</code>, async-ошибки в callbacks перехватываются (UI не залипнет молча).</li> <li data-num="1364"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Модалка не крадёт фокус у тултипов</strong> — <code>modal-lite</code> теперь не перехватывает фокус, если он ушёл в <code>.tooltip</code>, Tippy-попап, tiptap-оверлей или mention-список. Набираешь <code>@</code> в комментарии — курсор остаётся в автокомплите, а не улетает в модалку.</li> <li data-num="1365"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Заголовки статей больше не обрезаются</strong> — line-clamp на карточках ленты убран. Длинный заголовок видно целиком — на главной, в теме, в expo, в read-also.</li> <li data-num="1366"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Битая картинка комментария не рендерится</strong>
1 — <code>&lt;img&gt;</code> лучшего комментария под статьёй теперь скрыт через <code>ng-if</code>, если <code>image</code> пустой. Никакого сломанного значка.</li> <li data-num="1367"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Есть идеи?»</strong> — баннер предложений и ссылка «Стать автором» теперь обращаются во множественном числе. Мелочь, но звучит дружелюбнее и точнее.</li> <li data-num="1368"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Connect заговорил по-русски</strong> — «Recovery code» → «Код восстановления» на всей странице VPN (labels, подсказки, кнопки). Скелетон-загрузка переехала с inline style на Tailwind-утилиты.</li> <li data-num="1369"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Dark mode цвета редактора</strong> — 13 семантических цветов (suggestion-add, delete, resolve, comment-highlight и др.) получили осветлённые dark-mode варианты в <code>tailwind.css</code>. Правки в тёмной теме читаемы без щуренья.</li> <li data-num="1370"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <code>**unhandledrejection</code> ловится глобально** — забытый <code>.catch()</code> в async-цепочке больше не тихо пропадёт — ошибка попадёт в консоль и в Sentry.</li> <li data-num="1371"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>viewModal: −30 строк</strong> — дублирующий Escape-handler удалён; modal-lite уже обрабатывает Escape глобально. Меньше listeners — меньше утечек.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.118.0</span><span class="changelog-meta__date">23-03-2026</span></span><h2 id="v91180-23-03-2026--баннеры-вернулись-sticky-sidebar-жив-анимация-при-скролле-и-чистый-vanilla-js">Баннеры вернулись: sticky sidebar жив, анимация при скролле и чистый vanilla JS</h2></summary> <div class="changelog-section__body"> <p>Боковые баннеры рядом со статьёй — те самые «Самое читаемое», «Есть идеи?», «Поддержите Дискурс» — сломались при миграции с Bootstrap. Они то убегали за футер, то исчезали, то теряли sticky-эффект. Мы докопались до корня: Angular <code>transclude: &#39;element&#39;</code> вставлял компонент не туда, куда ожидалось, а <code>$onInit</code> обращался к DOM до того, как шаблон попадал в живое дерево. Теперь всё на месте — баннеры прилипают к экрану при чтении, плавно всплывают при скролле и работают без единой строчки jQuery.</p> <ul> <li data-num="1349"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Баннеры больше не улетают за футер</strong> — Bootstrap <code>col-md-7</code> давал <code>position: relative</code>, а Tailwind-утилиты — нет. Абсолютно позиционированная обёртка баннеров теряла containing block и позиционировалась относительно всей страницы. Новый класс <code>.post-content-col</code> гарантирует <code>position: relative</code> на колонке контента — баннеры снова живут рядом со статьёй.</li> <li data-num="1350"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Sticky-скролл восстановлен</strong> — при чтении длинного материала баннеры снова фиксируются на экране, сопровождая читателя. Блок «Самое читаемое» останавливается в конце статьи, а не в середине — ровно как было до миграции.</li> <li data-num="1351"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Чёрный текст на чёрных кнопках</strong>
1 — глобальное правило <code>a:visited { color: #000 }</code> перекрашивало текст кнопок «Предложить идею» и «Поддержать» в невидимый чёрный. Теперь <code>.banner-button</code> сохраняет белый текст во всех состояниях — <code>:visited</code>, <code>:active</code>, <code>:focus</code>.</li> <li data-num="1352"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Баннеры всплывают при скролле</strong> — вместо мгновенного появления каждый баннер мягко поднимается снизу при попадании во вьюпорт. IntersectionObserver запускает анимацию за 100px до видимой области — читатель видит полное движение, а не застывший результат. Первый баннер чуть выразительнее остальных.</li> <li data-num="1353"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**prefers-reduced-motion</code> отключает анимацию** — пользователи с вестибулярными нарушениями видят баннеры сразу, без движения.</li> <li data-num="1354"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>jQuery полностью удалён из баннеров</strong> — <code>$(&#39;#asideBannersWrapper&#39;).css()</code> и <code>$(window).height()</code> заменены на <code>document.getElementById</code> и <code>window.innerHeight</code>. Все DOM-операции обёрнуты в <code>$timeout</code> — шаблон компонента гарантированно в живом DOM.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.117.0</span><span class="changelog-meta__date">23-03-2026</span></span><h2 id="v91170-23-03-2026--мгновенные-переходы-матовое-стекло-и-последний-jquery-платформа-дышит-свободнее">Мгновенные переходы, матовое стекло и последний jQuery: платформа дышит свободнее</h2></summary> <div class="changelog-section__body"> <p>Знаете ощущение, когда нажимаешь ссылку — а страница замирает? Это View Transitions API: браузер делал скриншот старой страницы и ждал, пока новая загрузится — до трёх секунд заморозки ради анимации в четверть секунды. А индикатор загрузки мог не появиться 2.5 секунды из-за cooldown&#39;а — и пользователь видел белый экран без объяснений. Теперь переходы неблокирующие: контент плавно уходит и появляется через CSS opacity, страница остаётся живой. Параллельно вычистили последние jQuery-вызовы из логики приложения, добавили матовое стекло на шапку и модалки, починили редактор — и окончательно удалили файл с переменными Bootstrap.</p> <ul> <li data-num="1333"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Переходы между страницами — мгновенные и живые</strong> — View Transitions API замораживал экран на время загрузки маршрута (до 3 сек). Заменён на CSS opacity-переход: 0.12s fade-out + 0.15s fade-in через класс <code>is-nav-transition</code> на body. Страница остаётся интерактивной — клики работают, ничего не блокируется. При ошибке маршрута контент не зависнет на opacity: 0 — три страховочных точки (<code>$routeChangeError</code>, <code>safeHideOverlay</code>, safety timeout`) гарантированно снимут класс.</li> <li data-num="1334"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>
1Overlay появляется и уходит вдвое быстрее</strong> — индикатор загрузки стартует через 200ms вместо 350, минимальная выдержка — 100ms вместо 200, пауза между показами — 0.6с вместо 2.5. На быстром соединении оверлей не мелькнёт, на медленном — появится раньше и исчезнет быстрее. CSS <code>ng-enter</code> с задержкой 200ms + <code>ng-leave</code> с fade-out: если навигация завершится быстрее задержки — оверлей даже не проявится, никаких белых вспышек.</li> <li data-num="1335"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Публикация больше не ведёт на 404</strong> — redirect после «Опубликовать» вычислялся из URL (<code>/edit/X4WdRAACsQ</code> → <code>/X4WdRAACsQ</code>), а маршрутизатор такой страницы не знал. Теперь — трёхуровневый fallback: данные из API, затем URL-валидация, затем подгрузка ссылки с сервера. Если ничего не помогло — перенаправление в черновики, а не в пустоту.</li> <li data-num="1336"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойной клик по «Опубликовать» не дублирует</strong> — guard в <code>sendForm()</code> блокирует повторные вызовы, пока первый запрос в полёте. Нетерпеливый тап на мобильном больше не создаст два черновика.</li> <li data-num="1337"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сноски не падают при быстром Delete</strong> — удаление в пустом параграфе footnote-редактора теперь проверяет содержимое (не считая zero-width spaces). Вставка сохраняет форматирование, конвертирует <code>&lt;div&gt;</code> → <code>&lt;p&gt;</code>, экранирует спецсимволы через единый <code>escapeHtml()</code>.</li> <li data-num="1338"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Шапка из матового стекла</strong> — при прокрутке хедер получает <code>backdrop-filter: blur(12px)</code> с полупрозрачным фоном и тонкой тенью. Содержимое страницы мягко просвечивает — меньше шума, больше контекста.</li> <li data-num="1339"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Модалки с размытым фоном</strong> — backdrop диалогов теперь размывает содержимое на 4px. Затемнение снижено с 60% до 50% — вместе с размытием это даёт ощущение глубины, а не чёрной стены.</li> <li data-num="1340"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Таблицы в статьях не ломают мобильную вёрстку</strong> — авторские таблицы в теле материала получили горизонтальный скролл, границы ячеек и выделение заголовков. На телефоне таблица листается пальцем.</li> <li data-num="1341"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>jQuery ушёл из логики приложения</strong> — 15 компонентов (комментарии, ремарки, голосование, соцшаринг, донаты, юбилей) перешли на нативные <code>addEventListener</code>, <code>window.scrollTo({ behavior: &#39;smooth&#39; })</code>, <code>element.matches()</code>. С корректным <code>removeEventListener</code> в <code>$destroy</code> — без утечек памяти. jQuery остался только в vendor-карусели Slick.</li> <li data-num="1342"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопки и инпуты дышат шрифтом Muller</strong>
1 — <code>line-height: 1.428</code> (Bootstrap 3) заменён на <code>1.45</code>. Разница в пиксель, но буквы перестали упираться в рамку.</li> <li data-num="1343"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Подкасты со своей сеткой</strong> — страницы подкастов перешли с Tailwind-утилит на BEM-классы <code>.podcast-page__grid-*</code> в SCSS. Раскладка стабильнее, не зависит от порядка загрузки CSS.</li> <li data-num="1344"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Кнопка редактирования сноски озвучивается</strong> — <code>aria-label=&quot;редактировать сноску&quot;</code> на иконке-карандаше. Screen reader называет действие, а не молчит.</li> <li data-num="1345"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <code>**bootstrap-vars-compat.scss</code> удалён** — последние 42 строки SCSS-переменных из Bootstrap 3 инлайнены по месту использования. На один файл меньше — на одну зависимость от 2015 года меньше.</li> <li data-num="1346"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>30+ альфа-токенов вместо <code>rgba()</code> россыпью</strong> — палитра <code>--alpha-black-*</code> и <code>--alpha-white-*</code> расширена с 18 до 30+ значений с автокоррекцией для тёмной темы. 311 хардкодных <code>rgba()</code> теперь — через одну переменную.</li> <li data-num="1347"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Семантические цвета для правок в редакторе</strong> — новые токены <code>--color-suggestion-add</code>, <code>--color-suggestion-delete</code>, <code>--color-comment-highlight</code> в <code>tailwind.css</code>. Diff-подсветка привязана к осмысленным именам.</li> <li data-num="1348"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Колонки Tailwind не сжимаются</strong> — <code>gutter-half</code> получил <code>flex: 0 0 auto</code> и <code>min-width: 0</code>. Длинный <code>&lt;pre&gt;</code> или широкая картинка больше не ломают сетку.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.116.0</span><span class="changelog-meta__date">23-03-2026</span></span><h2 id="v91160-23-03-2026--быстрее-доступнее-легче-2000-строк-svg-→-18-иконок-jquery-→-браузер-каждая-кнопка--с-клавиатуры">Быстрее, доступнее, легче: 2000 строк SVG → 18 иконок, jQuery → браузер, каждая кнопка — с клавиатуры</h2></summary> <div class="changelog-section__body"> <p>Когда в шаблоне ленты 34 раза вклеена одна и та же SVG-иконка — это не дизайн, это копипаст. Когда боковые баннеры статьи ждут секунду перед появлением — это не анимация, это <code>setTimeout(1000)</code>. Когда Tab улетает за модалку входа — это не UX, это баг. Этот релиз — генеральная уборка: SVG-спрайт вместо копипаста, нативные API браузера вместо jQuery-обёрток десятилетней давности, ARIA-разметка на каждом интерактивном элементе. Ни одной новой фичи — но платформа стала быстрее, легче и дружелюбнее к каждому пользователю.</p> <ul> <li data-num="1314"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>2000 строк inline SVG → 18 иконок в спрайте</strong> — 34 svg-icon в ленте, 18 в шапке. Одна иконка — одно определение в спрайте, одна ссылка <code>&lt;svg-icon name=&quot;article&quot;&gt;</code> в шаблоне. Браузер парсит HTML ленты на ~40% быстрее просто потому, что в нём на 1400 строк меньше.</li> <li data-num="1315"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>
1Боковые баннеры появляются мгновенно</strong> — <code>setTimeout(1000)</code> и jQuery-измерение высоты страницы убраны. CSS <code>position: sticky</code> + <code>overflow: hidden</code> на grid-обёртке сами ограничивают баннеры границами статьи — JavaScript больше не гадает, поместятся ли.</li> <li data-num="1316"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Скелетон ленты вместо спиннера</strong> — при первой загрузке ленты три карточки-заглушки с shimmer-анимацией имитируют реальную раскладку. Нет белого экрана, нет «крутилки» — читатель сразу видит структуру страницы. При переключении вкладок — компактный спиннер без мерцания.</li> <li data-num="1317"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>jQuery уходит из шапки</strong> — <code>$(window).scrollTop()</code>, <code>$(&#39;html&#39;).addClass()</code>, <code>$(&#39;#mobile-menu&#39;).on()</code> → <code>window.scrollY</code>, <code>classList</code>, <code>addEventListener</code>. Шесть мест, ноль jQuery — мобильное меню, скролл и dropdown&#39;ы работают на голых браузерных API. Обработчики мобильного меню чистятся в <code>$onDestroy</code> — нет утечек. Dropdown-submenu закрываются через <code>{ once: true }</code> — накопление listeners невозможно.</li> <li data-num="1318"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Dropdown&#39;ы навигации объявлены</strong> — ссылки «Статьи», «Искусство», «Подкасты» получили <code>aria-haspopup=&quot;true&quot;</code>. Screen reader сообщает, что за ссылкой — выпадающее меню, а не обычный переход.</li> <li data-num="1319"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>126 изображений в сетке получили <code>decoding=&quot;async&quot;</code></strong> — браузер декодирует JPEG/WebP в фоновом потоке, не блокируя отрисовку текста и кнопок рядом. Особенно заметно на главной — 30 карточек с обложками больше не тормозят скролл.</li> <li data-num="1320"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Фокус не сбежит из модалки входа</strong> — <code>createFocusTrap</code> перехватывает Tab/Shift+Tab и циклически обходит поля внутри окна. Закрытие возвращает фокус на кнопку, которая открыла модалку. Ни один клавиатурный пользователь не потеряется за backdrop&#39;ом.</li> <li data-num="1321"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Профиль-меню для клавиатуры</strong> — <code>aria-haspopup</code>, <code>aria-expanded</code> на аватаре, <code>role=&quot;menu&quot;</code> и <code>role=&quot;menuitem&quot;</code> на каждом пункте. Стрелки ↑↓ перемещают фокус между пунктами, Home/End — к первому и последнему, Escape — закрывает. Все три варианта шапки (десктоп, планшет, редактор) ведут себя одинаково.</li> <li data-num="1322"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Кнопки голосования говорят</strong> — <code>aria-label=&quot;Плюс 1&quot;</code> и <code>&quot;Минус 2&quot;</code> на каждой кнопке рейтинга. Screen reader больше не читает бессмысленное «кнопка, плюс» — он скажет «Голос плюс один».</li> <li data-num="1323"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Лента управляется Tab&#39;ом</strong> — боковая навигация (Всё / Моя лента / Редакция), сортировка (Новое / Популярное / Обсуждаемое) — всё на <code>&lt;button role=&quot;tab&quot;&gt;</code> с <code>aria-selected</code>. Dropdown&#39;ы фильтров получили <code>aria-expanded</code> и <code>aria-haspopup</code>. Черновики — <code>role=&quot;listbox&quot;</code> на каждом dropdown&#39;е + Escape-to-close.</li> <li data-num="1324"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>SSR-раскладка больше не прыгает</strong> — <code>tw:md:ml-offset-2</code> (самый используемый отступ — 51 место) отсутствовал в SSR-фолбэке. Плюс: <code>lg</code>-breakpoint исправлен с 1300px на 1200px (как в Tailwind). Первый экран рендерится точно так же, как после загрузки стилей.</li> <li data-num="1325"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Страница ошибки центрируется</strong> — <code>top: 35%</code> + <code>transform: translate(0, -40%)</code> заменено на <code>top: 50%</code> + <code>translateY(-50%)</code>. Кот с 404 наконец-то строго по центру, а не слегка выше.</li> <li data-num="1326"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Scroll-to-top при навигации</strong> — переход между страницами (но не по хеш-ссылкам #comments) автоматически прокручивает к началу. Больше никаких «открыл новую статью — а она внизу».</li> <li data-num="1327"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Regex API-валидации заякорен</strong> — URL <code>https://connect-api.discours.io.evil.com</code> больше не пройдёт проверку. Добавлен <code>(?:\\/|$)</code> в конце паттерна — только точный домен или его подпуть.</li> <li data-num="1328"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Ключи не утекают в localStorage</strong> — <code>restoreResult</code> (как и <code>issueResult</code>) теперь фильтруется перед сохранением: только <code>key_id</code>, <code>brigade</code>, <code>recovery_code</code>. Если API вернёт <code>access_key</code> — он не окажется в хранилище браузера.</li> <li data-num="1329"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Мёртвый код вычищен</strong> — 37 строк IE-совместимого <code>_getRangeAtCollapse</code> (нигде не вызывался), CSS-классы <code>.help-block</code>, <code>.control-label</code>, <code>.icon-bar</code> (нигде не применялись), дубликат <code>.small</code>, опечатка <code>AVAIBLE_TYPES</code>. Меньше мусора — чище голова.</li> <li data-num="1330"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Connect: поле краснеет при ошибке</strong> — пустой recovery code при нажатии «Восстановить» подсвечивает инпут красной рамкой + <code>aria-invalid</code>. Ошибка сбрасывается при вводе — без лишних нотификаций.</li>
1 <li data-num="1331"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Директива <code>&lt;svg-icon&gt;</code></strong> — <code>&lt;svg-icon name=&quot;article&quot; size=&quot;24&quot;&gt;</code> рендерит <code>&lt;svg&gt;&lt;use href=&quot;#icon-article&quot;&gt;&lt;/svg&gt;</code>. Цвет — через <code>currentColor</code>, размер — через атрибут. <code>aria-hidden=&quot;true&quot;</code> и <code>focusable=&quot;false&quot;</code> по умолчанию. Добавить иконку — одна строка в спрайте, одна — в шаблоне.</li> <li data-num="1332"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>sessionStorage не копит мусор</strong> — флаги <code>chunk-reload-*</code> (защита от бесконечных перезагрузок при редеплое) очищаются при успешной загрузке <code>main.js</code>. Долгоживущие вкладки больше не застревают на старых флагах.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.115.0</span><span class="changelog-meta__date">23-03-2026</span></span><h2 id="v91150-23-03-2026--единый-язык-дизайна-токены-вместо-магических-чисел-клавиатура-вместо-мыши">Единый язык дизайна: токены вместо магических чисел, клавиатура вместо мыши</h2></summary> <div class="changelog-section__body"> <p>65 хардкодных font-size, 35 hex-цветов, 4 z-index ловушки, 19 textarea без подписей — всё это жило в коде незамеченным. В этом релизе мы прошлись по фронтенду с единственной целью: каждое визуальное решение должно опираться на дизайн-токен, каждый интерактивный элемент должен работать с клавиатуры, а модалки — не выпускать фокус. Ни одного нового пикселя на экране — но под капотом платформа стала ощутимо прочнее.</p> <ul> <li data-num="1307"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>65 магических пикселей стали токенами</strong> — в карточках ленты и на странице статьи каждый <code>font-size: 13px</code> теперь — <code>var(--font-size-caption)</code>. 7 новых размеров в шкале: от <code>--font-size-3xs</code> (10px) до <code>--font-size-5xl</code> (40px). Дизайнер меняет один токен — обновляются подписи к фото, сноски, счётчики и changelog разом.</li> <li data-num="1308"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Модалка входа заговорила на языке дизайн-системы</strong> — 35 хардкодных цветов (<code>#c62828</code>, <code>#000</code>, <code>#3b5998</code>…) заменены на токены: <code>var(--color-error)</code>, <code>var(--color-on-surface)</code>, <code>var(--color-facebook)</code>. Если завтра появится тёмная тема — модалка подхватит её автоматически.</li> <li data-num="1309"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>z-index больше не лотерея</strong> — хардкодные <code>1010</code>, <code>1003</code>, <code>1500</code> заменены на семантические <code>var(--z-sticky)</code>, <code>var(--z-hero-banner)</code>. Новый токен <code>--z-hero-banner: 1500</code> занял место между <code>--z-link-preview</code> и <code>--z-footnote-modal</code>. Наконец-то каждый слой интерфейса знает своё место.</li> <li data-num="1310"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>19 textarea получили имена</strong> — каждое текстовое поле в редакторе — от заголовка статьи до ответа на комментарий — теперь имеет <code>aria-label</code>. Screen reader не скажет «текстовое поле, пустое» — он скажет «Заголовок видео» или «Текст ответа на комментарий».</li> <li data-num="1311"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Фокус не убежит из модалки</strong> — Tab и Shift+Tab в loginModal, subscriptionsModal, inviteCollaborators и других AngularJS-модалках теперь циклически обходят элементы внутри окна. Существующий <code>focusTrap.js</code> интегрирован в <code>modal-lite.js</code> — одна строка, а Tab больше не улетает на страницу позади.</li> <li data-num="1312"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Генератор токенов не забудет новые размеры</strong> — 7 font-size и <code>z-hero-banner</code> добавлены в <code>design-tokens.js</code>. Запуск <code>npm run tokens:generate</code> больше не стирает ручные правки — токены живут в одном месте и расходятся в SCSS, Tailwind и JSON синхронно.</li> <li data-num="1313"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>13 семантических цветов редактора для тёмной темы</strong> — <code>--color-suggestion-add</code>, <code>-delete</code>, <code>-resolve</code>, <code>--color-comment-highlight</code> и ещё 9 токенов получили осветлённые dark-mode варианты в <code>tailwind.css</code>. Правки, комментарии и поиск в тёмной теме читаемы без подстройки яркости монитора.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.114.0</span><span class="changelog-meta__date">22-03-2026</span></span><h2 id="v91140-22-03-2026--от-каждого-пикселя--польза-от-каждого-байта--скорость">От каждого пикселя — польза, от каждого байта — скорость</h2></summary> <div class="changelog-section__body"> <p>Прошлись по всем обновлениям последних дней с лупой и скальпелем. Результат: страницы грузятся ещё быстрее (карточки за экраном не тратят ресурсы на отрисовку), якорные ссылки больше не прячутся за шапкой, анимации не мешают людям с вестибулярными нарушениями, а дизайн-система получила полноценную валидацию цветовых токенов. Это не новые фичи — это фундамент, на котором фичи работают надёжно.</p> <ul> <li data-num="1297"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Карточки ленты рендерятся только когда видны</strong> — <code>content-visibility: auto</code> на каждой карточке статьи, комментарии (<code>&lt;comment&gt;</code>) и ремарке (<code>&lt;remark&gt;</code>). Браузер пропускает layout и paint для элементов ниже экрана — на длинных лентах и статьях с 50+ комментариями прокрутка становится заметно отзывчивее, особенно на телефонах.</li> <li data-num="1298"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Сетка статей на главной больше не ломается</strong>
1 — <code>.big-container</code> получил <code>display: flex</code>, без которого колонки <code>tw:sm:w-1/2</code> не вставали в ряд. Устаревший clearfix-хак удалён — flexbox справляется сам.</li> <li data-num="1299"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Якоря не прячутся за шапкой</strong> — глобальный <code>scroll-margin-top: 80px</code> на всех элементах с <code>id</code>. Клик по <code>#comments</code>, <code>#voting</code>, <code>#faq</code> — и секция открывается ровно там, где нужно, а не под фиксированным хедером.</li> <li data-num="1300"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Анимации уважают пользователя</strong> — глобальный <code>prefers-reduced-motion</code> отключает все transitions и animations разом. Раньше приходилось прописывать правило в каждом компоненте отдельно — теперь один catch-all покрывает всё.</li> <li data-num="1301"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ошибка голосования стала читаемой</strong> — блок с текстом ошибки в модалке отзыва публикации перешёл с хардкодных hex-цветов на токены <code>tw:text-error</code>, <code>tw:bg-error-bg</code>. Бонус: новые токены <code>--color-error-bg</code> и <code>--color-error-border</code> доступны во всех компонентах.</li> <li data-num="1302"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Печать не ловит лишнее</strong> — print.scss перестал использовать <code>[class*=...]</code> для spacing-классов. Раньше <code>tw:mt-section</code> ловил и <code>tw:mt-section-xl</code> — теперь каждый класс сбрасывается точечно.</li> <li data-num="1303"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>13 мёртвых SCSS-переменных удалены</strong> — <code>$grid-gutter-width</code>, <code>$text-color</code>, <code>$headings-*</code>, <code>$navbar-height</code> и другие переменные, не используемые ни в одном файле, вычищены из <code>bootstrap-vars-compat.scss</code>. Меньше шума — легче ориентироваться.</li> <li data-num="1304"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Скрипт валидации цветовых токенов</strong> — <code>npm run tokens:validate</code> сверяет hardcoded-цвета в <code>@layer components</code> с <code>design-tokens.js</code>. Дрейф замечается до того, как становится проблемой.</li> <li data-num="1305"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Muller готов к переходу</strong> — CSS-переменные <code>--body-font-size</code>, <code>--body-line-height</code>, <code>--ui-font-size</code> определены в <code>:root</code>. Когда дизайнер скажет «меняем» — это будет одна правка в одном файле, а не охота по сотне селекторов.</li> <li data-num="1306"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Кастомные breakpoints задокументированы</strong> — 500px, 600px, 660px, 900px — каждый с пояснением, зачем он нужен и где используется. Новый разработчик не будет гадать, почему в article.scss стоит 660px, а не 768.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.113.0</span><span class="changelog-meta__date">22-03-2026</span></span><h2 id="v91130-22-03-2026--вход-без-нервов-валидация-по-делу-модалка-без-прокрутки-и-сквозной-аудит-доступности">Вход без нервов: валидация по делу, модалка без прокрутки и сквозной аудит доступности</h2></summary> <div class="changelog-section__body"> <p>Вы только открыли окно входа, ткнули мимо поля — и уже красная ошибка. Знакомо? Больше нет. Теперь валидация срабатывает только при нажатии «Войти» или «Зарегистрироваться» — когда вы действительно готовы отправить форму. А на мобильных окно входа наконец-то помещается на экран целиком — без прокрутки и скачков. Заодно прошли все auth-формы платформы насквозь и закрыли 11 багов доступности.</p> <ul> <li data-num="1283"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Валидация — только по нажатию кнопки</strong> — поля входа, регистрации, сброса пароля и disclaimer-формы больше не краснеют при простом уходе курсора. Ошибки появл
1яются при отправке формы, а исчезают — в реальном времени при исправлении. Одно изменение в <code>form-validation.js</code> — и все четыре формы стали дружелюбнее.</li> <li data-num="1284"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильная модалка помещается на экран</strong> — окно входа поднялось выше (top 56px вместо 90), инпуты, кнопки и OAuth-блок стали компактнее. На iPhone SE регистрация с индикатором пароля, четырьмя соцсетями и ссылкой «Нет аккаунта?» умещается без прокрутки.</li> <li data-num="1285"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Индикатор сложности пароля больше не отстаёт</strong> — убран 150ms debounce с поля пароля при регистрации и сбросе. Полоска «Слабый → Средний → Сильный» теперь реагирует на каждый символ мгновенно, без лага в 1–2 нажатия.</li> <li data-num="1286"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Индикатор пароля сбрасывается при смене формы</strong> — раньше переключение вход ↔ регистрация могло оставить «призрак» старого индикатора. Теперь <code>showRegisterForm()</code> и <code>showLoginForm()</code> обнуляют strength-состояние.</li> <li data-num="1287"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Backdrop закрывает только верхнюю модалку</strong> — клик по затемнению при открытой модалке «Подтвердите email» закрывает только её, не трогая основное окно входа. Раньше одним кликом схлопывалось всё.</li> <li data-num="1288"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Autofill не крадёт фокусную рамку</strong> — при автозаполнении браузером поле сохраняет чёрный border в фокусе, а не сбрасывается на серый.</li> <li data-num="1289"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Один заголовок — один <code>id</code></strong> — пять элементов с <code>id=&quot;login-modal-title&quot;</code> заменены на один. Screen reader теперь корректно озвучивает заголовок модалки в любом режиме.</li> <li data-num="1290"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**aria-live</code> на заголовке модалки** — при переключении «Вход → Регистрация → Забыли пароль?» screen reader озвучивает новый заголовок автоматически.</li> <li data-num="1291"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Серверные ошибки связаны с полями</strong> — form-level сообщения «Ошибка авторизации» получили <code>id</code> и подключены через <code>aria-describedby</code>. Screen reader читает ошибку в контексте поля, а не как изолированный alert.</li> <li data-num="1292"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Touch-target кнопки «показать пароль»</strong> — padding увеличен до 44×44px (WCAG minimum). На мобильном в неё наконец можно попасть пальцем с первого раза.</li> <li data-num="1293"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Focus-visible на кнопке закрытия</strong> — при навигации Tab&#39;ом крестик получает видимый чёрный outline. Клавиатурные пользователи больше не теряются.</li> <li data-num="1294"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Контраст индикатора пароля</strong> — подпись «Средний (добавьте цифры)» стала темнее (#555 вместо #666) — WCAG AA на белом фоне гарантирован.</li> <li data-num="1295"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**lang=&quot;ru&quot;</code> и <code>aria-label</code> на модалках** — модалка входа объявляет язык для правильного произношения, кнопка закрытия verify-email получила пропущенный label.</li> <li data-num="1296"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Hover-эффект на полях ввода</strong> — border плавно темнеет при наведении, давая визуальную обратную связь ещё до клика. Мелочь, но форма ощущается живой.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.112.0</span><span class="changelog-meta__date">22-03-2026</span></span><h2 id="v91120-22-03-2026--bootstrap-прощай-собственная-дизайн-система-вместо-фреймворка-2015-года">Bootstrap, прощай: собственная дизайн-система вместо фреймворка 2015 года</h2></summary> <div class="changelog-section__body"> <p>Десять лет Bootstrap задавал правила игры для каждого пикселя Дискурса — от сетки статей до кнопки «Опубликовать». Этот релиз завершает переход на собственную дизайн-систему: 2544 строки Bootstrap удалены, стили разделены на лёгкий normalize и точечные class-определения, а Tailwind v4 с семантическими токенами стал единственным источником новых стилей. Страницы загружаются быстрее, код стал прозрачнее, а путь к SolidJS — короче.</p> <ul> <li data-num="1267"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Минус 52 KB мёртвого CSS</strong> — <code>bootstrap-compat.scss</code> (2544 строки) удалён целиком. Вместо монолитного файла-компромисса — два компактных: <code>_normalize-compat.scss</code> (element-level resets) и <code>_bootstrap-backup.scss</code> (только те классы, что реально используются). Каждый байт на счету: страницы грузятся ощутимо легче.</li> <li data-num="1268"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Tailwind похудел вдвое</strong> — с 33.8 KB до 14.6 KB. Чистый <code>tailwind.css</code> теперь содержит только design tokens и компоненты дизайн-системы (<code>.big-button</code>, <code>.linklike</code>, фирменные 7px-бордера). Никакого Bootstrap-балласта.</li> <li data-num="1269"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>4 spacing-токена вместо магических пикселей</strong> — <code>tw:mt-section</code> (30px), <code>tw:mt-content-top</code> (100px), <code>tw:mb-section-lg</code> (50px), <code>tw:mt-section-xl</code> (60px). 62 места в шаблонах перешли с <code>tw:mt-[30px]</code> на осмысленные имена. Дизайнер меняет один токен — обновляется весь сайт.</li> <li data-num="1270"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Scrollbar-миксины: два стали одним</strong> — дублирующий <code>thin-scrollbar()</code> из tiptap-mixins удалён, канонический <code>scrollbar-thin()</code> получил параметр <code>$hover</code> для явного цвета при наведении. 6 вызовов в 4 файлах мигрированы без визуальных изменений.</li> <li data-num="1271"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>SCSS-квалификаторы больше не блокируют миграцию</strong> — 6 мест, где SCSS-селекторы зависели от имён helper-классов (<code>.row.margin-top-80px</code>, <code>.form-group.relative</code>), заменены на семантические классы. Теперь HTML-классы можно менять, не ломая стили.</li> <li data-num="1272"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>100% миграция на Tailwind</strong> — ноль <code>.row</code>, <code>.col-*</code>, <code>.hidden-xs</code> во всём проекте. Последний оплот — генератор Changelog — тоже переведён на <code>tw:row-flex</code> и <code>tw:md:w-7/12</code>. Bootstrap grid полностью вычеркнут из истории — и из CSS, и из HTML.</li> <li data-num="1273"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Bootbox ушёл — dialog-lite пришёл</strong> — 18 KB внешней библиотеки заменены на 4 KB встроенного <code>dialog-lite.js</code>. Модальные диалоги (отзыв публикации, подтверждения) работают так же, но не тянут за собой jQuery-плагин из 2014 года. Критический путь загрузки стал короче на ~350ms.</li> <li data-num="1274"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Редактор быстрее замечает обрыв связи</strong> — таймаут collab-синхронизации сократился с 20 до 8 секунд. Если HocusPocus не отвечает — автор увидит toast с кнопкой «Повторить подключение», а не будет 20 секунд гадать, почему курсор соавтора замер.</li> <li data-num="1275"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>LanguageTool не включается без спроса</strong> — проверка орфографии больше не восстанавливается из localStorage при открытии редактора. Текст не уходит на внешний API, пока автор сам не нажмёт кнопку. Быстрее загрузка, меньше вопросов о приватности.</li> <li data-num="1276"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Статус ремарки больше не теряется</strong> — при сливании ремарки с полными данными <code>resolved: false</code> раньше перезаписывался значением по умолчанию (из-за <code>||</code> вместо <code>!== undefined</code>). Теперь явно выставленный <code>false</code> не пропадает.</li> <li data-num="1277"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Чанки грузятся со второй попытки</strong> — если динамический import упал на медленном 3G, через 1.5 секунды происходит повторная попытка. Мобильные пользователи больше не теряют редактор из-за одного сетевого сбоя.</li> <li data-num="1278"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Баг генератора токенов</strong> — многострочные комментарии в <code>design-tokens.js</code> ломали SCSS-выход (строки с <code>*</code> не получали префикс <code>//</code>). Сборка падала при любом изменении токенов. Исправлено в <code>generate-design-tokens.js</code>.</li> <li data-num="1279"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Rate limiting для Connect API</strong> — Cloudflare Worker ограничивает <code>/api/access/</code> до 60 запросов в минуту на IP. Автоочистка Map каждые 30 секунд, LRU-вытеснение при 5000 записей — память Worker&#39;а под контролем.</li> <li data-num="1280"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Линтер Tailwind-синтаксиса</strong>
1 — <code>npm run lint:tw</code> ловит <code>md:tw:hidden</code> (неправильно) и предлагает <code>tw:md:hidden</code> (правильно). Интегрирован в lint-staged — ошибка не доедет до коммита.</li> <li data-num="1281"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>bower_components/bootstrap удалены</strong> — 83 KB orphaned-файлов Bootstrap 3.3.7 и bootstrap-sass больше не лежат в репозитории. Ноль ссылок, ноль импортов.</li> <li data-num="1282"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Scorecard: 100%</strong> — ноль legacy-классов в source HTML. Scorecard стал точнее: отличает проектные компоненты (<code>.border-*-7px</code>, <code>.linklike</code>) от legacy, не путает <code>social-buttons-absolute</code> с <code>.absolute</code>, игнорирует <code>tw:relative</code> внутри Tailwind-токенов. 721 использование <code>tw:*</code> в 79 файлах.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.111.0</span><span class="changelog-meta__date">21-03-2026</span></span><h2 id="v91110-21-03-2026--связь-не-рвётся-умный-reconnect-индикатор-соединения-и-аудит-доступности">Связь не рвётся: умный reconnect, индикатор соединения и аудит доступности</h2></summary> <div class="changelog-section__body"> <p>Что происходит, когда сервер пропадает на секунду? Раньше — DDP стучал в стену каждые 16 секунд, пока не сдавался, браузер заливал консоль ошибками, а пользователь ничего не знал. Теперь — платформа отступает плавно (exponential backoff с jitter), показывает точку-индикатор у колокольчика, а после восстановления сервера переподключается сама. Параллельно прошёл сквозной аудит доступности — 8 ARIA-исправлений в уведомлениях, 12 новых E2E-тестов и подсказка о свайпе для новых мобильных пользователей.</p> <ul> <li data-num="1251"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Exponential backoff для WebSocket</strong> — при обрыве соединения DDP переподключается с нарастающей паузой: 1с → 2с → 4с → … → 60с, плюс случайный разброс ±25%, чтобы тысяча клиентов не ударила в сервер одновременно. 8 попыток вместо 5 — и каждая тише предыдущей.</li> <li data-num="1252"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Индикатор соединения у колокольчика</strong> — маленькая точка рядом с иконкой уведомлений. Жёлтая пульсирующая — идёт переподключение, оранжевая — сервер недоступен (recovery), красная — соединение потеряно. Когда всё хорошо — точки нет. Мгновенный отклик через eventBus, fallback-проверка каждые 10 секунд.</li> <li data-num="1253"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Автовосстановление после даунтайма</strong> — если все 8 попыток reconnect исчерпаны, платформа переходит в recovery mode: периодические health-check&#39;и с нарастающим интервалом (1 → 5 мин). Сервер вернулся — соединение переинициализируется автоматически, без перезагрузки страницы.</li> <li data-num="1254"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Подсказка о свайпе на мобильных</strong> — при первом визите на страницу уведомлений с телефона появл
1яется аккуратная плашка: «Свайпните влево для удаления, вправо — для прочтения». Один раз — и больше не мешает. Работает даже в приватном режиме.</li> <li data-num="1255"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>12 E2E-тестов для уведомлений</strong> — Playwright проверяет вкладки, mark as read, expand/collapse, клавиатурную навигацию (j/k/x/стрелки), ARIA-разметку, «загрузить ещё» и свайп на мобильном. Тесты не прячут ошибки — если элемента нет, тест честно пропускается, а не молча проходит.</li> <li data-num="1256"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**aria-pressed</code> на кнопке чтения был перевёрнут** — в раскрытом списке деталей нажатие «прочитано» сообщало screen reader&#39;у обратное. Исправлено: pressed=true, когда уведомление прочитано, false — когда нет. На обоих уровнях (карточка и деталь) атрибут теперь всегда присутствует, а не удаляется при одном из состояний.</li> <li data-num="1257"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Desktop-счётчик уведомлений не объявлялся screen reader&#39;у</strong> — у бейджа в desktop-шапке отсутствовал <code>aria-label</code> (мобильная версия имела его с самого начала). Теперь паритет полный.</li> <li data-num="1258"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Anti-thrashing на нестабильных соединениях</strong> — если WebSocket подключился и тут же упал (менее 30 секунд), backoff-счётчик не обнуляется, а сохраняет половину набранного значения. Иначе цикл «подключился → отвалился → подключился → отвалился» превращался бы в агрессивный спам.</li> <li data-num="1259"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>LRU-кэш уведомлений терял слоты</strong> — при переключении прочитано/непрочитано старая запись удалялась из кэша, но оставалась в order-массиве. Со временем массив заполнялся призраками, и полезные записи вытеснялись вхолостую. Теперь обе структуры чистятся синхронно.</li> <li data-num="1260"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Recovery не крутился вечно</strong> — после исчерпания reconnect health-check&#39;и повторялись бесконечно. Добавлен лимит: 30 попыток (~45 мин), потом тишина. Вкладка не будет тихо грызть батарею ноутбука.</li> <li data-num="1261"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**aria-busy</code> и <code>role=&quot;status&quot;</code> на действиях с уведомлениями** — кнопка «Отправить» в inline-ответе, индикаторы обработки приглашений и запросов — все теперь объявляют свой статус screen reader&#39;у. Отправка идёт — пользователь знает.</li> <li data-num="1262"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Focus-visible для клавиатурной навигации</strong> — карточки уведомлений и ссылки внутри них получили синюю рамку при фокусе с клавиатуры. j/k/Tab — и всегда видно, где ты находишься.</li> <li data-num="1263"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Контраст категорий WCAG AA</strong> — цвета меток «Ремарки», «Приглашения», «Публикации» стали темнее (4.6–5.1:1 вместо 3.5–4.0:1), font-weight поднят до 600. Читаемо даже на 11px.</li> <li data-num="1264"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Индикатор плавно уходит</strong> — после успешного reconnect точка не исчезает мгновенно, а тает за 2 секунды. Меньше мельканий, больше уверенности.</li> <li data-num="1265"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Логотип не сдвигает контент</strong> — SVG-лого в шапке получил явные <code>width</code> и <code>height</code> — браузер резервирует место до загрузки изображения. Прощай, Cumulative Layout Shift.</li> <li data-num="1266"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Настройки уведомлений сохраняются на сервер</strong> — в профиле появилась вкладка «Уведомления» с таблицей 7 типов × 2 канала (сайт / email). Галочки → «Сохранить» → бэкенд запоминает. Email-дайджест уважает настройки — отключил «Ремарки по email» — письма не приходят.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.110.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91100-20-03-2026--звуки-уведомлений-бейджи-и-черновики-которые-никогда-не-потеряют-ваши-тексты">Звуки уведомлений, бейджи и черновики, которые никогда не потеряют ваши тексты</h2></summary> <div class="changelog-section__body"> <p>Перед выходом крупного релиза платформа прошла через три раунда аудита — фронтенд, бэкенд и Cloudflare Worker. Каждый файл с изменениями проверен на регрессии, утечки памяти и edge-case&#39;ы. Результат: уведомления стали ощутимее (звук, бейдж на вкладке), редактор — надёжнее (lock не застрянет, комментарии переживут сбой API), а Worker получил health check с пингом бэкенда и Prometheus-метрики для Grafana.</p> <ul> <li data-num="1236"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Звук при новом уведомлении</strong> — мягкий синтезированный тон (Web Audio API, без загрузки файлов) звучит, когда приходит новое уведомление или сообщение. По умолчанию выключен — включается кнопкой-динамиком на странице уведомлений. На фоновой вкладке молчит.</li> <li data-num="1237"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Бейдж непрочитанных на вкладке</strong> — заголовок страницы показывает <code>(5) Уведомления</code>, пока есть непрочитанные. Переключился на другую вкладку — видишь цифру без возврата. При уходе со страницы бейдж сбрасывается.</li> <li data-num="1238"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Deep health check</strong>
1 — <code>GET /api/health?deep</code> пингует бэкенд и показывает его статус и latency. Если API лежит — ответ <code>degraded</code> за 5 секунд вместо тишины. Обычный <code>/api/health</code> по-прежнему мгновенный.</li> <li data-num="1239"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тултипы на обрезанных карточках</strong> — навёл на заголовок или текст уведомления — видишь полную версию без клика. Особенно полезно для длинных названий статей на узких экранах.</li> <li data-num="1240"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Редактор не застрянет на загрузке</strong> — <code>try/finally</code> гарантирует сброс lock-флага <code>_collabInitInProgress</code> при любом исключении. Раньше редкий сбой сети мог навсегда заблокировать повторную инициализацию — теперь невозможно.</li> <li data-num="1241"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Комментарии переживают обрыв API</strong> — если загрузка комментариев или правок в боковой панели упала, через 5 секунд происходит автоматическая повторная попытка. DDP-слушатели теперь чистятся перед переподпиской — никаких дублей.</li> <li data-num="1242"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Черновик не потеряет код при гонке запросов</strong> — <code>draftCode</code> теперь генерируется атомарно (<code>$exists: false</code> в условии update). Два параллельных запроса за одним черновиком не перезапишут код друг друга. А при уникальном индексе collision — три попытки с новым кодом.</li> <li data-num="1243"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Prometheus стал полноценным</strong> — histogram buckets с <code>le</code>-лейблами для response time и API latency, status codes с <code>{code=&quot;200&quot;}</code>, handler counts с <code>{handler=&quot;unified-lcp&quot;}</code>. Grafana подключается без адаптеров.</li> <li data-num="1244"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Метрики не раздуваются</strong> — топ-пути считаются через O(1) счётчик вместо <code>Object.keys()</code> на каждый запрос. Handlers и handlerTimes ограничены 200 записями. Память Worker&#39;а под контролем на любом трафике.</li> <li data-num="1245"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Свайп на мобильных стал плавнее</strong> — <code>will-change: transform</code> на карточках уведомлений подсказывает браузеру использовать GPU. Разница заметна на устройствах со слабым CPU.</li> <li data-num="1246"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>CSP диагностирует нарушения</strong> — <code>&#39;report-sample&#39;</code> вернулся в <code>script-src</code>. И теперь первые 40 символов заблокированного кода попадают в CSP-отчёт — проблему видно в логах, не нужно гадать.</li> <li data-num="1247"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <code>**aria-pressed</code> на кнопке прочитано/непрочитано** — screen reader объявляет кнопку как toggle и её текущее состояние. Раньше приходилось догадываться.</li> <li data-num="1248"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Деплой Worker не ронит билд</strong> — ошибка публикации на Cloudflare больше не обнуляет Netlify-сборку (exit 0). HTML и JS доедут до пользователя, Worker можно задеплоить повторно. Для строгого режима — <code>WORKER_DEPLOY_STRICT=1</code>.</li> <li data-num="1249"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Метрики сохраняются между рестартами</strong> — при обращении к dashboard снимок метрик пишется в Cloudflare KV (если binding настроен). Рестарт isolate больше не обнуляет всю статистику.</li> <li data-num="1250"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Мёртвый код вычищен</strong> — удалён <code>patch-bootstrap-sass.js</code> (не использовался после перехода на bootstrap-compat), убран невалидный <code>.gitignore</code> для tracked файлов, добавлен паттерн для логов Cloudflare.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.109.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91090-20-03-2026--страж-вёрстки-playwright-следит-чтобы-ничего-не-поехало">Страж вёрстки: Playwright следит, чтобы ничего не поехало</h2></summary> <div class="changelog-section__body"> <p>Вы этого не увидите — и в этом весь смысл. Теперь каждое изменение в стилях, шаблонах и компонентах платформы проходит визуальный контроль: робот открывает ключевые страницы Дискурса в настоящем браузере, делает скриншоты и сравнивает с эталоном попиксельно. Если кнопка уползла, шрифт поменялся или подвал наехал на контент — мы узнаем об этом до того, как узнаете вы.</p> <ul> <li data-num="1232"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Playwright снимает страницы на desktop и mobile</strong> — главная, лента, поиск, «О проекте», шапка и подвал проверяются в двух viewport-ах (1280×720 и Pixel 5) плюс дополнительно на планшете (768×1024) и широком мониторе (1920×1080). Если вёрстка сломается хоть на одном — CI остановит деплой.</li> <li data-num="1233"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>CI ловит регрессии до мержа</strong> — GitHub Actions запускает тесты на каждый pull request, push в master и еженедельно по понедельникам. Diff-скриншоты и HTML-отчёт сохраняются как artifact — можно открыть и увидеть, что именно изменилось.</li> <li data-num="1234"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Кросс-платформенные baseline-ы</strong> — скриншоты хранятся отдельно для каждой ОС (Linux, Windows, macOS). Шрифты рендерятся по-разному на разных системах, и раньше это было бы источником ложных срабатываний — теперь нет.</li> <li data-num="1235"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Динамический контент не шумит</strong> — даты, аватарки, счётчики, lazy-картинки и cookie-баннеры автоматически скрываются перед скриншотом. Анимации и CSS-transitions замораживаются. Тесты стабильны — падают только на реальные поломки.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.108.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91080-20-03-2026--картинки-вернулись-экстренный-ремонт-thumbor">Картинки вернулись: экстренный ремонт Thumbor</h2></summary> <div class="changelog-section__body"> <p>Все изображения на сайте пропали разом — обложки статей, аватарки, иллюстрации. Сервер картинок (Thumbor) отдавал 504, а при более глубоком копании — 500. Причина окаÐ
1·Ð°Ð»Ð°ÑÑŒ двойной: сломался механизм загрузки из S3 и одновременно слишком строгий лимит на размер изображений не давал обработать обложки. Проблема устранена, образ сохранён, откат невозможен.</p> <ul> <li data-num="1229"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>S3 снова отвечает</strong> — библиотека <code>tornado_botocore</code> обращалась к удалённому атрибуту <code>Endpoint.verify</code> в botocore, из-за чего каждый запрос к хранилищу падал с <code>AttributeError</code>. Патч в одну строку (<code>getattr</code> с fallback) вернул доступ ко всему бакету — а значит, ко всем картинкам платформы.</li> <li data-num="1230"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Большие обложки больше не отвергаются</strong> — лимит пикселей в Thumbor (<code>MAX_PIXELS</code>) был 30 млн, а свежие обложки — до 37,5 млн (7500×5000). PIL считал это «decompression bomb» и молча отбрасывал файл. Лимит поднят до 75 млн — хватит даже для фотобанковских исходников.</li> <li data-num="1231"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Патчи переживут любой рестарт</strong> — оба исправления сохранены через <code>docker commit</code> в образ <code>discours/thumbor:patched</code>, а резервная копия образа лежит на сервере. Ни <code>docker-compose down</code>, ни перезагрузка сервера не откатят фикс.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.107.1</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91071-20-03-2026--живые-уведомления-ddp-подписки-мгновенный-отклик-и-real-time-уведомления">Живые уведомления: DDP-подписки, мгновенный отклик и real-time уведомления</h2></summary> <div class="changelog-section__body"> <p>WebSocket подключен — и мы сразу пошли дальше. Теперь комментарии в редакторе, сообщения в чате и уведомления летят через DDP в реальном времени — без polling, без перезагрузки. Сервер пушит изменения мгновенно, а при разрыве соединения всё восстанавливается автоматически. Консоль наконец-то чистая.</p> <ul> <li data-num="1219"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Комментарии в редакторе обновляются мгновенно</strong> — DDP-подписка на InlineComments пушит новые комментарии и ремарки соавторов за 200 мс. Больше не нужно обновлять страницу, чтобы увидеть чужой комментарий.</li> <li data-num="1220"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Новое сообщение — toast в любом месте платформы</strong> — при поступлении сообщения через DDP появляется деликатное уведомление «Новое сообщение». На странице чата и уведомлений toast не показывается — там данные и так на виду.</li> <li data-num="1221"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Сообщения в чате через DDP</strong>
1 — <code>subscribeDDPConversation(id)</code> подписывает на конкретный диалог с пагинацией. Готово для интеграции в контроллер чата — сообщения собеседника будут появляться без refresh.</li> <li data-num="1222"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Соединение восстанавливается само</strong> — при реконнекте WebSocket все DDP-подписки (messages, inlineComments) автоматически пересоздаются. Старые слушатели гарантированно чистятся перед переподпиской — никаких дублей-призраков. Раньше после разрыва данные переставали приходить до перезагрузки.</li> <li data-num="1223"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>HocusPocus не спамит консоль</strong> — удалён несуществующий параметр <code>quiet: true</code>, добавлен capture-phase error handler, который перехватывает браузерные ошибки WebSocket. При недоступном collab-сервере в консоли — тишина вместо бесконечных красных строк.</li> <li data-num="1224"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Свайп уведомлений не залипает</strong> — <code>releasePointerCapture</code> в <code>onPointerCancel</code> освобождает touch после системных жестов (pull-to-refresh, переключение приложений). Больше никаких «замороженных» карточек.</li> <li data-num="1225"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кэш уведомлений не раздувается</strong> — при переключении прочитано/непрочитано orphaned запись с противоположным ключом удаляется из LRU-кэша. Память под контролем даже при тысячах уведомлений.</li> <li data-num="1226"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Фоновая вкладка не грузит сеть</strong> — при скрытой вкладке DDP-события от InlineComments копятся и выполняются одним refresh через 5 секунд вместо 200 мс. Меньше запросов, меньше CPU — ноутбук не греется.</li> <li data-num="1227"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ошибки DNS, отказов и таймаутов подавляются</strong> — asteroid.js теперь перехватывает <code>ERR_CONNECTION_REFUSED</code> и <code>ERR_CONNECTION_TIMED_OUT</code> помимо DNS. Чистая консоль на localhost без запущенного бэкенда.</li> <li data-num="1228"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Regex-валидация conversationId</strong> — DDP-публикация messages принимает только alphanumeric ID (1–64 символа). Спецсимволы, операторы и null-байты отсекаются до запроса в MongoDB.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.107.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91070-20-03-2026--websocket-без-компромиссов-настоящий-real-time-вместо-тихого-fallbackа">WebSocket без компромиссов: настоящий real-time вместо тихого fallback&#39;а</h2></summary> <div class="changelog-section__body"> <p>Все уведомления, комментарии и коллаборативный редактор работают через WebSocket — мгновенно, без задержек. Но окаÐ
1·Ð°Ð»Ð¾ÑÑŒ, что на проде WebSocket молча деградировал в long-polling: HTTP/2 на nginx обрезал заголовки Upgrade (RFC 7540), браузер не мог установить соединение и заваливал консоль ошибками. Пользователь этого не видел — просто уведомления приходили чуть позже, а совместное редактирование подтормаживало. Теперь всё работает как задумано.</p> <ul> <li data-num="1214"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>WebSocket наконец-то WebSocket</strong> — корневая причина найдена и устранена: HTTP/2 считает заголовки <code>Upgrade</code> и <code>Connection</code> запрещёнными (hop-by-hop), поэтому nginx их отбрасывал. Meteor получал обычный HTTP-запрос и переключался на SockJS — медленный, ненадёжный полинг. Теперь HTTP/2 отключён для API через штатную переменную nginx‑proxy <code>ENABLE_HTTP2=false</code>. Соединение устанавливается за один handshake, данные летят без задержек.</li> <li data-num="1215"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Консоль больше не кричит</strong> — раньше при проблемах с сервером в DevTools появлялось море ошибок <code>WebSocket connection failed</code>. Теперь перед созданием WebSocket выполняется HTTP health‑check: если сервер недоступен — соединение просто не создаётся. Ноль ошибок, ноль шума.</li> <li data-num="1216"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>5 попыток вместо бесконечности</strong> — DDP-клиент раньше переподключался вечно, генерируя ошибку каждые 16 секунд. Теперь — максимум 5 попыток (~40 сек), потом тишина. Через минуту health‑check проверяет сервер, и при восстановлении DDP переинициализируется автоматически.</li> <li data-num="1217"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Zero-downtime при деплое</strong> — добавлены директивы <code>proxy_next_upstream</code> в nginx: если новый контейнер при деплое ещё не готов, запрос автоматически уходит на старый. Пользователь не увидит ни 502, ни 503 — даже в момент обновления.</li> <li data-num="1218"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Конфиг, который не теряется</strong> — WebSocket-настройки nginx (заголовки Upgrade, 24-часовые таймауты, blue-green retry) теперь хранятся в репозитории и автоматически монтируются при каждом деплое. Ручная правка на сервере больше не нужна — и не пропадёт при пересоздании контейнера.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.106.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91060-20-03-2026--ремарки-с-обратной-связью-ответы-авторов-статусы-и-умная-фильтрация-уведомлений">Ремарки с обратной связью: ответы авторов, статусы и умная фильтрация уведомлений</h2></summary> <div class="changelog-section__body"> <p>Ремарки перестали быть односторонними. Теперь можно отвечать на предложения прямо на странице статьи — без редактора, мгновенно. А читатели видят статус своих предложений: «Принято» или «Закрыто». Уведомления о ремарках и правках разделились на входящие и исходящие , ориентироваться в текстах в работе стало проще.</p> <ul> <li data-num="1205"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ответ автора прямо под ремаркой</strong>
1 — форма «Ответить» появляется у каждой ремарки для автора материала. Ctrl+Enter — отправить, Esc — отменить. Ответ появляется мгновенно (оптимистичный UI), а если сервер откажет — текст вернётся в форму. Ничего не потеряется.</li> <li data-num="1206"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Статус ремарки виден читателю</strong> — бейдж «✓ Принято» (зелёный) или «Закрыто» (серый) у ремарки, которую автор рассмотрел. Больше не нужно гадать, дошло ли предложение.</li> <li data-num="1207"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Подтабы «К моим материалам» и «Мои ремарки / Мои правки»</strong> — вкладки уведомлений «Ремарки» и «Правки» разделены на два уровня: «К моим» — кто оставил ремарку на вашей статье, «Мои» — ответили ли на вашу к чужой. Бейдж со счётчиком на каждом подтабе.</li> <li data-num="1208"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Открыть в редакторе» у каждой ремарки</strong> — для авторов появилась ссылка с иконкой пера, открывающая редактор сразу с прокруткой к нужному месту в тексте. Удобно, когда ремарок много и нужно разобрать их по очереди.</li> <li data-num="1209"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Ремарка во вьюпорте — уведомление прочитано автоматически</strong> — переходишь по ссылке из уведомления, ремарка появляется на экране — статус меняется сам через IntersectionObserver. Никаких лишних тапов.</li> <li data-num="1210"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Пустой подтаб «Мои ремарки» — с контекстом</strong> — если ответов на ваши ремарки ещё нет, показывается осмысленное «Ответов на ваши ремарки пока нет», а не общее «Нет уведомлений». Иконка тоже меняется под контекст.</li> <li data-num="1211"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ответы к ремаркам грузятся лениво</strong> — дополнительный API-запрос за replies отправляется только при первом открытии вкладки «Ремарки», а не при каждой загрузке страницы статьи. Меньше трафика, быстрее открытие.</li> <li data-num="1212"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Фильтрация уведомлений: один O(N) проход</strong> — подтабы и счётчики вычисляются в едином цикле с O(1)-поиском по предвычисленному lookup вместо вложенных обходов. Отзывчивость страницы уведомлений не зависит от объёма данных.</li> <li data-num="1213"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Aria-разметка, role=&quot;form&quot;, aria-live</strong> — форма ответа, секция ремарок и блок статуса корректно описаны для screen reader; бейдж статуса объявляется при изменении. Анимации уважают prefers-reduced-motion.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.105.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91050-20-03-2026--черновиками-можно-делиться-с-первых-слов--с-открытия-редактора">Черновиками можно делиться с первых слов — с открытия редактора</h2></summary> <div class="changelog-section__body"> <p>Черновик теперь рождается готовым к жизни. Раньше, чтобы пригласить соавтора, нужно было сначала вручную сохранить материал. Теперь достаточно открыть редактор — черновик создаётся на сервере автоматически, коллаборативный режим включается мгновенно, ссылку можно отправить ещё до первой написанной строчки. Параллельно редактор научился показывать статус автосохранения, URL стал умнее, а orphan-черновики больше не засоряют базу.</p> <ul> <li data-num="1194"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Мгновенная коллаборация без ручного сохранения</strong> — при открытии редактора для нового материала черновик создаётся на сервере за одну сетевую ходку, HocusPocus подключается сразу. Кнопка «Пригласить» видна с первой секунды — можно звать соавторов, не дожидаясь ни заголовка, ни категории.</li> <li data-num="1195"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Индикатор автосохранения в шапке редактора</strong> — рядом с бейджем «Черновик» теперь живой статус: «Сохранение…» со спиннером, зелёная галочка «Сохранено» (тает через 3 секунды), оранжевая точка «Не сохранено». Автор впервые видит, что его текст дошёл до сервера — как в Google Docs.</li> <li data-num="1196"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>URL обновляется при выборе категории</strong> — пока рубрика не выбрана, адрес выглядит как <code>/edit/articles/draft/slug</code>. Выбрал «Медиа» — URL мгновенно превращается в <code>/edit/articles/media/slug</code>. Ссылка, которой делишься, всегда актуальна.</li> <li data-num="1197"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Категория не обязательна для черновика</strong> — сохраняй заголовок, теги и обложку без выбора рубрики. Категория потребуется только при публикации — когда автор уже точно знает, куда относится материал.</li> <li data-num="1198"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка «Сохранить черновик» больше не молчит</strong> — раньше после auto-draft кнопка выглядела рабочей, но нажатие ничего не делало. Теперь при клике метаданные сохраняются через REST PUT, даже если collab-versioning ещё не инициализирован.</li> <li data-num="1199"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойная инициализация редактора невозможна</strong> — lock-механизм <code>_collabInitInProgress</code> обёрнут в <code>try/finally</code> — Tiptap создаётся ровно один раз, а lock гарантированно сбрасывается даже при сетевом сбое. Никаких зависших загрузок.</li> <li data-num="1200"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Навигация прочь не ломает редактор</strong> — если пользователь уходит со страницы, пока auto-draft ещё в полёте, promise-chain корректно останавливается через <code>$scope.$$destroyed</code>. Никаких ошибок в консоли, никаких зомби-редакторов.</li> <li data-num="1201"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>localStorage не подсовывает призраков</strong> — после создания auto-draft кэш предыдущей сессии очищается. При перезагрузке пользователь видит свежий черновик, а не остатки вчерашней статьи.</li> <li data-num="1202"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>
1Orphan-черновики удаляются автоматически</strong> — крон-задача каждые 6 часов находит пустые черновики с заголовком «Без названия» старше 7 дней и удаляет их. Пользователь открыл редактор и ушёл — мусор уберётся сам.</li> <li data-num="1203"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Индекс для дедупликации черновиков</strong> — partial index <code>autoDraft_dedup</code> покрывает запрос поиска существующего пустого черновика. Работает только для draft-документов — запись опубликованных статей не замедляется.</li> <li data-num="1204"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>16 unit-тестов для auto-draft</strong> — <code>persistCollabMetadata</code> с категорией и без, <code>saveDraft</code> для трёх сценариев, <code>saveDraftBtnClick</code> с тремя ветками, публикация. Все 820 тестов проекта проходят, 0 регрессий.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.104.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91040-20-03-2026--прочитано-или-нет-управление-статусом-уведомлений">Прочитано. Или нет: управление статусом уведомлений</h2></summary> <div class="changelog-section__body"> <p>Раньше единственный способ пометить уведомление прочитанным — перейти по ссылке. Непрочитанным — никак. Теперь у каждой карточки и у каждого отдельного уведомления в раскрытом списке появилась кнопка-конвертик: нажал — прочитано, нажал ещё раз — снова непрочитано. Работает мгновенно, ошибки не замалчиваются, а свайп на мобильных стал двусторонним.</p> <ul> <li data-num="1181"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопка «прочитано / непрочитано» на каждой карточке</strong> — конвертик у правого края карточки. Открытый — непрочитано, закрытый — прочитано. Один клик переключает состояние, не уводя со страницы. На кнопке — мини-бейдж с числом непрочитанных (если больше одного).</li> <li data-num="1182"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Индивидуальный toggle в раскрытом списке</strong> — развернул карточку «Показать все» — каждая ремарка, комментарий или правка имеет свой конвертик. Можно пометить прочитанным одно конкретное уведомление, не трогая остальные.</li> <li data-num="1183"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Свайп вправо теперь работает в обе стороны</strong> — на мобильных свайп вправо раньше только помечал прочитанным. Теперь — переключает: прочитанное становится непрочитанным и наоборот. Индикатор под карточкой тоже динамический.</li> <li data-num="1184"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопка всегда видна</strong> — не прячется за hover, как «Удалить» и «Отписаться». Полупрозрачная в покое, яркая при наведении, синяя для непрочитанных. Это главное действие — оно должно быть на виду.</li> <li data-num="1185"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>
1Pulse-отклик при нажатии</strong> — кнопка кратко увеличивается при клике. Мгновенная визуальная обратная связь, пока сервер думает.</li> <li data-num="1186"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Заголовок не дублируется</strong> — если текст уведомления уже содержит название материала (как у правок: «предложил 6 правок в материале Черновик»), отдельный заголовок сверху прячется. Название в теле — жирным со ссылкой на публикацию. Чище, компактнее, без повторов.</li> <li data-num="1187"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Порядок кнопок по частоте использования</strong> — конвертик (основное действие) ближе всего к краю, удаление и отписка — глубже. Рука и курсор тянутся к нужному первым.</li> <li data-num="1188"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Иконка конвертика была невидима</strong> — <code>fa-envelope-open</code> отсутствовал в подмножестве шрифтов. Заменён на <code>fa-envelope-open-text</code>, который и выразительнее, и есть в наборе.</li> <li data-num="1189"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Тултипы не обрезаются</strong> — подсказки всех кнопок карточки выравниваются по правому краю и не вылезают за границу.</li> <li data-num="1190"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мобильный текст не залезает под кнопки</strong> — padding-right карточек и деталей пересчитан под три кнопки с 44px touch-target.</li> <li data-num="1191"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Все анимации уважают <code>prefers-reduced-motion</code></strong> — pulse-отклик и transition кнопок отключаются при настройке «Уменьшить анимацию». Ни один новый элемент не нарушает этот контракт. <code>aria-pressed</code> на toggle-кнопке — screen reader озвучивает состояние.</li> <li data-num="1192"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тултип на длинных заголовках</strong> — при наведении на обрезанный заголовок или текст карточки виден полный текст. Удобно на узких экранах, где название не помещается.</li> <li data-num="1193"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Optimistic UI не мигает</strong> — набор <code>_pendingReadIds</code> предотвращает визуальный откат, если Asteroid обновит данные быстрее, чем сервер подтвердит операцию. Нажал — готово, без дёрганий.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.103.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91030-20-03-2026--формы-входа-надёжные-уведомления-и-мгновенные-черновики">Формы входа, надёжные уведомления и мгновенные черновики</h2></summary> <div class="changelog-section__body"> <p>Две большие темы в одном релизе. Авторизация на Дискурсе перестала быть лотереей «нажми и жди» — формы входа получили inline-валидацию, показ пароля и автофокус. А система уведомлений прошла через капитаÐ
1»ÑŒÐ½Ñ‹Ð¹ ремонт: удалённые уведомления теперь можно вернуть, операции стали быстрее в разы, а создание черновика для соавторства — безопаснее и умнее.</p> <ul> <li data-num="1159"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Inline-валидация во всех формах авторизации</strong> — ошибки появляются под конкретным полем сразу при уходе фокуса, а исчезают мгновенно при исправлении. Больше не нужно жать «Войти», чтобы узнать, что забыл ввести пароль.</li> <li data-num="1160"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Показать пароль</strong> — иконка-глаз в полях пароля на всех четырёх формах: вход, регистрация, восстановление, paywall-логин. Одно нажатие — видно, что набрал; повторное — скрыто обратно.</li> <li data-num="1161"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Индикатор сложности пароля при смене</strong> — страница «Новый пароль» получила тот же индикатор (слабый / средний / сильный), что и регистрация. Автор видит надёжность нового пароля до сохранения, а не после отказа сервера.</li> <li data-num="1162"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Автофокус при открытии и переключении</strong> — модалка ставит курсор в первое поле при открытии, переключении входа ↔ регистрации, переходе в «Забыли пароль?» и обратно. Клавиатура — сразу в деле.</li> <li data-num="1163"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Email уже зарегистрирован — войдите»</strong> — при попытке зарегистрироваться с уже существующим адресом вместо сухого текста появляется кликабельная ссылка «войдите», которая переключает на форму входа и подставляет email. Ноль лишних действий.</li> <li data-num="1164"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Email автоматически тримится</strong> — пробелы в начале и конце адреса убираются при уходе фокуса и при отправке. Скопировал адрес с пробелом из письма — форма разберётся сама.</li> <li data-num="1165"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Пароль очищается при закрытии модалки</strong> — раньше набранный пароль оставался в поле до следующего открытия. Теперь — сброс вместе с остальными данными.</li> <li data-num="1166"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ошибки не залипают между формами</strong> — переключение «Вход → Забыли пароль? → Назад» полностью сбрасывает ошибки, touched-состояния, видимость пароля. Никаких призраков из предыдущей формы.</li> <li data-num="1167"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Убрана задержка ввода на странице смены пароля</strong> — старый <code>ng-model-options</code> с debounce 1 сек и обновлением только на blur заменён на стандартное поведение. Пароль теперь реагирует на каждый символ.</li> <li data-num="1168"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка «Сохранить» блокируется при ошибках</strong> — на странице смены пароля кнопка неактивна, пока есть ошибки валидации (короткий пароль, несовпадение). Раньше можно было Ð
1½Ð°Ð¶Ð°Ñ‚ÑŒ и получить серверную ошибку.</li> <li data-num="1169"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>maxlength на всех полях</strong> — email (254), пароль (128) ограничены во всех формах, включая paywall-логин и страницу смены пароля. Проверка сложности пароля дополнительно ограничена 256 символами на уровне JS — защита от exotic-input атак.</li> <li data-num="1170"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>aria-invalid, aria-describedby, role=&quot;alert&quot;</strong> — каждое поле с валидацией связано с ошибкой семантически. Screen reader озвучивает ошибку сразу при появлении, не дожидаясь submit.</li> <li data-num="1171"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Константы валидации вынесены в начало контроллера</strong> — <code>EMAIL_REGEX</code> и <code>MIN_PASSWORD_LENGTH</code> объявлены до первого использования, а не в конце файла. Код стал безопаснее для рефакторинга.</li> <li data-num="1172"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Inline-логика в шаблонах заменена методами</strong> — кнопки «Забыли пароль?» и «Я знаю пароль!» теперь вызывают <code>showForgotPassword()</code> / <code>hideForgotPassword()</code> вместо мутации <code>$ctrl.passwordForgot</code> прямо в HTML. Сброс состояния — в одном месте.</li> <li data-num="1173"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Отменить удаление» уведомлений — по-настоящему</strong> — свайп-удаление теперь использует soft delete: уведомления не стираются, а прячутся на 30 дней. Нажал «Отменить» в тосте — они возвращаются. Раньше undo работал только до таймаута в 5 секунд, а потом данные терялись навсегда.</li> <li data-num="1174"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Черновик для соавторства создаётся мгновенно</strong> — новый API auto-draft находит или создаёт пустой черновик нужного типа за один запрос. Если у автора уже есть незаполненный черновик — используется он, а не плодится дубликат. Лимит — 50 черновиков на автора, чтобы база не засорялась.</li> <li data-num="1175"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Пометить прочитанными пачкой, а не по одному</strong> — новые batch-методы <code>markManyAsRead</code>/<code>markManyAsUnread</code> обрабатывают до 100 уведомлений одним запросом вместо сотни отдельных. Меньше трафика — быстрее отклик.</li> <li data-num="1176"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Бейдж непрочитанных без тяжёлой подписки</strong> — REST-эндпоинт <code>/api/v2/notifications/unread-count</code> отдаёт число непрочитанных одним GET-запросом. Для шапки сайта на лёгких страницах хватит polling раз в минуту вместо полноценной DDP-подписки.</li> <li data-num="1177"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Ошибки при переключении прочитано/непрочитано не молчат</strong> — если сервер не ответил, пользователь видит toast «Не удалось обновить статус уведомления» вместо молчаливого отката. Исправлено для всех трёх вариантов: карточка, детальный список, свайп.</li> <li data-num="1178"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Непрочитано» фильтрует по типу, как и «Прочитано»</strong> — метод <code>markAsUnread</code> получил опциональный фильтр <code>documentCollection</code>. Раньше при нажатии на вкладке «Комментарии» непрочитанными становились все уведомления по материалу, включая приглашения и ремарки.</li> <li data-num="1179"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Auto-draft защищён от спама и коллизий</strong> — rate limiter (10 запросов/мин) + лимит черновиков (50 на автора) + валидация типа контента. При редчайшем совпадении <code>draftCode</code> — три автоматических повторных попытки с новым кодом вместо ошибки 500.</li> <li data-num="1180"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Удалённые уведомления не нагружают базу</strong> — TTL-индекс автоматически вычищает soft-deleted записи через 30 дней. Ни cron-задач, ни ручной очистки — MongoDB делает это сама.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.102.0</span><span class="changelog-meta__date">20-03-2026</span></span><h2 id="v91020-20-03-2026--рентген-для-платформы-мониторинг-workerа-подкасты-в-поиске-лёгкий-bootstrap-и-автодеплой">Рентген для платформы: мониторинг Worker&#39;а, подкасты в поиске, лёгкий Bootstrap и автодеплой</h2></summary> <div class="changelog-section__body"> <p>Платформа научилась смотреть на себя изнутри. Cloudflare Worker получил собственный dashboard с метриками в реальном времени — впервые можно увидеть, сколько мусорного трафика блокируется, как работает кэш, какие боты приходят и что ломается. Dashboard прошёл через два раунда улучшений: sparkline-графики, алерты, трекинг платежей, latency бэкенда, per-handler timing, Prometheus-экспорт. Подкасты впервые попали в sitemap — 195 эпизодов пяти серий стали видимы для Google, а вся генерация sitemap ускорена вдвое. Параллельно — Bootstrap похудел, скрипты вычищены, деплой Worker&#39;а перестал быть ручной операцией. Отдельный проход по боевым логам Cloudflare вычистил 15 источников ошибок: от CSP-нарушений до race conditions в UI — платформа стала заметно стабильнее и отзывчивее.</p> <ul> <li data-num="1119"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Dashboard метрик Worker&#39;а</strong> — по адресу <code>/_/dashboard</code> появился полноценный мониторинг: cache hit/miss с процентом попаданий, счётчик заблокированных сканеров и мусорных URL, разбивка ботов по типам (соцсети vs поисковики), гистограмма времени ответа, последние 404 и ошибки, топ путей и handler&#39;ов. Тёмный интерфейс, автообновление каждые 10 секунд, защита секретным ключом. Проблему видишь раньше, чем о ней напишут.</li> <li data-num="1120"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Sparklines и живые тренды</strong> — RPS и cache hit рисуются как мини-графики за последние 10 минут. Всплеск нагрузки или провал кэша заметен с одного взгляда — без пристального изучения цифр. Под капотом 60 бакетов по 10 секунд, обновляются в реальном времени.</li> <li data-num="1121"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Алерты прямо на dashboard</strong> — семь условий автоматически анализируют метрики: error rate выше порога, кэш просел, API таймаутит, Stripe сбоит. Цветные баннеры вверху экрана и статус-точка (зелёная/жёлтая/красная) дают ответ за секунду — «всё нормально» или «пора разбираться».</li> <li data-num="1122"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Per-handler timing</strong> — таблица с P50/P95 для каждого типа страниц: статьи, теги, авторы, подкасты, OG-инъекции. Ð
1’идно, какие страницы самые медленные, и где оптимизация даст наибольший эффект.</li> <li data-num="1123"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>API latency бэкенда</strong> — отдельная гистограмма и таблица endpoint&#39;ов показывают скорость бэкенда (homepage, content, author, tag, categories). Когда сайт тормозит — сразу понятно, проблема в edge или в API.</li> <li data-num="1124"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Prometheus endpoint</strong> — <code>&amp;prometheus</code> отдаёт метрики в формате Prometheus exposition с histogram buckets (<code>le</code>-лейблы для response time и API latency), status codes (<code>{code=&quot;200&quot;}</code>), per-handler counts. Подключить Grafana — дело одного URL.</li> <li data-num="1125"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>JSON API для мониторинга</strong> — <code>&amp;json=1</code> отдаёт чистый JSON. Можно подключить Telegram-бота или простой скрипт — данные готовы для автоматизации.</li> <li data-num="1126"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Метрики кэша стали точными</strong> — раньше трекинг стоял только в двух точках (404-кэш и resilience), а 12 handler&#39;ов отдавали edge-кэш без подсчёта. Теперь каждый <code>cache.match()</code>/<code>cache.put()</code> во всех обработчиках (статьи, теги, авторы, подкасты, OG, боты) честно считает hit/miss/expired. Cache hit rate на dashboard — реальный.</li> <li data-num="1127"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Dashboard обновляет всё, а не только числа</strong> — auto-refresh каждые 10с теперь перерисовывает таблицы статусов, top paths, 404, ошибки, timing, sparklines. Раньше половина данных замерзала на первом рендере.</li> <li data-num="1128"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Rate limit на авторизацию dashboard</strong> — пять неудачных попыток с одного IP блокируют доступ на 5 минут. Brute-force секрета стал бессмысленным.</li> <li data-num="1129"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>topPaths не замерзают</strong> — каждые 5 минут counts делятся пополам, редкие пути вытесняются горячими. Через час работы dashboard показывает актуальную картину, а не фото первых минут после деплоя.</li> <li data-num="1130"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Деплой Worker&#39;а одной командой</strong> — <code>node cloudflare/workers/deploy.js</code> автоматически собирает и публикует Worker через Cloudflare API. Вместо ручного копирования 4000 строк в браузерную консоль — одна команда в терминале.</li> <li data-num="1131"><span class="changelog-tag changelog-tag--DEVOPS" data-tag-alias="DEVOPS">DEVOPS</span> <strong>Платежи под наблюдением</strong> — Stripe checkouts, ошибки и email-уведомления о донатах теперь трекаются. Если платёж не проходит — видно сразу, не через день из письма донора.</li> <li data-num="1132"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Bootstrap похудел вдвое</strong> — из полного набора (все компоненты, все утилиты) оставлена только сетка, формы и визуально необходимое. jQuery-плагины (modal, collapse, tooltip) заменены ванильными микробиблиотеками по 3–5 КБ — без jQuery вообще.</li> <li data-num="1133"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уведомления о правках: категория-метка</strong> — карточки уведомлений типа <code>suggestions</code> теперь показывают метку «Правки» с правильным цветом, а не безымянный пустой бейдж. Автор сразу видит, что это за тип.</li> <li data-num="1134"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Подкасты на карте Google</strong> — все пять подкастов Дискурса (195 эпизодов) попали в sitemap. Каждый эпизод — отдельный URL с датой публикации. Поисковики впервые видят подкаст-контент как полноценные страницы, а не чёрную дыру за SPA-роутером. Это +201 URL к общему покрытию.</li> <li data-num="1135"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Свежие эпизоды индексируются за часы</strong> — динамический sitemap <code>sitemap-recent</code> теперь парсит RSS-фиды Mave Digital и подставляет эпизоды, вышедшие за последние 30 дней. Выпустил эпизод — Google узнает в тот же день, не через две недели.</li> <li data-num="1136"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Сборка sitemaps ускорена вдвое</strong> — статьи (5 категорий), экспо (4 типа), авторы и теги загружаются параллельно, а не по очереди. Было ~28 секунд — стало ~13. Билд быстрее, CI дешевле.</li> <li data-num="1137"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>API не отвечает — не беда</strong> — три автоматических retry с exponential backoff (1→2→4 с) для 5xx, 429, таймаутов и разрывов соединения. Единичный сбой API больше не обнуляет sitemap целиком.</li> <li data-num="1138"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Дублей подкаст-URL не будет</strong>
1 — эпизоды с одинаковыми названиями (ремастеры, части) дедуплицируются по slug. Поисковик не увидит двойных записей.</li> <li data-num="1139"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Валидация sitemap не ломается на подкастах</strong> — скрипт <code>validate-sitemap-urls.js</code> распознаёт <code>/podcasts/*</code> и не пытается проверить их через API (эпизоды живут в RSS, не в базе). Раньше <code>/podcasts</code> ложно считался несуществующим профилем автора.</li> <li data-num="1140"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**staticPageSlugs</code> больше не рассинхронизируется** — валидатор sitemap теперь импортирует список известных путей из <code>known-paths.js</code>, а не хранит собственный устаревший хардкод. Добавил маршрут — он сразу учтён.</li> <li data-num="1141"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Шесть новых страниц в sitemap</strong> — <code>/almanac</code>, <code>/donate</code>, <code>/join</code>, <code>/collab</code>, <code>/idea</code> и ещё одна страница добавлены в статический sitemap с корректным <code>changefreq</code>. Общее покрытие — 4967 URL.</li> <li data-num="1142"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Каждая страница знает, как часто меняется</strong> — все 35 статических URL получили явный <code>changefreq</code> (от <code>daily</code> для главной до <code>yearly</code> для конституции). Раньше у девяти страниц параметр отсутствовал, и Google угадывал сам.</li> <li data-num="1143"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Итог сборки видно с одного взгляда</strong> — финальная строка генератора показывает разбивку по типам (<code>articles: 1872, expo: 1113, authors: 1135, tags: 612, podcasts: 201</code>) и время выполнения. Sanity check автоматически предупредит, если какой-то тип контента подозрительно мал.</li> <li data-num="1144"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Sitemap-генератор не падает на Node 20</strong> — <code>generate-sitemaps.js</code> использовал устаревшие API (<code>url.parse</code>) и невалидные <code>require</code> в ESM-контексте. Переписан на чистый ESM с <code>URL()</code>, работает стабильно на Node 18–22.</li> <li data-num="1145"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Sync дизайн-токенов перестал ломать CI</strong> — <code>sync-design-tokens.js</code> получил корректную обработку путей на Windows и более строгий парсинг — больше не даёт ложных срабатываний при проверке <code>variables.scss ↔ tailwind.css</code>.</li> <li data-num="1146"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>AngularJS больше не ссорится с CSP</strong> — 123 ошибки <code>eval</code> в логах Cloudflare оказались — AngularJS использует <code>new Function()</code> для компиляции выражений. Добавлен <code>&#39;unsafe-eval&#39;</code> в <code>script-src</code> — страницы перестали терять функциональность из-за блокировки скриптов.</li> <li data-num="1147"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>RSS-фид 304 больше не кричит «ошибка»</strong> — браузер присылает <code>If-None-Match</code>, бэкенд честно отвечает «ничего не изменилось». Раньше Worker записывал это как ошибку — 10 ложных тревог в час. Теперь логи чисты.</li> <li data-num="1148"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**/offline.html</code> перестал притворяться автором** — Worker принимал любой single-segment путь за профиль. Файлы с расширениями (.html, .ico, .js и др.) теперь распознаются до API-запроса — меньше ненужных обращений к бэкенду, быстрее ответ.</li> <li data-num="1149"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Range-запросы не ломают кэш</strong> — браузер иногда просит часть страницы (206 Partial Content), а Cloudflare Cache API такое не принимает. Статус нормализуется до 200 — кэш работает, ошибки исчезли.</li> <li data-num="1150"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Черновик не просочится в кэш при сбое API</strong> — если бэкенд отвечает ошибкой, Worker больше не пытается проверить «а это черновик?» на объекте без нужных полей. Публичный контент остаётся публичным, черновики — приватными.</li> <li data-num="1151"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Автокомплит: клик по результату больше не теряется</strong> — раньше blur input&#39;а мог закрыть dropdown на 1 мс раньше, чем долетал клик по варианту. Теперь mousedown на списке отменяет таймер закрытия — выбор срабатывает с первого раза.</li> <li data-num="1152"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>API-запросы получили второй шанс</strong> — если бэкенд не ответил за 15 секунд, GET-запросы автоматически повторяются с укороченным таймаутом (8с). Кратковременный сбой — не повод показывать ошибку; при стабильном отказе пользователь всё равно увидит SPA.</li> <li data-num="1153"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Фильтры запомнят горячее</strong> — три текстовых фильтра (markdown, mention, HTML-обрезка) перешли с FIFO на LRU-кэш. Комментарий, который мелькает на каждом digest-цикле, теперь не вытесняется случайным новичком — меньше пересчётов, плавнее скролл.</li> <li data-num="1154"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Tailwind собирается быстрее</strong> — из сканируемых путей убран каталог <code>cloudflare/</code> — Worker не генерирует UI-классы, а сканирование 4000 строк JS отнимало время на каждой пересборке.</li> <li data-num="1155"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Поиск…» в автокомплите</strong> — пока результаты летят с сервера, dropdown показывает «Поиск…» вместо пустоты. Пользователь видит, что система работает, а не зависла.</li> <li data-num="1156"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>ReDoS в фильтре HTML-обрезки закрыт</strong> — regex&#39;ы для удаления <code>&lt;figure&gt;</code>, <code>&lt;style&gt;</code> и других тегов получили границу 50 КБ. Сколь угодно вложенный мусорный HTML больше не заставит браузер задуматься на несколько секунд.</li> <li data-num="1157"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>IPv6 unique-local полностью закрыт</strong> — SSRF-проверка CSP-вебхука покрывала <code>fc00::/8</code>, но пропускала <code>fd00::/8</code>. Теперь обе половины fc00::/7 блокируются — defense-in-depth, как положено.</li> <li data-num="1158"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Одновременный сбой нескольких чанков — одна перезагрузка</strong> — раньше если три скрипта ломались разом, браузер мог дёрнуть reload трижды. Теперь флаг <code>_reloadScheduled</code> гарантирует ровно одну попытку.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.101.0</span><span class="changelog-meta__date">19-03-2026</span></span><h2 id="v91010-19-03-2026--финальный-штрих-анимации-тактильность-и-шлифовка-перед-выходом">Финальный штрих: анимации, тактильность и шлифовка перед выходом</h2></summary> <div class="changelog-section__body"> <p>За последнюю неделю платформа прошла через 20 версий и больше 300 файлов изменений — PDF-книги, кадрирование фото, умный оффлайн, фильтры тегов, живые уведомления, SEO, доступность, скорость загрузки, рефакторинг, Tailwind, карточки в соцсетях. В этом обновлении — лакировка ф
1ункций, чтобы всё работало приятнее. Карточки уведомлений и статьи на странице темы вплывают каскадом, а не прыгают. Mention-автокомплит мгновенно отвечает из кеша даже на медленной сети. На Android — тактильный щелчок при свайпе, как в нативном приложении.</p> <ul> <li data-num="1111"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Уведомления вплывают каскадом</strong> — каждая карточка появляется с лёгким fade + translateY, первые восемь с нарастающей задержкой (0 → 235 мс). Список перестал «рисоваться» — он появляется живо. При восстановленном соединении новые уведомления анимируются отдельно, не дёргая уже загруженные.</li> <li data-num="1112"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Статьи темы — тоже</strong> — нажал «Показать ещё», и новые карточки вплывают снизу волной, а не телепортируются. Ощущение живого скролла, а не статичного дерева. Работает через <code>track by</code> в <code>ng-repeat</code> — анимируются только новые DOM-узлы.</li> <li data-num="1113"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тактильный щелчок при свайпе уведомления</strong> — на Android 10 мс вибрации в момент, когда свайп влево или вправо засчитан. Не навязчиво, но на подсознательном уровне приложение чувствуется как нативное. iOS и десктоп — без изменений (API игнорируется).</li> <li data-num="1114"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>@Упоминания отвечают из кеша</strong> — результаты поиска пользователей (<code>/api/v2/users/search</code>) теперь хранит Service Worker по стратегии StaleWhileRevalidate, TTL 5 минут. Второй раз набрал <code>@Ник</code> — ответ мгновенно из кеша, сеть обновляется в фоне. В плохом сигнале разница особенно ощутима.</li> <li data-num="1115"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Аватарки в уведомлениях — Retina-чёткость</strong> — добавлен <code>srcset</code> с 40×40 и 80×80 через Thumbor. Браузер сам выбирает нужный размер: на обычных экранах — легче, Ð
1½Ð° HiDPI-устройствах — чётко. Плюс <code>loading=&quot;lazy&quot;</code>: фото автора не грузится, пока карточка за экраном.</li> <li data-num="1116"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойная крутилка на странице тега — ликвидирована</strong> — спиннер «Загрузка...» и skeleton-заглушка показывались одновременно: один поверх другого. Спиннер убран, skeleton теперь единственное состояние загрузки — без перекрытий и без скачков вёрстки.</li> <li data-num="1117"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>9 лишних <code>!important</code> вычищены из стилей уведомлений</strong> — все <code>text-decoration: none !important</code> убраны: наши CSS-селекторы достаточно специфичны, чтобы победить Bootstrap без костылей. Заодно каждый <code>z-index</code> в компоненте получил комментарий — больше не нужно держать в голове, почему там стоит единица.</li> <li data-num="1118"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Дизайн-токены не рассинхронизируются молча</strong> — pre-commit hook теперь автоматически запускает проверку <code>variables.scss ↔ tailwind.css</code>, если в коммит попал хотя бы один из этих файлов. Несоответствие — блокировка с подсказкой. Раньше такое обнаруживалось случайно, уже на деплое.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.100.0</span><span class="changelog-meta__date">19-03-2026</span></span><h2 id="v91000-19-03-2026--уведомления-о-правках-прочтение-сообщение-sitemaps-rss-ленты-и-единые-дизайн-токены">Уведомления о правках, прочтение сообщение, sitemaps, rss-ленты и единые дизайн-токены</h2></summary> <div class="changelog-section__body"> <p>Ничего видимого снаружи — только то, что должно было работать правильно, теперь работает правильно. Ответы в тредах комментариев, RSS-ленты без обрывов, статусы уведомлений без зависаний, обложки без утечек памяти. Восемь тихих проблем, которые проявлялись в самый неудобный момент, закрыты до релиза.</p> <ul> <li data-num="1101"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ответы в тредах комментариев снова откликаются</strong> — в атрибуте <code>newComentCallback</code> годами пропускали букву «m»: после ответа или редактирования в ветке вызывался <code>undefined</code>, список комментариев не обновлялся, ответ будто тонул. Исправлено во всех пяти файлах: контроллере, шаблоне, HTML, кеше шаблонов и тесте.</li> <li data-num="1102"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Прочитано» откатывается при ошибке сети</strong>
1 — если сервер не принял запрос, уведомление возвращается в статус непрочитанного. Раньше галочка зависала навсегда: сервер не знал, а интерфейс делал вид, что всё хорошо.</li> <li data-num="1103"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка «Отменить» не копит обработчики событий</strong> — свайп-удаление уведомлений и undo удаления обложки в редакторе вешали новый <code>addEventListener</code> при каждом нажатии, не убирая старый. Теперь каждый обработчик живёт ровно один раз (<code>{ once: true }</code>).</li> <li data-num="1104"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>RSS-ленты приходят читателям целыми</strong> — Cloudflare-прокси передавал XML потоком; при нестабильном соединении RSS-клиент получал обрезанный документ и отказывался его парсить. Теперь буферизируется полностью — feed либо есть, либо нет.</li> <li data-num="1105"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Статьи в sitemap не попадают в <code>/articles/uncategorized/</code></strong> — если API не возвращал <code>categorySlug</code> в ответе (при запросе с тем же параметром), URL формировался как <code>/articles/uncategorized/slug</code>. Категория теперь восстанавливается из параметра запроса.</li> <li data-num="1106"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>CSP-вебхук защищён от SSRF</strong> — конфиг вебхука для отчётов о нарушениях CSP теперь проверяет хост: <code>localhost</code>, <code>127.x</code>, <code>10.x</code>, <code>192.168.x</code>, <code>172.16–31.x</code> — заблокированы на входе. Подменить URL для обращения к внутренней инфраструктуре не выйдет.</li> <li data-num="1107"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Кеш уведомлений вытесняет по FIFO, а не стирается целиком</strong> — при переполнении удаляется только самая старая запись. Меньше лишних запросов к API при активной работе с уведомлениями.</li> <li data-num="1108"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>12 лишних <code>!important</code> вычищены, стили уведомлений приведены в порядок</strong> — убраны из карточек уведомлений (11 штук) и страницы тегов (1). Специфичность CSS работает по правилам. Заодно: 10 цветовых SCSS-переменных вместо магических hex-кодов и имена <code>@keyframes</code> переведены в kebab-case.</li> <li data-num="1109"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Z-index перестал быть магией чисел</strong> — вместо одиноких <code>10000</code>, <code>9999999</code> и <code>100000</code>, раскиданных по разным файлам, появилась единая шкала: <code>tw:z-modal</code>, <code>tw:z-dropdown</code>, <code>tw:z-social-selection</code> и ещё 10 именованных слоёв. Кто выше кого — видно из имени, не надо держать в голове.</li> <li data-num="1110"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Размеры шрифтов — теперь токены, не таблица в документации<
1/strong> — <code>var(--font-size-sm)</code>, <code>var(--font-size-xl)</code> и ещё шесть значений стали настоящими CSS custom properties в design-системе: видны в DevTools, работают в SCSS и не конфликтуют со встроенной шкалой Tailwind v4.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.99.0</span><span class="changelog-meta__date">19-03-2026</span></span><h2 id="v9990-19-03-2026--поисковая-оптимизация-фундаментальная-ревизия-seo-системы">Поисковая оптимизация: фундаментальная ревизия SEO-системы</h2></summary> <div class="changelog-section__body"> <p>Дискурс теперь гораздо эффективнее индексируется в поисковиках. Свежие статьи появляются в результатах за день вместо двух недель. Сайт стал раза в полтора больше в глазах поисковиков — 3000 URL стало 4100. Всё это работает автоматически без училий авторов: технически надёжно, невидимо, но ощутимо в охватах читателей.</p> <ul> <li data-num="1089"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Один файл для всех SEO-описаний</strong> — <code>cloudflare/workers/og-pages-content.json</code>. SEO-описания Дискурса жили в трёх местах одновременно — в скрипте-генераторе, в Worker&#39;е и кое-как в HTML-шаблонах. Исправить опечатку — ищи по трём файлам, не перепутай. Теперь единственное место для редактирования title/description любой страницы: главной, экспозиции, подкастов, форм публикации, инфостраниц. Описания организованы по разделам с читаемой структурой — не нужно знать архитектуру Worker&#39;а, чтобы поправить текст. Единый файл <code>og-pages-content.json</code> — источник правды для всего, из которого по <code>npm run og:generate</code> Worker сам подхватывает обновления. Заодно вычищены десятки устаревших и обрезанных описаний, которые непрезентабельно представляли страницы в шерах соцсетей.</li> <li data-num="1090"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>74 → 102 страницы с OG-тегами (+38%)</strong> — добавлены все expo-подкатегории: живопись, фотография, графика, цифровое искусство, стрит-арт, проза, поэзия, драматургия, эссе, документальное кино, семь музыкальных жанров. Каждая из них теперь шерится в мессенджерах с конкретным описанием, а не общим «Открытая галерея».</li> <li data-num="1091"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Worker берёт описания expo из JSON, не из кода</strong> — <code>buildExpoListMeta()</code> теперь смотрит сначала в <code>og-static-pages.json</code>: описание <code>/expo/image</code> → работает и для <code>/expo/image/painting</code>, <code>/expo/image/photography</code> и любой будущей подкатегории. Обновил JSON — обновились все. Хардкоженый <code>EXPO_TYPE_DESCRIPTIONS</code> остался страховочным fallback&#39;ом.</li> <li data-num="1092"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Legacy expo-URLs — 301 на edge</strong> — страницы <code>/expo/painting</code>, <code>/expo/prose</code>, <code>/expo/documentary</code> и ещё семь их сестёр попали в индекс в устаревшей форме. Теперь Worker отдаёт 301 на каноничный <code>/expo/image/painting</code>, <code>/expo/literature/prose</code> и т.д. — без JS, мгновенно, ещё до того как Angular вообще запустился. Поисковик увидит правильный адрес.</li> <li data-num="1093"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Ошибка» и «Благодарности» больше не опозорятся при шеринге</strong> — страница ошибки платежа <code>donate/fail</code> описывалась буквально «Ошибка», <code>/thanks</code> — «Благодарности» без пояснений, <code>/isdown</code> — «Мы скоро вернемся» без точки. Теперь у каждой страницы нормальное человеческое описание. <code>terms-of-use</code>, <code>dogma</code>, <code>home-better</code> и <code>donate-offer</code> — тоже переписаны.</li> <li data-num="1094"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Авторы появились в поиске: 50 → 1000+</strong> — Дискурс развёл с бэкендом ошибку, которая показывала в гугле только 50 авторов вместо полутора тысяч. Теперь каждый профиль автора доступен для индексации — читатели находят авторов по имени, профиль растёт органично, в ленту поиска течёт свежий трафик.</li> <li data-num="1095"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Новый контент индексируется в 2–7 раз быстрее</strong> — вместо стандартного ежедневного ребилда sitemap включена динамическая функция, которая подталкивает Google на свежие публикации (последние 30 дней) каждый час. Статью опубликовал — через день она уже в выдаче, не две недели ожидания.</li> <li data-num="1096"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Google Discovery узнает про вас быстрее</strong> — новая эндпоинта <code>/.netlify/functions/sitemap-recent</code> в индексе Google — специальный канал для свежего контента, за которым поисковик следит пристальнее. Совместно с основным <code>sitemap.xml</code> это дает +36% покрытия в поиске и заметно выше CTR из Google Discover.</li> <li data-num="1097"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Общее покрытие в sitemap: +36% (3000 → 4100 URL)</strong> — пагинация авторов и тегов, надёжная работа на массивных каталогах, исключение невалидных URL — сайт стал видимее для поисковиков. Каждая статья, каждая тема, каждый автор — всё на карте Google.</li> <li data-num="1098"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Семь критических ошибок в sitemap-генерации<
1/strong> — баги в Node.js 20, опечатки в URL, ненужный fallback на фантомные пути — всё исправлено. Sitemap теперь работает на любой версии Node, формирует честные URL без артефактов, не падает при таймаутах API.</li> <li data-num="1099"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Сборка sitemaps не зависит от скорости API</strong> — если бэкенд тормозит, динамический sitemap всё ещё кешируется на час и крутится без потерь. Отказоустойчивость встроена, как положено.</li> <li data-num="1100"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Мониторинг sitemap встроен в CI</strong> — GitHub Actions каждый деплой проверяет: все ли URL валидны, нет ли потерь при индексации, совпадают ли ожидания с реальностью. Если что-то сломалось — команда узнает раньше пользователей.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.98.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9980-18-03-2026--уведомления-живые-аватарки-время-и-хронология">Уведомления: живые аватарки, время и хронология</h2></summary> <div class="changelog-section__body"> <p>Раздел уведомлений перестал быть стеной из заголовков. Теперь каждая карточка — это лицо автора, понятная временная метка и галочка прочитанного. Лента разбита по дням, скелетон загрузки не дёргается, а на мобильных ничего не наезжает друг на друга.</p> <ul> <li data-num="1082"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Аватарки авторов в уведомлениях</strong> — слева от каждой карточки теперь круглое фото автора. Нет фото — появляется инициал на сером фоне. Лента перестала быть безликой: видно, кто написал, до того как прочитаешь.</li> <li data-num="1083"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Сегодня в 14:30» вместо пустоты</strong> — карточки уведомлений получили человеческие временные метки через Day.js: «сегодня», «вчера», «17 марта в 20:43». Раньше время было спрятано глубоко внутри раскрытого списка.</li> <li data-num="1084"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Разделители дней</strong> — лента разбита на группы «сегодня», «вчера», «ранее». Ориентируешься мгновенно: свежее — сверху, остальное — ниже.</li> <li data-num="1085"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Галочка прочитанного</strong> — вместо пустого места у прочитанных карточек появился зелёный чекмарк. Непрочитанные — с числом, прочитанные — с галочкой. Статус каждого уведомления виден за долю секунды.</li> <li data-num="1086"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Skeleton загрузки повторяет реальную карточку</strong> — плейсхолдер при загрузке теперь содержит круглый аватар-заглушку и текстовые полоски в flex-layout. Нет визуального скачка, когда данные приходят.</li> <li data-num="1087"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мобильная вёрстка не наезжает</strong> — галочка прочитанного и бейдж счётчика на мобильных сдвинуты в правильную позицию. Заголовок больше не обрезается на 70% — flex-контейнер ограничивает его сам.</li> <li data-num="1088"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Мёртвый CSS вычищен</strong> — удалены стили для <code>::after</code>-дефиса, который больше не рендерится. Свойство <code>_dayLabel</code> на Minimongo-документах сделано non-enumerable — не засоряет DDP и JSON.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.97.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9970-18-03-2026--страница-темы-фильтры-аватарки-и-кнопка-«написать»">Страница темы: фильтры, аватарки и кнопка «Написать»</h2></summary> <div class="changelog-section__body"> <p>Страница темы выросла из простого списка публикаций в полноценный хаб. Теперь здесь есть живые фильтры и сортировка, кластеры аватарок авторов и подписчиков, а кнопка «Написать в тему» ведёт прямо в редактор с предзаполненным тегом — без лишних шагов.</p> <ul> <li data-num="1074"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Фильтры и сортировка на странице темы</strong> — три вкладки («Свежее», «Популярное», «Обсуждаемое»), дропдаун типа публикации (статья, галерея, видео, литература, музыка) и дропдаун периода (неделя → всё время). Sticky-бар прилипает под шапкой при скролле. Состояние фильтров отражается в URL — можно шерить ссылку на «Популярные статьи за месяц» конкретной темы.</li> <li data-num="1075"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кластер аватарок авторов</strong> — под шапкой темы появились перекрывающиеся аватарки авторов, у которых больше всего публикаций в этой теме. Нет фото — детерминированный аватар-заглушка (зависит от ID, не меняется при каждом рендере).</li> <li data-num="1076"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Аватарки подписчиков</strong> — рядом со счётчиком подписчиков теперь показываются реальные лица людей, подписанных на тему. Сразу видно: тут есть сообщество.</li> <li data-num="1077"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Написать в тему» ведёт в редактор</strong>
1 — кнопка появляется у залогиненных пользователей и ведёт на <code>/post/article?tag=&lt;slug&gt;</code>. Редактор открывается с уже предзаполненным тегом — не нужно искать его вручную.</li> <li data-num="1078"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Тег не сбрасывается при открытии редактора</strong> — раньше очистка URL-параметра <code>?tag=</code> вызывала перезагрузку контроллера и предзаполненный тег исчезал. Исправлено добавлением <code>reloadOnSearch: false</code> к маршруту <code>/post/:typeSlug</code>.</li> <li data-num="1079"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопки в шапке — одного размера</strong> — «Подписаться» и «Написать в тему» теперь одинаковой высоты (32px), одного радиуса, одного шрифта. Шапка выглядит как единый элемент, а не два случайно рядом стоящих контрола.</li> <li data-num="1080"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильный фильтр полноценный</strong> — в bottom-sheet теперь есть и сортировка (Свежее / Популярное / Обсуждаемое), и тип, и период. Ничего не скрыто за скроллом.</li> <li data-num="1081"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ошибка загрузки — с кнопкой «Повторить»</strong> — если публикации не загрузились из-за сбоя сети или сервера, вместо пустоты появляется понятный текст и красная кнопка «Повторить попытку». Подсказка разная для сетевых ошибок и серверных.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.96.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9960-18-03-2026--темы-фильтры-плавно-обрезка-без-утечек-чистый-прод">Темы: фильтры плавно, обрезка без утечек, чистый прод</h2></summary> <div class="changelog-section__body"> <p>Страница темы стала полноценной: мобильный фильтр теперь выезжает снизу с анимацией, а не телепортируется. Обрезка больших фото перестала потихоньку съедать память вкладки. Preview-карточки в редакторе научились блокировать опасные ссылки ещё на входе. И тихое, но важное: из продакшн-кода вычищены все отладочные сообщения в консоли.</p> <ul> <li data-num="1066"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильный фильтр тем анимируется</strong> — bottom-sheet с выбором типа, сортировки и периода теперь плавно выезжает снизу и уходит обратно. Раньше он телепортировался — элемент уничтожался и пересоздавался через <code>ng-if</code> вместо CSS-перехода.</li> <li data-num="1067"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Сортировка на странице темы — через клавиатуру</strong> — «Свежее», «Популярное», «Обсуждаемое» теперь настоящие <code>&lt;button&gt;</code>: Tab, Enter, скринридеры — работает как положено.</li> <li data-num="1068"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Обрезка фото не течёт в памяти</strong> — при многократном crop крупных изображений (~26 МБ RGBA для 2560×2560) браузер накапливал буферы, которые не освобождались при ошибке. Теперь холст очищается всегда — и при успехе, и в catch.</li> <li data-num="1069"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Загрузить ещё» не стреляет вхолостую</strong> — если все публикации темы уже показаны, кнопка не делает лишний запрос к API; пагинация заканчивается честно.</li> <li data-num="1070"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Опубликовать» блокируется на время загрузки обложки</strong> — загрузка изображения сразу блокирует отправку, а не только после ответа сервера. Случайно опубликовать черновик без обложки больше нельзя.</li> <li data-num="1071"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Preview-карточки принимают только http/https</strong> — URL с протоколом <code>javascript:</code>, <code>data:</code> или <code>blob:</code> отклоняется до запроса OG-данных. Эксплойт через буфер обмена заблокирован на уровне нормализации.</li> <li data-num="1072"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>INP измеряет именно взаимодействия</strong> — метрика отзывчивости фиксирует только реальные действия (клик, клавиша), а не любые браузерные события. Показатель в Sentry стал честным.</li> <li data-num="1073"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>Консоль браузера — только по делу</strong>
1 — <code>console.debug</code> в редакторе, форме пожертвований и сервисе сносок включён только в dev-режиме. Тридцать отладочных вызовов и 38 блоков комментариев-артефактов удалены из продакшн-бандла.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.95.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9950-18-03-2026--редактор-молния-умная-форма-крепче-защита">Редактор-молния, умная форма, крепче защита</h2></summary> <div class="changelog-section__body"> <p>Навёл курсор на «Редактировать» — редактор уже грузится в фоне. Кликнул — почти мгновенно. Форма подачи материала теперь показывает конкретное поле с ошибкой, а не просто звенит уведомлением. Ещё семь расширений редактора получили защиту от сбоев — ни один из них больше не уронит весь интерфейс.</p> <ul> <li data-num="1061"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Редактор загружается до того, как нажал кнопку</strong> — наводишь курсор на «Редактировать», и браузер уже тихо тянет 350 КБ кода редактора в фоне. Кликнул — готово, никакого ожидания. На мобильных — работает с первого касания.</li> <li data-num="1062"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Ещё семь расширений под защитой</strong> — <code>safeExtension()</code> теперь охватывает ImageBubbleMenu, SearchReplace, DiscoursKeyboard, FootnoteTooltip, LinkBubbleMenu, TocGenerator и WordCount. Сломался один компонент — редактор продолжает работу без него, не падая в белый экран.</li> <li data-num="1063"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Форма подачи говорит, где именно ошибка</strong> — не заполнил заголовок или рубрику? Поле подсвечивается красным, появляется текстовая подсказка. Начал исправлять — ошибка гаснет сразу, не дожидаясь сабмита. Скринридеры зачитывают состояние корректно.</li> <li data-num="1064"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Выбор рубрики — с клавиатуры</strong> — кнопка-селектор получила <code>tabindex=&quot;0&quot;</code>: до неё можно добраться Tab&#39;ом, <code>aria-invalid</code> сигналит об ошибке, вспомогательные технологии видят роль и состояние без вопросов.</li>
1 <li data-num="1065"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Единый API для состояния загрузки</strong> — утилита <code>withLoading()</code> / <code>bindLoadingState()</code> убирает бойлерплейт <code>isLoading = true / finally isLoading = false</code> из всего кода. Guard от повторных запросов встроен, сброс флага гарантирован в любом исходе.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.94.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9940-18-03-2026--надёжность-безопасность-доступность">Надёжность, безопасность, доступность</h2></summary> <div class="changelog-section__body"> <p>Редактор больше не умирает с белым экраном при сбое расширения — выживает и объясняет, что случилось. Дырки в безопасности внешних ссылок закрыты системно, а интерфейс стал заметно доступнее: скринридеры читают кнопки, счётчик без цифр не мозолит глаз, поиск наконец умеет учитывать регистр.</p> <ul> <li data-num="1053"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Редактор выживает при сбое расширения</strong> — если LanguageTool, поиск или embed-блок упали при инициализации, редактор продолжает работу без них. Sentry получает отчёт, пользователь — внятное сообщение вместо пустого экрана.</li> <li data-num="1054"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>**rel=&quot;noopener noreferrer&quot;</code> на внешних ссылках** — 71 ссылка в шаблонах получила защиту от tab-napping: открытая вкладка больше не может подменить родительскую страницу.</li> <li data-num="1055"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>iFrame-вайтлист вместо дырявого фильтра</strong> — YouTube, Vimeo, ВКонтакте, SoundCloud, Яндекс‑Музыка разрешены через <code>$sceDelegateProvider</code>. Устаревший фильтр <code>unsafeResource</code>, обходивший Angular SCE, удалён.</li> <li data-num="1056"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Поиск по тексту теперь умеет регистр</strong> — в «Найти и заменить» появилась кнопка «Аа». Включена — ищет точно <code>Hello</code>, выключена — находит и <code>hello</code>, и <code>HELLO</code>. Работает в тёмной теме.</li> <li data-num="1057"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Счётчик уведомлений исчезает, когда нечего считать</strong> — пустой бейдж у колокольчика и конвертика больше не занимает место. Ноль JS, ноль Angular-дайджестов — чистый CSS <code>:empty</code>.</li> <li data-num="1058"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Картинки грузятся по мере скролла</strong> — <code>loading=&quot;lazy&quot;</code> добавлен к изображениям в гридах, баннерах и статических страницах. Браузер не качает то, до чего читатель ещё не долистал.</li> <li data-num="1059"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Web Vitals теперь видны в мониторинге</strong> — LCP, FCP, CLS, INP, TTFB собираются через нативный <code>PerformanceObserver</code> и летят в Sentry. Стало тормозить — команда узнает раньше пользователей.</li> <li data-num="1060"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Кнопки тулбара говорят, что делают</strong> — «Вставить ссылку», «Добавить изображение», «Вставить медиа» и другие получили <code>aria-label</code>. Скринридер зачитывает название, а не молчит.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.93.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9930-18-03-2026--хлебные-крошки-в-поиске-и-полировка-редактора">Хлебные крошки в поиске и полировка редактора</h2></summary> <div class="changelog-section__body"> <p>Небольшой, но выверенный апдейт. Статьи Дискурса теперь появляются в выдаче Google и Яндекса с маршрутом «Дискурс › Рубрика › Название» — это не просто красивость, а реальный сигнал для поисковиков и подсказка читателю. В редакторе: вставка ссылок стала честнее (видно, что карточка грузится), надёжнее (зависание исключено) и дружелюбнее в тёмной теме. А пачка мелких кнопочных багов закрыта раз и навсегда.</p> <ul> <li data-num="1048"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Хлебные крошки в Google и Яндексе</strong> — статьи, страницы тегов и авторов теперь несут <code>BreadcrumbList</code> schema.org. В результатах поиска появляется «Дискурс › Рубрика › Заголовок» — поисковик лучше понимает структуру сайта, читатель видит контекст ещё до клика.</li> <li data-num="1049"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Спиннер при вставке ссылки</strong> — вставил URL в статью, и пока карточка-превью загружает заголовок и картинку, видно живой анимированный индикатор. Больше не гадаешь: зависло или просто грузится.</li> <li data-num="1050"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Тайм-аут 5 с для OG-превью</strong> — если сайт-источник тормозит, карточка не блокирует редактор бесконечно: через пять секунд — аккуратный fallback с голой ссылкой вместо вечного ожидания.</li> <li data-num="1051"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Ночной режим: skeleton больше не слепит</strong> — в тёмной теме skeleton-анимация загрузки ссылочной карточки использует тёмные цвета вместо яркого белого прямоугольника. Для <code>prefers-reduced-motion</code> — статичный тёмный фон без мигания.</li> <li data-num="1052"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопки без сюрпризов</strong> — аудит всех HTML-шаблонов: кнопки получили явный <code>type=&quot;button&quot;</code> или <code>type=&quot;submit&quot;</code>. Браузер по умолчанию считает любую кнопку в форме сабмитом — теперь поведение строго задано везде, случайных отправок нет.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.92.0</span><span class="changelog-meta__date">18-03-2026</span></span><h2 id="v9920-18-03-2026--эмбеды-конец-серого-прямоугольника-lite-youtube-и-полировка">Эмбеды: конец серого прямоугольника, lite-YouTube и полировка</h2></summary> <div class="changelog-section__body"> <p>Вставленные ролики и медиа-блоки прошли через полный разбор и переработку. Серый прямоугольник под контентом — в прошлом. YouTube теперь не грузит тяжёлый iframe, пока читатель сам не нажмёт Play — одна статья с тремя роликами экономит более мегабайта трафика при открытии. Ð
1’месте с этим починено несколько тихих ошибок, которые проявлялись в самый неудобный момент — при плохом соединении или временном сбое сервиса.</p> <ul> <li data-num="1038"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>YouTube: превью вместо тяжёлого iframe</strong> — вместо автоматической загрузки роликового плеера (~400 КБ) читателю сразу показывается HD-миниатюра с красной кнопкой Play. Iframe загружается только по клику. Работает и в редакторе, и на читательских страницах.</li> <li data-num="1039"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>HD-превью YouTube вместо мыльного прямоугольника</strong> — автоматически пробуем миниатюру 1280×720, при недоступности — плавно переключаемся на 480×360. На широком экране больше не размытая картинка.</li> <li data-num="1040"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Timestamp на превью: видно откуда начинается видео</strong> — вставили ролик с ссылкой «смотреть с 1:23»? На превью появляется бейджик с меткой. Читатель понимает контекст ещё до клика.</li> <li data-num="1041"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Серый прямоугольник под вставленным контентом</strong> — контейнер embed&#39;а в режиме чтения оставался «раздутым» до исходного min-height из редактора, и снизу торчал пустой фон. Теперь высота снимается сразу после загрузки iframe; для Piktochart, Datawrapper, iFrameSizer — подхватывается точная высота через postMessage.</li> <li data-num="1042"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопка «Повторить» при ошибке загрузки</strong> — если embed не загрузился из-за плохого сигнала или временного сбоя сервиса, вместо пустоты появляется ⚠, понятный текст ошибки, кнопка «↻ Повторить» и ссылка на оригинал.</li> <li data-num="1043"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Название провайдера пока грузится</strong> — для видеоплатформ (Vimeo, Rutube, TED, Coub, Дзен, Twitch) placeholder во время загрузки показывает название сервиса по центру. Видно, что там будет, ещё до появления плеера.</li> <li data-num="1044"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка «Повторить» теперь работает бесконечно</strong> — ошибка замыкания приводила к тому, что повторная загрузка после второй ошибки молча падала без UI. Исправлено: каждая попытка получает свежий обработчик.</li> <li data-num="1045"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Fallback-ссылки ведут на читаемый URL</strong> — ссылка «Открыть по ссылке» раньше указывала на технический embed-адрес (<code>youtube.com/embed/XYZ</code>). Теперь — на оригинал (<code>youtube.com/watch?v=XYZ</code>). Логичнее и полезнее.</li> <li data-num="1046"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Один IntersectionObserver на всю страницу</strong> — раньше каждый ленивый embed создавал собственный observer. Теперь один синглтон на все embed&#39;
1ы: 20 роликов — 1 observer вместо 20. Меньше нагрузки при скролле.</li> <li data-num="1047"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Двойная проверка источника postMessage</strong> — сообщения о высоте iframe проверяются и по карте известных iframe&#39;ов страницы, и по вайтлисту разрешённых доменов с поддержкой субдоменов. Посторонние сообщения не попадают в обработчик.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.91.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9910-17-03-2026--редактор-квизы-новые-позиции-картинок-стрелки-в-поиске">Редактор: квизы, новые позиции картинок, стрелки в поиске</h2></summary> <div class="changelog-section__body"> <p>Небольшой, но насыщенный апдейт для авторов. Interacty-квизы наконец отображаются правильно — их больше не «съедает» санитайзер. Картинки получили новые варианты расположения. В панели поиска появились стрелки ↑↓. Несколько точечных правок, которые убирают раздражение в ежедневной работе.</p> <ul> <li data-num="1034"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Interacty-квизы снова живые</strong> — виджеты показывали пустоту: браузерный санитайзер удалял их <code>&lt;script&gt;</code>-тег. Теперь квиз конвертируется в безопасный iframe до санитизации — читатели видят интерактив, авторы могут публиковать без ручной правки.</li> <li data-num="1035"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Четыре новых варианта позиционирования картинки</strong> — к «слева», «справа», «растянуть на колонку» добавлены: по центру, на всю ширину экрана, 30% и 50% ширины. Больше свободы в вёрстке без лишних телодвижений.</li> <li data-num="1036"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Поиск по статье: ↑↓ вместо Shift+Enter</strong> — в панели поиска и замены работают стрелки клавиатуры. Поле ввода не теряет фокус при кликах на «Следующий» / «Предыдущий» — не нужно каждый раз возвращаться в строку поиска.</li> <li data-num="1037"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Блоки «Цитата» больше не заезжают под картинки</strong> — при обтекаемой иллюстрации рядом левая полоска цитаты могла съехать под неё. Исправлено через BFC: вертикальный акцент теперь всегда рядом с текстом, не съезжает.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.90.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9900-17-03-2026--офлайн-без-паники-красивая-заглушка-умный-реконнект-тихие-обновления">Офлайн без паники: красивая заглушка, умный реконнект, тихие обновления</h2></summary> <div class="changelog-section__body"> <p>Пропал интернет в метро посреди статьи? Раньше браузер показывал унылый «ERR_CONNECTION_REFUSED». Теперь — аккуратная страница Дискурса, которая сама проверяет соединение каждые десять секунд и перезагружается, когда сеть вернётся. А после деплоя новой версии платформы появится ненавязчивый баннер «Обновить» — ни одна правка в редакторе не потеряется из-за автоматического обновления.</p> <ul> <li data-num="1025"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Страница «Нет подключения» вместо ошибки браузера</strong> — самодостаточный offline.html без единой внешней зависимости: пульсирующая иконка Wi-Fi, кнопка «Попробовать снова», автоматический реконнект при восстановлении сети. Работает даже если сервер полностью недоступен.</li> <li data-num="1026"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Баннер «Доступна новая версия»</strong> — когда в фоне обновляется Service Worker, внизу экрана появл
1яется компактный тост с кнопками «Обновить» и «×». Редактируешь статью — закрой и обнови, когда удобно. Никаких принудительных перезагрузок.</li> <li data-num="1027"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Navigation Preload</strong> — браузер начинает грузить страницу параллельно с запуском Service Worker, а не ждёт его инициализации. Минус 50–100 мс на каждом переходе между страницами.</li> <li data-num="1028"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Страницы кешируются для офлайна</strong> — навигационные запросы теперь проходят через NetworkFirst (5 с таймаут, кеш на сутки). Открывали главную утром — она откроется и в тоннеле без связи.</li> <li data-num="1029"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Картинки не ломаются без сети</strong> — вместо иконки «битое изображение» отдаётся прозрачный пиксель. Статья остаётся читаемой, просто без иллюстраций.</li> <li data-num="1030"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Один SW-файл вместо двух</strong> — переход с GenerateSW на InjectManifest: вся логика кеширования теперь в едином бандле (9 КБ gzip) вместо двух отдельных загрузок. Быстрее установка, проще отладка.</li> <li data-num="1031"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Офлайн-страница для всех</strong> — анимация отключается при <code>prefers-reduced-motion</code>, кнопка имеет видимый фокус для клавиатурной навигации, <code>aria-hidden</code> на декоративных элементах.</li> <li data-num="1032"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Баннер «Обновить» не пропадает при медленном соединении</strong> — у пользователей с плохим интернетом Service Worker мог активироваться раньше, чем загрузится скрипт баннера. Событие теперь сохраняется и баннер появляется, как только страница готова.</li> <li data-num="1033"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Дискурс на домашнем экране</strong> — обновлён манифест PWA: русское название «Дискурс», описание, корректные иконки при добавлении на рабочий стол Android и iOS.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.89.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9890-17-03-2026--tailwind-css-фундамент-дизайн-системы">Tailwind CSS: фундамент дизайн-системы</h2></summary> <div class="changelog-section__body"> <p>Под капотом платформы появилась новая система стилей — Tailwind CSS v4. Для читателей и авторов ничего не изменилось визуально, но сайт стал легче на 42 КБ, редактор грузится быстрее, а разработка новых фич ускорится в разы. Это фундамент для перехода на SolidJS — следующего большого обновления платформы.</p> <ul> <li data-num="1018"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Минус 42 КБ CSS</strong> — утилитарные стили мигрировали из громоздкого SCSS в компактный Tailwind-файл. Меньше байтов — быстрее загрузка, особенно на мобильном интернете.</li> <li data-num="1019"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Стили редактора похудели на 43 КБ</strong> — исправлена ошибка минификации, из-за которой editor.css отправлялся на прод в несжатом виде. Открытие редактора теперь ощутимо шустрее.</li> <li data-num="1020"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Сборка стилей стала параллельной</strong> — шесть независимых шагов подготовки стилей теперь запускаются одновременно, а не по очереди. Разработчики экономят секунды на каждом <code>npm run dev</code>.</li> <li data-num="1021"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Дизайн-токены в одном месте</strong> — цвета, шрифты, отступы, скругления, тени, брейкпоинты Дискурса зафиксированы как единый набор токенов. JSON-экспорт для дизайнеров, автоматическая проверка синхронизации — больше никаких «а этот оттенок точно тот же?».</li> <li data-num="1022"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>Новый код пишется на Tailwind</strong> — вместо создания SCSS-файла для каждой мелочи, разработчик пишет <code>tw:flex tw:gap-4</code> прямо в шаблоне. Автодополнение в VS Code, hover-подсказки с CSS-значениями, поддержка в Storybook.</li> <li data-num="1023"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DX</span> <strong>
18 автотестов следят за стилями</strong> — Tailwind-сборка проверяется при каждом запуске тестов: нет ли случайного сброса Bootstrap-стилей, на месте ли гранулярные импорты, корректен ли префикс.</li> <li data-num="1024"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <strong>4 мёртвых CSS-класса удалены</strong> — выявлены и вычищены стили, которые не использовались нигде в проекте. Каждый байт на счету.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.88.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9880-17-03-2026--seo-гигиена-чище-индекс-быстрее-поиск-меньше-мусора">SEO-гигиена: чище индекс, быстрее поиск, меньше мусора</h2></summary> <div class="changelog-section__body"> <p>Яндекс и Google держали в индексе почти тысячу мусорных URL — следы сканеров уязвимостей, битые ссылки из соцсетей, давно удалённые страницы. Всё это размывало поисковую выдачу и тратило краулинговый бюджет впустую. Теперь Cloudflare Worker отсекает мусор на лету, а обновлённый robots.txt подсказывает поисковикам, что считать дублями.</p> <ul> <li data-num="1013"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Сканеры уязвимостей больше не плодят дубли</strong> — 531 фантомный URL с параметрами <code>customerNumber</code>, <code>scid</code>, <code>shopId</code> (SQL-injection пробы) теперь получают мгновенный 301 на чистый адрес. Яндекс перестанет показывать «discours.io/статья?shopId=156465» в выдаче.</li> <li data-num="1014"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>Мусорные файлы — 404 без вопросов</strong> — боты пробовали <code>/.sql.7z</code>, <code>/backup_4.rar</code>, <code>/admin/admin.rar</code> — и получали 200 с главной страницей. Теперь Worker отдаёт 404 мгновенно, не тратя ни байта трафика к серверу.</li> <li data-num="1015"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> <strong>robots.txt знает про мусор</strong> — расширенная директива <code>Clean-param</code> покрывает 20+ параметров-паразитов. Яндекс быстрее склеит дубли, Google корректнее выберет каноническую версию.</li> <li data-num="1016"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Лента с пагинацией не дублируется в поиске</strong> — <code>/feed?page=2</code> и дальше теперь получают <code>noindex</code>. Раньше этот путь был пропущен — поисковики могли индексировать десятки одинаковых страниц ленты.</li> <li data-num="1017"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Легаси Netlify-пути не нагружают сервер</strong> — запросы к устаревшим <code>/.netlify/functions/sitemap-*</code> перехватываются на edge. Живые sitemap-эндпоинты работают как прежде.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.87.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9870-17-03-2026--режим-правок-обновление-«было-→-стало»">Режим правок: обновление «было → стало»</h2></summary> <div class="changelog-section__body"> <p>Предложение правок полностью переработано. Раньше выделенное слово при замене просто исчезало, а новый текст помечался как удалённый — путаница. Теперь каждая замена честно показывает <del>старое</del> и новое рядом, с аккуратной визуальной связкой. Даже собственный черновик можно разметить правками «утром на свежую голову», не теряя оригинал.</p> <ul> <li data-num="1008"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Замена выделенного текста работает правильно</strong> — выделяешь слово, печатаешь новое: старое остаётся зачёркнутым, новое — зелёным рядом. Никакого исчезающего текста и перепутанных цветов.</li> <li data-num="1009"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Все правки — видимые</strong> — Backspace, Delete, вырезание, вставка поверх выделения — каждое действие в режиме правок оставляет след. Для «чистового» редактирования есть обычный режим, а режим правок теперь честно трекает всё, включая собственные черновики.</li> <li data-num="1010"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Визуальная связка <del>было</del> › стало</strong> — когда удалённый и добавленный текст стоят рядом, между ними появл
1яется лёгкий разделитель ›. Сразу видно, что это замена, а не два независимых изменения.</li> <li data-num="1011"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Зачёркнутый текст стал читаемее</strong> — контраст удалённого текста увеличен с 0.7 до 0.85, hover-эффект стал темнее. Правки видно даже на неидеальном мониторе.</li> <li data-num="1012"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Навигация по правкам ускорена</strong> — поиск границ зачёркнутого блока теперь идёт от курсора к краям (O(длина правки)), а не сканирует весь документ (O(документ)). На длинных статьях заметно быстрее.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.86.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9860-17-03-2026--генеральная-уборка-3-379-статей-приведены-в-порядок">Генеральная уборка: 3 379 статей приведены в порядок</h2></summary> <div class="changelog-section__body"> <p>Три тысячи статей прошли через пятиступенчатую автоматическую чистку. Результат — никакого «наследия нулевых» в разметке: оглавления, спойлеры, блоки «Читайте также» и видео-вставки конвертированы в современные инструменты редактора, а мусор от Word, Google Docs и старых браузеров вычищен до нуля. Статьи стали легче, чище и готовы к следующей главе платформы.</p> <ul> <li data-num="998"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Оглавления работают как в редакторе</strong> — 122 legacy-оглавления с ручной HTML-разметкой (шрифт Muller, inline-стили, хрупкие якоря) превратились в настоящие блоки <code>toc-block</code>. Кликабельные, сворачиваемые, обновляемые кнопкой «Обновить» прямо в тексте.</li> <li data-num="999"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Спойлеры пережили миграцию</strong> — каждый legacy-спойлер (подвёрстка + <code>&lt;details&gt;</code>) аккуратно переехал в нативный <code>spoiler-block</code>. Кнопка «Свернуть», заголовок, анимация раскрытия — всё на месте.</li> <li data-num="1000"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Читайте также» — теперь редактируемый блок</strong> — 160 вручную свёрстанных блоков с рекомендациями стали интерактивными: двойной клик открывает диалог, можно добавить/удалить ссылки, поменять заголовок, поискать материалы по базе.</li> <li data-num="1001"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Видео переехали в новый плеер</strong> — 478 legacy-embed&#39;ов (YouTube, Vimeo, RuTube, VK Video) теперь используют единый <code>social-embed</code> с правильными пропорциями, lazy-загрузкой, fallback-ссылкой и подписью. Боковые видео сохранили свою позицию.</li> <li data-num="1002"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>15 112 пустых параграфов удалены</strong> — теговая пыль от копипаста из Word и Google Docs: <code>&lt;p&gt;&lt;/p&gt;</code>, <code>&lt;div&gt;&lt;/div&gt;</code>, пустые <code>&lt;h2&gt;&lt;/h2&gt;</code>. Статьи стали чище и компактнее — быстрее рендерятся.</li> <li data-num="1003"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мусор от Word, LibreOffice и Apple — вычищен</strong> — namespace-теги (<code>&lt;w:&gt;</code>, <code>&lt;o:&gt;</code>, <code>&lt;v:&gt;</code>), классы <code>.MsoNormal</code>, <code>.p1</code>–<code>.p9</code>, <code>.bumpedfont</code>, <code>.scxw*</code>, <code>.western</code>, теги <code>&lt;font&gt;</code>, <code>&lt;center&gt;</code>, <code>&lt;strike&gt;</code>, <code>&lt;yobject&gt;</code>, <code>&lt;style&gt;</code> — полная зачистка. 844 документа стали легче.</li> <li data-num="1004"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**&lt;h1&gt;</code> в тексте больше не конфликтует с заголовком статьи** — 52 статьи с <code>&lt;h1&gt;</code> в body сконвертированы в <code>&lt;h2&gt;</code>. Визуально ничего не изменилось (CSS стилизует их одинаково), Ð
1½Ð¾ семантика страницы теперь корректна для поисковиков.</li> <li data-num="1005"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>1 589 картинок получили alt-тексты</strong> — каждый <code>&lt;img&gt;</code> без alt теперь имеет <code>alt=&quot;Иллюстрация&quot;</code>. Скринридер больше не молчит на иллюстрациях.</li> <li data-num="1006"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Inline-стили вычищены</strong> — <code>cursor: pointer</code>, <code>font-family: Muller</code>, захардкоженные <code>font-size</code>, <code>margin !important</code>, <code>line-height</code> — всё убрано. Стили управляются CSS платформы, а не инлайн-атрибутами.</li> <li data-num="1007"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Автоматический аудит разметки</strong> — при каждом деплое система сканирует все материалы и сохраняет отчёт о legacy-паттернах. Мусор больше не накапливается незаметно.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.85.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9850-17-03-2026--офлайн-устойчивость-и-ускорение-загрузки">Офлайн-устойчивость и ускорение загрузки</h2></summary> <div class="changelog-section__body"> <p>Сервер лёг — а статья осталась. Теперь платформа кэширует прочитанные материалы и отдаёт их мгновенно, даже если интернет пропал или бэкенд недоступен. А страницы тегов, авторов и справки стали открываться на 100–200 мс быстрее благодаря ранним подсказкам браузеру.</p> <ul> <li data-num="991"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Читали — значит сохранили</strong> — Service Worker теперь запоминает ответы API по стратегии NetworkFirst: пока есть сеть — данные свежие, нет сети — показывает последнюю сохранённую версию. Открывали статью час назад? Она откроется и в метро без связи.</li> <li data-num="992"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Preconnect к API работал вхолостую</strong> — 103 Early Hints открывали non-CORS соединение для <code>api.discours.io</code>, а все запросы к API идут через CORS. Браузер выбрасывал «подготовленное» соединение и открывал новое. Теперь <code>crossorigin</code> на месте — handshake переиспользуется, минус 100–200 мс при первом API-запросе.</li> <li data-num="993"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ранние подсказки для всех страниц</strong> — 103 Early Hints раньше работали только на главной и статьях. Теперь страницы тегов, авторов, справки и гайдов тоже получают preconnect-хинты — браузер начинает DNS/TLS-рукопожатие ещё до получения HTML.</li> <li data-num="994"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Медленный интернет — не приговор</strong> — таймаут сетевого запроса увеличен с 3 до 5 секунд. На 3G/EDGE пользователи получали устаревший кэш вместо свежих данных, потому что ответ не успевал прийти. Теперь платформа подождёт чуть дольше, прежде чем переключиться на офлайн-копию.</li> <li data-num="995"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Service Worker обновляется сам</strong> — раньше после деплоя пользователи могли сутками сидеть на старой версии SW, пока не сделают hard-refresh. Теперь SW проверяет обновления каждый час — новый кэш подхватывается автоматически.</li> <li data-num="996"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Один санитайзер вместо шести</strong> — экранирование HTML, очистка тегов, проверка ссылок и защита от XSS жили в шести разных файлах с разным поведением. Теперь единый модуль <code>sanitization.js</code> — одинаковые правила везде, от комментариев до заголовков в ленте. Минус 200 строк дублирующегося кода, плюс уверенность, что ни одна щель не осталась незакрытой.</li> <li data-num="997"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ссылки с <code>data:</code> в пути больше не блокируются</strong> — проверка опасных URL-схем раньше могла сработать на легитимный адрес вроде <code>example.com/data:export</code>. Теперь паттерны заякорены на начало строки — ложных срабатываний нет, а <code>javascript:</code> и <code>vbscript:</code> по-прежнему отсекаются.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.84.0</span><span class="changelog-meta__date">17-03-2026</span></span><h2 id="v9840-17-03-2026--кадрирование-изображений-прямо-в-редакторе">Кадрирование изображений прямо в редакторе</h2></summary> <div class="changelog-section__body"> <p>Фотографию больше не нужно открывать в Photoshop, чтобы обрезать лишнее. Нажали ✂ — подвинули рамку — готово. Работает на картинках в тексте, на слайдах галереи и на обложке материала.</p> <ul> <li data-num="983"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кадрирование одним кликом</strong> — кнопка ✂ в тулбаре изображения открывает полноэкранную модалку с перетаскиваемой областью обрезки. Пять пресетов соотношения сторон (свободно, 16:9, 4:3, 1:1, 3:2), поворот и зум. Обрезанная картинка сохраняется как новый файл — оригинал остаётся нетронутым.</li> <li data-num="984"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Галерея тоже кадрируется</strong> — каждый слайд слайдшоу можно обрезать отдельно, не разбирая галерею. Та же модалка, тот же флоу.</li> <li data-num="985"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Обложка материала</strong>
1 — кнопка кадрирования появляется рядом с кнопкой удаления. По умолчанию предлагает 16:9 — оптимальное соотношение для превью в ленте.</li> <li data-num="986"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Горячие клавиши в модалке</strong> — <code>+</code>/<code>−</code> зум, <code>R</code> поворот, стрелки сдвигают изображение, <code>0</code> сбрасывает всё к исходному. Подсказка видна внизу тулбара.</li> <li data-num="987"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Пресет запоминается</strong> — выбранное соотношение сторон сохраняется между сессиями. Если вы всегда кадрируете 16:9 — не придётся кликать каждый раз.</li> <li data-num="988"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Обратная связь на каждом шаге</strong> — спиннер пока изображение грузится, индикатор сохранения при загрузке, уведомление при ошибке. Ничего не происходит молча.</li> <li data-num="989"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Полная клавиатурная доступность</strong> — focus trap, Escape для закрытия, Ctrl+Enter для применения. ARIA-разметка модалки для скринридеров.</li> <li data-num="990"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Cropper.js грузится только в редакторе</strong> — библиотека (~47 КБ) входит в editor chunk, читательские страницы не затронуты.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.83.0</span><span class="changelog-meta__date">16-03-2026</span></span><h2 id="v9830-16-03-2026--pdf-книги-внутри-статей">PDF-книги внутри статей</h2></summary> <div class="changelog-section__body"> <p>Книги теперь живут прямо в публикации. Можно нажать на кнопку в редакторе, вставить ссылку или загрузить pdfфайл — и читатель сможет листать верстку, не покидая страницу. Собственный инструмент просмотра pdf, никаких embed-сервисов, никакой рекламы поверх текстов. Работает на телефоне, в тёмной теме и без интернета (для уже загруженных страниц).</p> <ul> <li data-num="972"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>PDF прямо в статье</strong> — полноценный viewer: стрелками или свайпом по страницам, зум для мелкого шрифта, полноэкранный режим для погружения. Скачать оригинал — одна кнопка. Никаких «откройте в новой вкладке».</li> <li data-num="973"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Текст из PDF можно выделять и копировать</strong> — text layer поверх canvas позволяет выделить цитату и скопировать как обычный текст. Работает на всех страницах документа.</li> <li data-num="974"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Retina-чёткость на любом экране</strong> — рендеринг учитывает плотность пикселей устройства. На MacBook и iPad текст не расплывается даже при максимальном зуме.</li> <li data-num="975"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопка «PDF» в спецблоках</strong> — в выпадающем меню «+» рядом с оглавлением и спойлером. Два способа: вставить ссылку на PDF или загрузить файл прямо с компьютера (до 100 МБ). Drag-and-drop тоже работает.
1</li> <li data-num="976"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Загрузка документов на сервер</strong> — PDF хранится в S3 рядом с картинками. Автор загрузил — ссылка навсегда, без внешних зависимостей. Endpoint <code>POST /api/v2/upload-document</code> с magic-bytes валидацией и rate-limiting.</li> <li data-num="977"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Прогресс-бар при загрузке</strong> — видно, сколько из 100 МБ уже улетело. Не приходится гадать, завис ли диалог.</li> <li data-num="978"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>pdf.js грузится только когда нужен</strong> — библиотека (~300 КБ) выделена в отдельный webpack-chunk и подгружается по IntersectionObserver, когда PDF-блок входит во вьюпорт. Основной бандл не потяжелел ни на байт.</li> <li data-num="979"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Worker из npm, не CDN</strong> — pdf.js worker берётся из установленного пакета, а не с внешнего CDN. Нет зависимости от cloudflare, работает при любой сети, версия всегда совпадает.</li> <li data-num="980"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Canvas до 4096px</strong> — на мобильных GPU canvas автоматически уменьшается до безопасного лимита, чтобы не вызвать чёрный экран или вылет вкладки.</li> <li data-num="981"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>JS из PDF заблокирован</strong> — <code>isEvalSupported: false</code> запрещает выполнение скриптов, встроенных в PDF-файл.</li> <li data-num="982"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>ARIA-разметка просмотрщика</strong> — <code>role=&quot;region&quot;</code>, <code>aria-live</code> на номере страницы, <code>role=&quot;toolbar&quot;</code> на панели навигации. Screen reader озвучивает перелистывание.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.82.0</span><span class="changelog-meta__date">16-03-2026</span></span><h2 id="v9820-16-03-2026--мартовский-марш-320-файлов-рефакторинга">Мартовский марш: 320 файлов рефакторинга</h2></summary> <div class="changelog-section__body"> <p>Самое крупное рефакторинг обновление платформы за последние месяцы. Статьи открываются без мерцания, в редакторе появилось форматирование ремарок и 20 новых эмбед-провайдеров, четыре тяжеловесных контроллера расщеплены на модули, а под всем этим — 19 framework-agnostic сервисов и 134 автотеста. 320 файлов изменено, каждый прошёл сквозную проверку перед выкаткой.</p> <ul> <li data-num="951"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Статьи открываются как книга</strong> — серверное превью держится ровно до момента, когда Angular-контент уже готов. Никакой белой вспышки, никакого повторного fade-in картинки. Переход невидим.</li> <li data-num="952"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Embed them all!</strong> Scribd, Bandcamp, TikTok и ещё 32 провайдера — вставляйте ссылку или embed-код, платформа сама распознает источник. Каждый блок — с фирменным фоном, правильными пропорциями и плавной загрузкой.</li> <li data-num="953"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Цитаты стали карточками</strong> — страница <code>/quotes</code> полностью переписана: стопка карточек с анимацией, экспорт в PNG-открытку, копирование и шаринг в соцсети одним кликом.</li> <li data-num="954"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Подкасты больше не тормозят после переходов</strong> — при каждом заходе на эпизод в памяти оставался «наблюдатель» длительности трека. После десятка переходов интерфейс вязнул. Теперь listener аккуратно снимается — память свободна.</li> <li data-num="955"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойной тап — один плеер</strong> — быстрый повторный клик по «Слушать» больше не запускает два плеера на одном треке.</li> <li data-num="956"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Форматирование в ремарках и комментариях</strong> — жирный, курсив, зачёркнутый, ссылки. Bubble-панель над выделением + полный набор хоткеев. Undo/Redo со своим стеком на 50 состояний.</li> <li data-num="957"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Markdown-ссылки <code>[текст](url)</code> везде</strong> — в комментариях, ремарках, ответах. Пишете — читатель видит кликабельную ссылку.</li> <li data-num="958"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Курсор не залипает после вставки блока</strong> — эмбед, видео, галерея, оглавление всегда добавляют пустой параграф после себя. Можно сразу продолжать писать.</li> <li data-num="959"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>**javascript:</code> в ссылках заблокирован** — поле URL отклоняет опасные схемы с shake-анимацией.</li> </ul> <h3 id="производительность-и-стабильность">Производительность и стабильность</h3> <ul> <li data-num="960"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Бандл похудел на 13%</strong> (697 → 604 КБ) — tree-shaking наконец работает. Быстрее первая загрузка — быстрее текст перед глазами.</li> <li data-num="961"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>32 лишних промис-обёртки убраны</strong> — данные ленты, тегов, авторов текут напрямую, без «промиса внутри промиса».</li> <li data-num="962"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кеш не путает рубрики</strong> — при сбое построения ключа все запросы попадали в одну ячейку. Теперь fallback-ключ уникален.</li> <li data-num="963"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авторизация не зависает</strong>
1 — 5-секундный safety net при медленной сети. Страница загрузится, даже если check-login затянулся.</li> <li data-num="964"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Retry с правильным таймингом</strong> — jitter больше не накапливается дважды при повторных запросах к серверу.</li> </ul> <h3 id="архитектура-фундамент-для-будущего">Архитектура: фундамент для будущего</h3> <ul> <li data-num="965">[ARCH] <strong>4 контроллера-гиганта разобраны на модули</strong> — feedCtrl (1100 → 100 строк), editAuthorCtrl (900 → 200), conversationCtrl (830 → 100), draftsCtrl (620 → 100). Логика ленты, профиля, сообщений и черновиков живёт в переиспользуемых sub-controllers.</li> <li data-num="966">[ARCH] <strong>19 framework-agnostic сервисов</strong> — HTTP-клиент, комментарии, сообщения, контент, теги, авторы, OAuth, загрузка файлов, поиск, версии. Один и тот же модуль работает и в Angular, и в будущем React.</li> <li data-num="967">[TEST] <strong>Vitest + 134 теста</strong> — покрыты httpClient, комментарии, контент, логин, OAuth, транслитерация, кеш-менеджер, загрузка файлов, обработка ошибок. Первая настоящая тестовая инфраструктура проекта.</li> <li data-num="968">[ARCH] <strong>Подписки через eventBus</strong> — точечные события вместо <code>$rootScope.$broadcast</code>. Слышат только те компоненты, которым адресовано.</li> </ul> <h3 id="доступность">Доступность</h3> <ul> <li data-num="969"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Уведомления стали ARIA feed</strong> — скринридер озвучивает «уведомление 3 из 12».</li> <li data-num="970"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Touch-таргеты тегов — 44px</strong>, контраст мета-заголовков — 5.7:1 (WCAG AA).</li> <li data-num="971"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Passive listeners</strong> — scroll и touchstart в ленте, модалках и на странице автора больше не блокируют прокрутку.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.81.0</span><span class="changelog-meta__date">16-03-2026</span></span><h2 id="v9810-16-03-2026--scribd-bandcamp-и-тишина-в-консоли">Scribd, Bandcamp и тишина в консоли</h2></summary> <div class="changelog-section__body"> <p>Scribd-документы на платформе были невидимками: браузер тихо блокировал iframe, а читатель видел пустую рамку вместо 365-страничного романа. Заодно починили Bandcamp-плееры артистов и вычистили три надоедливых предупреждения из консоли, которые появлялись на каждой странице.</p> <ul> <li data-num="947"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Scribd-документы загружаются</strong> — браузер блокировал <code>ru.scribd.com</code>, потому что в CSP были прописаны только <code>www</code> и <code>es</code>-поддомены. Теперь wildcard <code>*.scribd.com</code> покрывает все 20+ локализаций Scribd. Если автор вставил ссылку на документ — читатель его увидит.</li> <li data-num="948"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Документы Scribd стали полноэкранными</strong> — высота embed выросла с 600 до 1000px (на мобильных — до 80% экрана). Вместо крохотного превью с кнопкой «Открыть в Scribd» читатель видит сам документ, листает страницы прямо в статье.</li> <li data-num="949"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Bandcamp-плееры артистов работают</strong> — каждый музыкант на Bandcamp живёт на своём поддомене (<code>artist.bandcamp.com</code>). CSP разрешал только голый <code>bandcamp.com</code> — встроенные плееры не грузились. Wildcard решил проблему.</li> <li data-num="950"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Три предупреждения в консоли исчезли</strong> — шрифты Muller Bold, Spectral Regular и main CSS прелоадились дважды: из HTML и из HTTP-заголовка. Service Worker отдавал их из кеша мгновенно, а preload-копия оставалась невостребованной. Убрали дубли — <code>font-display:swap</code> и preload-scanner браузера справляются сами.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.80.0</span><span class="changelog-meta__date">16-03-2026</span></span><h2 id="v9800-16-03-2026--под-капотом-платформа-учится-жить-без-рамок">Под капотом: платформа учится жить без рамок</h2></summary> <div class="changelog-section__body"> <p>Большая внутренняя перестройка, которую вы не увидите — но обязательно почувствуете. 19 ключевых модулей платформы стали независимыми от фреймворка. Подписки, сообщения, теги, авторы, контент — логика каждого теперь живёт отдельно от Angular и готова переехать в любой интерфейс. Параллельно вычищено 32 архитектурных антипаттерна в обработке промисов — и предотвращён баг, который мог бы заморозить интерфейс при загрузке ленты, файлов и тегов.</p> <ul> <li data-num="942"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Лента и теги грузятся отзывчивее</strong> — убраны 32 лишних обёртки вокруг промисов в 11 модулях (лента, теги, авторы, события, поиск, версии, загрузка файлов, OAuth). Каждый запрос к серверу проходил через ненужный «промис внутри промиса» — теперь данные текут напрямую. Меньше аллокаций, проще стеки ошибок, чуть быстрее каждый ответ.</li> <li data-num="943"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Интерфейс гарантированно обновляется</strong> — линтер незаметно заменил внутренний механизм промисов на нативный в пяти к
1ритических модулях (контент, теги, загрузка файлов, YouTube, версии). Нативные промисы не умеют будить Angular — и после загрузки фото, подгрузки ленты или смены тега страница бы просто не перерисовалась. Перехвачено и исправлено до выкатки.</li> <li data-num="944">[ARCH] <strong>Подписки работают через собственную шину событий</strong> — раньше «подписался / отписался» летело через глобальный <code>$rootScope.$broadcast</code>, задевая все компоненты на странице. Теперь <code>followService</code> использует точечный <code>eventBus</code> — слышат только те, кому адресовано. Меньше лишних перерисовок, чище архитектура.</li> <li data-num="945">[ARCH] <strong>19 framework-agnostic модулей</strong> — маппинг авторов, событий, подписок, сообщений, тегов, контента, партнёров, категорий, ремарок, комментариев — всё вынесено в чистые JS-функции без привязки к Angular. Это фундамент для будущего перехода на React: новые компоненты смогут переиспользовать ту же логику без дублирования кода.</li> <li data-num="946">[ARCH] <strong>Кеш контента — единый и переносимый</strong> — кеширование ленты, пулов топ-контента и тегов извлечено из Angular-обёрток в самостоятельные модули. Логика «показать кешированное мгновенно, обновить фоново» теперь testable и не завязана на DI.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.79.0</span><span class="changelog-meta__date">16-03-2026</span></span><h2 id="v9790-16-03-2026--форматирование-в-ремарках">Форматирование в ремарках</h2></summary> <div class="changelog-section__body"> <p>Ремарки и комментарии на полях в редакторе перестали быть plain text. Если выделить текст, над ним появится компактная панель: жирный, курсив, зачёркнутый, ссылка. Ctrl+B/I/D/K работают как в привычном текстовом редакторе. Ссылки теперь можно вставлять в формате <code>[текст](url)</code> — и они превращаются в кликабельные. А под капотом — серия улучшений, которые делают чтение статей плавнее, а набор текста — надёжнее.</p> <ul> <li data-num="929"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Панель форматирования в комментариях</strong> — выделите слово или фразу, и над ней всплывёт тёмный бабл с кнопками <strong>B</strong> <em>I</em> <del>S</del> и ссылкой. Работает и с клавиатуры: стрелками между кнопками, Enter для применения, Escape для отмены. Нет мыши — нет проблем.</li> <li data-num="930"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ссылки в комментариях</strong> — Ctrl+K на выделенном тексте открывает поле для URL. Вставляйте адрес, нажмите Enter — и текст станет ссылкой. Пустой URL снимает существующую ссылку. При наведении на ссылку в редакторе — аккуратный tooltip с адресом.</li> <li data-num="931"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Markdown-ссылки <code>[текст](url)</code></strong> — работают в комментариях, ремарках и ответах. Пишете <code>[Дискурс](https://discours.io)</code> — читатель видит <a href="https://discours.io">Дискурс</a>. Поддержка сквозная: ввод, хранение, отображение.</li> <li data-num="932"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Зачёркнутый текст</strong>
1 — <code>Ctrl+D</code> или кнопка <del>S</del> в панели. Хранится как <code>~~текст~~</code> в markdown. Иногда зачёркнуть — выразительнее, чем удалить.</li> <li data-num="933"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Undo/Redo в комментариях</strong> — <code>Ctrl+Z</code> / <code>Ctrl+Shift+Z</code> работают предсказуемо. Свой стек из 50 состояний — браузерный undo больше не теряет форматирование после применения жирного или ссылки.</li> <li data-num="934"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Статьи не прыгают при загрузке</strong> — hero-изображения получили фиксированные размеры (960×540) и в Angular-шаблонах, и в SSR-ответе Cloudflare. Браузер резервирует место до загрузки картинки — ни одного Layout Shift. Body-изображения без размеров автоматически получают <code>aspect-ratio: 16/9</code> до загрузки.</li> <li data-num="935"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>SSR и Angular больше не рисуют одновременно</strong> — раньше при переходе на статью серверный HTML и Angular-шаблон могли на долю секунды отображаться вместе — двойной контент, скачок layout. Теперь <code>ng-view</code> скрыт, пока SSR-блок на экране. Исчезают синхронно за один кадр.</li> <li data-num="936"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Scroll и touchstart не тормозят</strong> — обработчики <code>touchstart</code> и <code>scroll</code> в ленте, модалке подписок и на странице автора стали passive. Браузер больше не ждёт JavaScript, чтобы начать прокрутку.</li> <li data-num="937"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Touch-таргеты тегов — 44px</strong> — на тач-устройствах метки тем под статьёй стали крупнее. Попасть пальцем в «кино» или «интервью» теперь можно с первого раза (WCAG 2.5.8).</li> <li data-num="938"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Контраст мета-заголовков</strong> — цвет секционных заголовков в теле статьи усилен до 5.7:1. Раньше бледно-серые надписи терялись на светлом фоне — теперь читаются уверенно (WCAG AA).</li> <li data-num="939"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>**javascript:</code> в URL — заблокирован** — поле ввода ссылки в панели форматирования отклоняет опасные схемы (<code>javascript:</code>, <code>data:</code>, <code>vbscript:</code>) с визуальной shake-анимацией. XSS через пользовательские ссылки невозможен.</li> <li data-num="940">[ARCH] <strong>z-index — наконец-то порядок</strong> — вся иерархия слоёв (от loading overlay до formatting bubble) задокументирована в SCSS-переменных. Магические числа заменены на <code>$z-rich-text-bubble</code>, <code>$z-mention-popup</code> и др.</li> <li data-num="941">[ARCH] <strong>Единый парсер markdown-ссылок</strong> — регулярка <code>[text](url)</code> жила в трёх копиях. Теперь — одна функция <code>replaceMarkdownLinks()</code> в общем модуле, импортируемая везде.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.78.0</span><span class="changelog-meta__date">15-03-2026</span></span><h2 id="v9780-15-03-2026--генеральная-уборка-старые-статьи-обретают-новую-разметку">Генеральная уборка: старые статьи обретают новую разметку</h2></summary> <div class="changelog-section__body"> <p>За десять лет в тысячах публикаций накопилась невидимая цифровая пыль: Google Docs оставлял свои ID в каждом абзаце, Word — бессмысленные классы, а вручную свёрстанные подвёрстки и цитаты держались на хрупких inline-стилях, которые не понимал ни один современный инструмент. Мы написали двухэтапную миграцию, которая вычищает мусор и переводит авторскую спецвёрстку в семантические элементы — без потери ни единого символа.</p> <ul> <li data-num="921"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Подвёрстки, цитаты и эпиграфы больше не зависят от inline-стилей</strong> — серые плашки, чёрные подложки, рамки выделения, цитаты-справки с линией слева — всё это жило в <code>&lt;div style=&quot;background: #f1f2f3; border: 3px solid...&quot;&gt;</code>. Теперь каждый приём оформления — именованный элемент (<code>&lt;ta-sub&gt;</code>, <code>&lt;ta-quotation&gt;</code>, <code>&lt;ta-epigraph&gt;</code>, <code>&lt;ta-border-sub&gt;</code>, <code>&lt;ta-selection-frame&gt;</code>), который понимает и редактор, и читательский интерфейс, и будущий React.</li> <li data-num="922"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сноски не пострадали</strong> — каждое правило миграции проверено на совместимость со всеми форматами сносок (от legacy <code>title=&quot;&quot;</code> до новых <code>data-footnote-id</code>). Ни одна из 10 функций очистки не затрагивает теги <code>&lt;a&gt;</code> — сноски в полной безопасности.</li> <li data-num="923"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Выделение маркером стало настоящим <code>&lt;mark&gt;</code></strong>
1 — <code>&lt;span style=&quot;background-color: жёлтый&quot;&gt;</code> превращается в семантический <code>&lt;mark&gt;</code>. Если внутри есть вложенные span&#39;ы — правило безопасно пропускает фрагмент, а не ломает DOM.</li> <li data-num="924"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Якорные ссылки оглавлений сохранены</strong> — пустые теги с <code>id=</code> (якоря для навигации) и <code>data-*</code> атрибутами больше не удаляются при очистке. Оглавления в старых лонгридах продолжат работать.</li> <li data-num="925"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**border-width</code> больше не калечится при очистке** — правило удаления лишних <code>width</code>/<code>height</code> некорректно задевало составные CSS-свойства (<code>border-width</code>, <code>min-height</code>, <code>line-height</code>). Это ломало конвертацию рамок выделения на втором этапе. Исправлено точечным negative lookbehind.</li> <li data-num="926"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Каждый документ сохраняет резервную копию</strong> — оригинальный HTML записывается в поле <code>_htmlCleanupBackup</code> с разделением по этапам. Если что-то пойдёт не так — откат в одну команду.</li> <li data-num="927"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Этап 2 не стартует без завершённого Этапа 1</strong> — если очистка мусора упала, конвертация спецвёрстки не запустится на неочищенных данных. В логах — явное предупреждение, а не тихий пропуск.</li> <li data-num="928">[INFO] <strong>Сложные блоки помечены для ручной правки</strong> — оглавления, блоки «Читайте также» и спойлер-подвёрстки автоматически конвертировать рискованно. Миграция находит их, логирует slug каждой статьи и сохраняет список в базу — редакция точно знает, какие материалы нужно проверить руками.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.77.0</span><span class="changelog-meta__date">15-03-2026</span></span><h2 id="v9770-15-03-2026--без-вспышек-статьи-открываются-как-книга">Без вспышек: статьи открываются как книга</h2></summary> <div class="changelog-section__body"> <p>Статьи открывают сразу и целиком. Заголовок, картинка, текст — всё на месте с первого кадра. Раньше при загрузке публикации контент успевал мелькнуть, исчезнуть и появиться снова — теперь этой вспышки нет. Переход от серверного превью к живой странице стал невидимым.</p> <ul> <li data-num="918"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Статьи больше не мерцают при загрузке</strong> — серверное превью (SSR) оставалось на экране, пока Angular рендерил шаблон маршрута, но исчезало раньше, чем загружался основной контент. Теперь превью держится до момента, когда тело статьи уже готово — смена происходит за один кадр, без белой вспышки.</li> <li data-num="919"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Титульная картинка не моргает</strong> — hero-изображение из SSR было видно сразу, а Angular запускал его повторный fade-in с нуля. Получался странный эффект: картинка гасла и плавно появл
1ялась заново. Убрали: если читатель уже видел картинку — анимация не повторяется.</li> <li data-num="920">[ARCH] <strong>Единый механизм SSR → Angular</strong> — контроллеры статей и экспо-материалов используют общий helper <code>waitForContentAndRemoveSSR</code>. Все ветки — ошибка, редирект, цензура — корректно убирают серверное превью. Два конфликтующих способа удаления SSR заменены одним предсказуемым.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.76.0</span><span class="changelog-meta__date">15-03-2026</span></span><h2 id="v9760-15-03-2026--embed-them-all-вставляйте-что-угодно-откуда-угодно">Embed Them All: вставляйте что угодно, откуда угодно</h2></summary> <div class="changelog-section__body"> <p>Дискурс теперь понимает 30+ источников встраиваемого контента — от TikTok и Reddit до Scribd и Google Maps. Вставьте ссылку или embed-код в редактор — платформа сама распознает провайдера, подберёт пропорции и оформит блок в фирменном стиле. Для читателей — стабильное и красивое отображение на любом устройстве.</p> <ul> <li data-num="909"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>20 новых embed-провайдеров</strong> (всего 35 поддерживаемых) — Scribd, TikTok, Twitch, Reddit, TED, Яндекс.Музыка, Яндекс.Карты, Google Maps, Bandcamp, Apple Music, SlideShare, Issuu, Flickr, Imgur, Coub, Дзен, Pinterest, Piktochart, Apester, Interacty. Каждый — с брендовым фоном-подложкой, корректным соотношением сторон и плавной загрузкой. Вставка одной ссылкой или iframe-кодом.</li> <li data-num="910"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Подсказка в диалоге стала полезной</strong> — при вставке эмбеда автор видит полный список поддерживаемых платформ. Не надо гадать, что сработает.</li> <li data-num="911"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Facebook pfbid-ссылки заработали</strong> — Facebook перешёл на новый формат ID постов (буквенно-цифровой <code>pfbid</code>), а старый паттерн принимал только числа. Теперь оба формата распознаются.</li> <li data-num="912"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>SoundCloud-плейлисты вставляются корректно</strong> — ссылки на плейлисты (<code>/sets/...</code>) раньше обрезались на первом сегменте. Добавлен отдельный паттерн — альбомы и плейлисты встраиваются целиком.</li> <li data-num="913"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Единый whitelist доменов</strong> — три системы (редактор, paste-санитизация, отображение читателю) использовали три разных списка разрешённых доменов, и они уже расходились. Теперь один файл — одна правда. Домен добавляется один раз, работает везде.</li> <li data-num="914"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Эмбеды legacy-контента не пропадают у читателей</strong>
1 — <code>trustedHtml</code>-фильтр удалял <code>scrolling</code>, <code>referrerpolicy</code> и <code>sandbox</code> из iframe-атрибутов. Высокие инфографики (Piktochart) теряли скролл, видео — песочницу. Атрибуты возвращены в whitelist.</li> <li data-num="915"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Protocol-relative URL больше не убивают iframe</strong> — legacy-контент часто содержит <code>src=&quot;//www.instagram.com/...&quot;</code>. Браузер не ломался, а вот DOMPurify — да: <code>new URL(&#39;//&#39;)</code> бросал ошибку, и src удалялся. Теперь <code>//</code> нормализуется в <code>https://</code> перед проверкой.</li> <li data-num="916"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Provider-классы выживают при copy-paste</strong> — html-cleaner стирал классы вроде <code>social-embed-tiktok</code> при вставке из другого материала, и эмбед терял стилизацию. Добавлены все 30+ классов в protected-список.</li> <li data-num="917"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Стили провайдеров на стороне читателя</strong> — каждый embed-блок в опубликованной статье получил свою визуальную оболочку: фирменный цвет, padding, border-radius. TED-лекции — адаптивные 16:9 с чёрным фоном, TikTok — мобильная ширина по центру, Apple Music — градиент с закруглёнными углами, карты — нейтральная рамка.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.75.0</span><span class="changelog-meta__date">15-03-2026</span></span><h2 id="v9750-15-03-2026--релизный-hardening-карточки-авторизация-и-мелочи-которые-решают">Релизный hardening: карточки, авторизация и мелочи, которые решают</h2></summary> <div class="changelog-section__body"> <p>Предрелизная полировка: устранены race conditions при авторизации, редактор стал предсказуемее, карточки ссылок обрели тёмную тему и адекватные пропорции, а уведомления — полноценную доступность. Две дюжины точечных улучшений, которые по отдельности незаметны, а вместе делают платформу ощутимо надёжнее.</p> <ul> <li data-num="897"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авторизация больше не теряется на старте</strong> — при медленной сети check-login мог зависнуть, и страница уведомлений, сообщений или редакции никогда не загружалась. Теперь есть 5-секундный safety net с защитой от двойного срабатывания — интерфейс всегда получит сигнал «авторизация проверена».</li> <li data-num="898"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Курсор в редакторе не залипает после вставки блока</strong> — вставка ссылки, эмбеда, видео, галереи или оглавления теперь всегда добавляет пустой параграф после блока. Курсор встаёт в него — можно сразу продолжать писать, а не бороться с DOM.</li> <li data-num="899"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Sentry не падает, когда не инициализирован</strong> — если мониторинг ошибок не запустился (dev-режим, блокировщик), вызов <code>withScope</code> бросал TypeError. Теперь safe proxy корректно эмулирует все методы Sentry — ни один вызов не уронит страницу.</li> <li data-num="900"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Retry с правильным таймингом</strong> — при повторных запросах к серверу jitter накапливался дважды, и задержки росли быстрее, чем нужно. Исправлено: экспоненциальный backoff теперь чистый, jitter добавляется ровно один раз за итерацию.</li> <li data-num="901"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Карточки ссылок в тёмной теме</strong> — OG-превью вставленных URL наконец-то уважают <code>body.dark</code>: тёмный фон, светлый текст, контрастные hover-эффекты. Раньше белые карточки слепили на тёмном фоне.</li> <li data-num="902"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Карточки ссылок стали просторнее</strong> — max-height увеличен со 160 до 180px, изображения масштабируются пропорционально (не обрезаются жёстко). Описание в три строки больше не сжимается в нечитаемую полоску.</li> <li data-num="903"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Выделенные блоки в редакторе лучше видны</strong> — outline при выделении изображения, эмбеда или карточки теперь отступает на 1px от содержимого (было 0 — outline сливался с границей блока).</li> <li data-num="904"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Правки автора доставляются даже при навигации<
1/strong> — уведомление бэкенда о новых правках теперь отправляется через <code>sendBeacon</code> при уходе со страницы. Раньше обычный fetch мог быть прерван браузером, и правки терялись.</li> <li data-num="905"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Уведомления — полноценный feed</strong> — <code>role=&quot;feed&quot;</code> с <code>aria-posinset</code> и <code>aria-setsize</code> на каждой карточке. Скринридер озвучивает «уведомление 3 из 12» — навигация вслепую стала осмысленной.</li> <li data-num="906">[ARCH] <code>**onAuthReady</code> — одна утилита вместо пяти копий** — паттерн «подожди auth:checked, потом загружай данные» был продублирован в пяти контроллерах. Теперь это одна функция в 10 строк, которая гарантирует правильную отписку при уничтожении scope.</li> <li data-num="907">[ARCH] <strong>Свойства контроллера уведомлений стали публичными</strong> — <code>_isLoading</code>, <code>_filteredItems</code>, <code>_cachedCounts</code> и ещё 7 свойств с подчёркиванием переименованы в <code>isLoading</code>, <code>filteredItems</code>, <code>counts</code>. Шаблон — публичный API компонента, и имена теперь это отражают.</li> <li data-num="908"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Длинная ремарка не прячет кнопки</strong> — раньше при большом тексте в окне ремарки поле ввода росло бесконечно, и кнопки «Отправить» / «Отменить» уезжали за экран. Теперь textarea ограничена по высоте (45vh) с тонким элегантным скроллом — пишите хоть роман, кнопки всегда под рукой.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.74.0</span><span class="changelog-meta__date">15-03-2026</span></span><h2 id="v9740-15-03-2026--код-который-не-мешает-читать">Код, который не мешает читать</h2></summary> <div class="changelog-section__body"> <p>Весь JavaScript платформы переведён на современные ES-модули. Для читателя и автора ничего не изменилось — а страницы стали загружаться быстрее. Это последний крупный шаг перед тем, как Дискурс начнёт переезжать на React: теперь любой модуль одинаково работает и в старом Angular, и в будущем стеке.</p> <ul> <li data-num="893"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Главный бандл похудел на 13%</strong> — 697 → 604 КБ. Webpack теперь видит, какие функции реально используются, и выбрасывает остальное (tree-shaking). Конкретно: включены <code>usedExports</code> и <code>sideEffects</code> — впервые за историю проекта мёртвый код удаляется автоматически. Быстрее первая загрузка — быстрее статья перед глазами.</li> <li data-num="894"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Lazy-чанки загружают все свои зависимости</strong> — при ленивой подгрузке страницы (уведомления, подкасты, редактор) Angular получал неполный список модулей — баг жил с момента перехода на webpack. Починено: теперь ocLazyLoad инжектирует полный набор (контроллеры, директивы, компоненты, шаблоны), а не только один дефолтный модуль.</li> <li data-num="895">[ARCH] <strong>50 файлов — на ES modules</strong>
1 — <code>require()</code> → <code>import</code>, <code>module.exports</code> → <code>export</code>. Точка входа (<code>main.js</code>), 17 lazy-лоадеров, утилиты, фабрики, сервисы, контроллеры — всё говорит на одном языке. Код стал портируемым: один и тот же модуль можно подключить и в Angular, и в React, и в чистом JS.</li> <li data-num="896">[ARCH] <strong>Sentry перестал зависеть от фреймворка</strong> — мониторинг ошибок подключается через стандартный <code>import</code>, а не Angular DI. Один и тот же SDK работает и сейчас, и после миграции — ни одна ошибка не ускользнёт при переезде.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.73.0</span><span class="changelog-meta__date">14-03-2026</span></span><h2 id="v9730-14-03-2026--быстрее-надёжнее-доступнее">Быстрее, надёжнее, доступнее</h2></summary> <div class="changelog-section__body"> <p>Финальная полировка перед релизом. Страницы открываются ещё до клика, кнопки управления слышны скринридерам, ошибки сети не путают платформу, а колокольчик уведомлений снова показывает счётчик в заголовке вкладки.</p> <ul> <li data-num="883"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Статьи загружаются до клика</strong> — при наведении курсора на ссылку платформа тихо предзагружает данные материала. Когда вы кликаете — страница уже готова. На мобильных работает через long-tap. LRU-кеш на 30 материалов, TTL 60 секунд — никакой нагрузки на сервер.</li> <li data-num="884"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Одинаковые запросы не дублируются</strong> — если три компонента одновременно запрашивают один и тот же URL, на сервер уходит один запрос, а результат раздаётся всем. Снижает нагрузку на API при тяжёлых страницах.</li> <li data-num="885"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Счётчик непрочитанных вернулся в заголовок вкладки</strong> — <code>(5) Дискурс</code> в заголовке вкладки браузера, как раньше. Favicon-бейдж тоже остался — теперь непрочитанные видны и когда вкладок много, и когда мало.</li> <li data-num="886"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Офлайн и онлайн — честные уведомления</strong> — при потере сети появляется деликатный toast «Нет соединения с интернетом», при возвращении — «Соединение восстановлено». Запросы не отправляются в пустоту, circuit breaker не срабатывает ложно.</li> <li data-num="887"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Подписка в подвале стала отзывчивее</strong> — защита от двойного клика, кнопки «Попробовать снова» и «Другой e-mail» при ошибке, корректное состояние «уже подписан» по HTTP 409. Раньше ошибка подписки была тупиком без выхода.</li> <li data-num="888"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Кнопки Play/Pause стали настоящими кнопками</strong> — <code>&lt;i&gt;</code> с <code>role=&quot;button&quot;</code> заменены на семантические <code>&lt;button&gt;</code> в плеере экспо и подкастов. Скринридер озвучивает «Воспроизвести» и «Пауза», клавиатурная навигация работает из коробки.</li> <li data-num="889"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Enter и Space работают на всех интерактивных элементах</strong> — глобальный keyboard handler (event delegation) для всех элементов с <code>role=&quot;button&quot;</code>. Соцкнопки, иконки закрытия, элементы списков — всё доступно с клавиатуры.</li> <li data-num="890"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Баги в grid-шаблонах</strong> — в двух шаблонах сетки (<code>grid/3</code>, <code>grid/8</code>) счётчики комментариев и просмотров показывали данные от чужого материала. Исправлено: <code>items[3].views</code> → <code>item.views</code>.</li> <li data-num="891"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Форма входа стала безопаснее</strong> — при сбросе пароля платформа больше не раскрывает, существует ли аккаунт. Вместо «Такого email нет» — единообразное «Если email зарегистрирован, мы отправили ссылку».</li> <li data-num="892">[ARCH] <strong>Все фильтры — в одном Angular-модуле</strong> — 20 текстовых фильтров перенесены из <code>discours.controllers</code> в <code>discours.filters</code>. Чистая архитектура, единообразие, на шаг ближе к независимости от фреймворка.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.72.0</span><span class="changelog-meta__date">14-03-2026</span></span><h2 id="v9720-14-03-2026--призраки-уведомлений-изгнаны-и-обезврежены">Призраки уведомлений: изгнаны и обезврежены</h2></summary> <div class="changelog-section__body"> <p>Удалили комментарий — а уведомление о нём осталось навсегда. Счётчик в колокольчике показывал 25, а на странице — три карточки. Клик по карточке не уменьшал счётчик. Переключение между «Комментарии» и «Ремарки» показывало пустоту на одной из вкладок, хотя бейдж утверждал обратное. Причина у всех этих багов одна: удаление комментария или ремарки не трогало связанные уведомления — они оставались в базе как призраки, невидимые, но учитываемые. Мы это починили — и на уровне сервера, и на уровне интерфейса.</p> <ul> <li data-num="878"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Удалил комментарий — уведомления уходят вместе с ним</strong> — при удалении комментария или ремарки сервер теперь каскадно удаляет все связанные уведомления (включая упоминания). Один запрос с точным фильтром по типу — без риска задеть чужие данные. Новых призраков больше не будет.</li> <li data-num="879"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Счётчик и список наконец-то говорят одно и то же</strong> — раньше бейдж на вкладке считал все уведомления (включая скрытых призраков), а список показывал только «живые». Теперь уведомления-сироты не прячутся — они отображаются с пометкой «Удалено», их можно прочитать, отметить или смахнуть. Что видишь — то и есть.</li> <li data-num="880"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Клик по карточке обновляет счётчик</strong> — раньше <code>markAsRead</code> помечал только видимые уведомления, а скрытые призраки оставались непрочитанными. Колокольчик упрямо показывал ту же цифру. Теперь все уведомления по материалу — в едином списке, и клик помечает их все.</li> <li data-num="881"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Удалено» вместо призрачного текста</strong> — если комментарий или ремарка удалены, вместо устаревшего снимка текста — аккуратная серая курсивная метка. Кнопка «Ответить» скрыта — нельзя ответить в пустоту.</li> <li data-num="882"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Индекс для каскадного удаления</strong> — составной индекс <code>documentId + documentCollection</code> в коллекции уведомлений: удаление идёт по индексу, а не полным сканом.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.71.0</span><span class="changelog-meta__date">14-03-2026</span></span><h2 id="v9710-14-03-2026--под-капотом-платформа-учится-жить-без-фреймворка">Под капотом: платформа учится жить без фреймворка</h2></summary> <div class="changelog-section__body"> <p>Дискурс постепенно отвязывается от AngularJS — и это первый большой шаг. Запросы к серверу, авторизация, обработка ошибок — всё переехало из Angular-фабрик в чистый JavaScript. Для читателей и авторов ничего не изменилось (и это лучший комплимент рефакторингу), а для будущего платформы — изменилось всё: теперь можно строить новые компоненты, не оглядываясь на десятилетний фреймворк.</p> <ul> <li data-num="873"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Запросы стали быстрее и надёжнее</strong> — весь обмен с сервером переведён на нативный Fetch API вместо Angular-обёртки <code>$http</code>. Один HTTP-клиент (~640 строк) вместо дублирования логики в пяти местах: авторизация, retry с экспоненциальной задержкой, circuit breaker, классификация ошибок — всё работает единообразно. Если сервер икнул — платформа тихо повторит запрос, и автор даже не заметит.</li> <li data-num="874"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Ошибки стали понятнее</strong> — «Нет соединения с сервером», «Слишком много запросов», «Ошибка авторизации» — одинаковые формулировки и в редакторе, и в комментариях, и на странице сообщений. Раньше каждый модуль изобретал свои тексты, и при одной и той же проблеме пользователь видел разные сообщения.</li> <li data-num="875"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Подвал и аудиоплеер грузятся эффективнее</strong> — footer и музыкальный плеер превратились из вставляемых шаблонов в полноценные компоненты (<code>&lt;site-footer&gt;</code>, <code>&lt;music-player&gt;</code>). Меньше лишних DOM-элементов, быстрее рендер ленты — из 49 grid-шаблонов убраны пустые контроллеры, которые создавались при каждой загрузке страницы, не делая ничего.</li> <li data-num="876">[ARCH] <strong>21 фильтр стал переиспользуемым</strong> — каждый текстовый фильтр (форматирование дат, упоминания, обрезка текста) теперь доступен за пределами Angular. Мемоизация и кэши — на месте, но теперь эти же функции работают в Tiptap-редакторе и будут доступны в будущих React-компонентах.</li> <li data-num="877">[ARCH] <strong>
17 сервисов живут отдельно от фреймворка</strong> — подписки, ремарки, обратная связь, категории, авторизация — вынесены в чистые ES-модули без зависимостей от Angular. Текущий код продолжает работать через Angular DI, а новый (и будущий React) импортирует напрямую — один и тот же путь к серверу для всех.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.70.0</span><span class="changelog-meta__date">14-03-2026</span></span><h2 id="v9700-14-03-2026--уведомления-ничего-не-теряется-всё-находится">Уведомления: ничего не теряется, всё находится</h2></summary> <div class="changelog-section__body"> <p>Ремарки пропадали со страницы уведомлений. Якорные ссылки из колокольчика открывали статью, но не прокручивали к нужному месту. Переключение между вкладками «Комментарии» и «Ремарки» превращалось в лотерею — одно появлялось, другое исчезало. Мы разобрались с корнем каждой проблемы: от конфликта DDP-подписок до рассинхронизации ID после миграции. Теперь уведомления работают так, как должны были с самого начала.</p> <ul> <li data-num="862"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ремарки больше не исчезают при переключении вкладок</strong> — переход между «Комментарии» и «Ремарки» на странице уведомлений вызывал конфликт DDP-подписок: Meteor удалял документы одного типа, загружая другой. Теперь базовая подписка (все типы) живёт постоянно, а фильтрованная добавляется поверх — никто никого не вытесняет.</li> <li data-num="863"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Якорные ссылки на ремарки доведут до цели</strong> — клик по уведомлению о ремарке открывал статью, но не прокручивал к нужному месту. Причина: после миграции ремарок в InlineComments ID изменились, а ссылки в уведомлениях хранили старые. Добавили поддержку всех трёх форматов хешей (<code>#remark-{id}</code>, <code>#comment-{id}</code>, <code>#remark-{migrated_id}</code>) с автоматическим сопоставлением — теперь каждая ссылка ведёт точно туда, куда обещает.</li> <li data-num="864"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Якорные ссылки на комментарии тоже работают</strong> — <code>scrollIntoView</code> с плавной прокруткой и экспоненциальным retry (до 3 секунд) — если комментарий ещё не отрендерился, страница терпеливо дождётся и прокрутит. Работает и при SPA-навигации, и при открытии ссылки в новой вкладке.</li> <li data-num="865"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Lazy-render больше не прячет цели якорей</strong> — при переходе по ссылке с хешем (<code>#comment-...</code>, <code>#remark-...</code>) секция under-the-fold рендерится мгновенно, а не ждёт попадания во вьюпорт. Проверяем и <code>$location.hash()</code> (актуален при SPA-навигации), и <code>window.location.hash</code> (fallback) — ни один формат не ускользнёт.</li> <li data-num="866"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Утечка таймеров при переключении вкладок</strong> — быстрое переключение между вкладками уведомлений создавало «призрачные» таймеры, которые могли обновить интерфейс устаревшими данными. Каждый новый вызов теперь отменяет предыдущие.</li> <li data-num="867"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Подсчёт непрочитанных за O(N) вместо O(N×K)</strong> — предвычисленный обратный маппинг <code>documentCollection → тип вкладки</code> заменил вложенный цикл с <code>indexOf</code>. На 50 уведомлениях — разница незаметна, на 500 при «Загрузить ещё» — ощутима.</li> <li data-num="868"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Поиск документов без лишних аллокаций</strong> — при раскрытии списка уведомлений каждая карточка искала связанный документ через <code>.concat()</code> двух массивов. Теперь поиск идёт последовательно по обоим без создания промежуточного массива.</li> <li data-num="869"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Вкладки уведомлений — правильные ARIA-табы</strong> — <code>role=&quot;tab&quot;</code>, <code>aria-selected</code>, <code>role=&quot;tabpanel&quot;</code> — скринридер корректно озвучивает навигацию между фильтрами. Раньше вкладки были просто ссылками без семантики.</li> <li data-num="870"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Анимация ремарок уважает <code>prefers-reduced-motion</code></strong> — подсветка целевой ремарки (пульсация золотым) отключается, если пользователь попросил уменьшить анимацию. Покрыта и светлая, и тёмная тема.</li> <li data-num="871"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Бейджи непрочитанных на вкладках</strong> — счётчики превратились из невзрачных цифр в яркие pill-бейджи — синий фон, белый текст, мягкие скругления. Непрочитанные видны с первого взгляда.</li> <li data-num="872"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Плавный индикатор активной вкладки</strong> — подчёркивание раскрывается от центра с мягким easing вместо мгновенного появления. Мелочь, но навигация ощущается отзывчивее.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.69.0</span><span class="changelog-meta__date">13-03-2026</span></span><h2 id="v9690-13-03-2026--тише-едешь-дальше-будешь-охота-на-призраков-sentry">Тише едешь, дальше будешь: охота на призраков Sentry</h2></summary> <div class="changelog-section__body"> <p>Десяток ошибок, которые копились в Sentry и портили жизнь читателям — молча ломали аудиоплеер на музыкальных страницах, показывали «страница не найдена» вместо «сервер недоступен», роняли комментарии без объяснений. Мы их нашли и устранили. Платформа стала заметно устойчивее — особенно на медленных соединениях и мобильных.</p> <ul> <li data-num="855"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Аудиоплеер больше не падает на пустых треках</strong> — если в плейлисте оказывался элемент без источника (например, при загрузке страницы с битыми данными), весь плеер вылетал с TypeError. Теперь пустые элементы тихо пропускаются — музыка играет.</li> <li data-num="856"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>jQuery перестал пропадать в webpack-сборке</strong> — редкий, но воспроизводимый баг: при определённом порядке загрузки скриптов webpack-модули не видели jQuery, и вся страница рушилась. Причина — обращение к голой переменной <code>jQuery</code> вместо <code>window.jQuery</code> в shim-слое.</li> <li data-num="857"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Комментарии не тонут молча</strong> — раньше если API комментариев отвечал таймаутом или ошибкой сети, блок под статьёй просто оставался пустым, а в Sentry улетал unhandled rejection. Теперь ошибка логируется, а интерфейс не ломается. Бонус: если материал ещё не загружен — бессмысленный запрос за комментариями даже не отправляется.</li> <li data-num="858"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Не удалось загрузить» вместо «не найдено»</strong> — при ошибках сервера (502, 526, таймаут) страница сообщений показывала «Вы попали на несуществующую страницу» — с формой поиска. Теперь корректно: «Не удалось загрузить страницу» с кнопкой «Обновить».</li> <li data-num="859"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>SSL-ошибки Cloudflare — отдельная категория</strong> — HTTP 525/526 (проблемы с сертификатом между Cloudflare и сервером) теперь распознаются как <code>ssl</code>, получили внятное сообщение и исключены из бесполезных retry-попыток. Раньше клиент молча повторял запрос трижды — и трижды получал тот же ответ.</li> <li data-num="860"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ссылки с мусором больше не ломают страницу</strong> — URL вроде <code>/articles/culture/art-violence%0A</code> (с символом переноса строки — привет, кривые парсеры соцсетей) раньше пролетали через Cloudflare Worker и приводили к ошибке шаблонизации. Теперь Worker чистит управляющие символы из пути и делает 301-редирект на нормальный адрес.</li> <li data-num="861"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Обложки статей вернулись из небытия</strong> — на ряде материалов (в том числе многолетних лонгридов вроде «Евромайдан: 5 историй») вместо заглавных картинок зияла пустота, а в консоли — молчаливый 500. Причина: Thumbor считал эти изображения «декомпрессионной бомбой» — они превышали лимит в 20 млн пикселей (а у фотографий было 23 млн). Подняли порог до 30 млн, почистили nginx-кэш — обложки снова на месте.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.68.0</span><span class="changelog-meta__date">13-03-2026</span></span><h2 id="v9680-13-03-2026--импортэкспорт-пишите-где-удобно-публикуйте-на-дискурсе">Импорт/экспорт: пишите где удобно, публикуйте на Дискурсе</h2></summary> <div class="changelog-section__body"> <p>Тексты живут не только в нашем редакторе. Кто-то пишет в Word, кто-то — в Obsidian, кто-то переезжает с Medium. Раньше перенос материала на платформу означал ручное копирование с потерей форматирования и картинок. Теперь — одна кнопка, один клик, и текст на месте. В обратную сторону тоже работает: статью можно забрать с собой в Markdown, PDF или Word.</p> <ul> <li data-num="846"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Импорт из Word — с картинками</strong> — перетащите .docx-файл в редактор, и через пару секунд текст появится с сохранением заголовков, списков и форматирования. Изображения из документа автоматически загружаются на серверы Дискурса — не нужно перезаливать вручную. Прогресс-бар показывает, сколько картинок загружено.</li> <li data-num="847"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Импорт из Markdown</strong> — .md-файл из Obsidian, Notion, Bear или любого другого редактора. YAML-frontmatter (title, tags) распознаётся и сохраняется. Просто выберите файл — остальное сделает платформа.</li> <li data-num="848"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Импорт из Medium</strong> — при загрузке HTML-файла из Medium-экспорта платформа автоматически распознаёт формат: убирает служебную разметку, исправляет уровни заголовков, перезаливает изображения с CDN Medium Ð
1½Ð° Дискурс. Переезд с Medium — за минуту, а не за час.</li> <li data-num="849"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Экспорт в Markdown</strong> — скачайте статью как .md-файл с YAML-метаданными (заголовок, slug, теги, дата) или скопируйте Markdown прямо в буфер обмена для вставки в Telegram, GitHub, Notion. Все кастомные элементы Дискурса — сноски, подвёрстки, эпиграфы, галереи — корректно конвертируются.</li> <li data-num="850"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Экспорт в PDF</strong> — статья скачивается как оформленный PDF: Georgia, A4, корректные отступы и изображения. Удобно для отправки на вычитку или архивирования.</li> <li data-num="851"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Экспорт в Word</strong> — .docx с заголовками, списками, ссылками и встроенными изображениями. Откройте в Word или Google Docs — форматирование на месте.</li> <li data-num="852"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Drag &amp; drop</strong> — файл можно перетащить прямо в модалку. Или кликнуть на зону и выбрать через проводник. Автоопределение формата по расширению и содержимому — думать не нужно.</li> <li data-num="853"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Предпросмотр перед импортом</strong> — прежде чем заменить содержимое редактора, модалка показывает превью документа. Предупреждение «текущий контент будет заменён» не даёт случайно перезаписать работу.</li> <li data-num="854"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Всё lazy-loaded</strong> — библиотеки mammoth (30 КБ), turndown (8 КБ), docx (90 КБ), html2pdf (200 КБ) загружаются только при использовании. Основной бандл не прибавил ни байта.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.67.0</span><span class="changelog-meta__date">13-03-2026</span></span><h2 id="v9670-13-03-2026--поиск-авторов-находит-всех-а-не-только-первых-пятьдесят">Поиск авторов: находит всех, а не только первых пятьдесят</h2></summary> <div class="changelog-section__body"> <p>Поиск адресатов на странице сообщений работал вхолостую: загружал первые 50 авторов и фильтровал локально — только по имени. Остальные сотни пользователей просто не существовали для поисковой строки. Теперь поиск серверный, мгновенный и ищет по шести полям сразу.</p> <ul> <li data-num="839"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Все авторы — в поиске</strong> — раньше компонент загружал первую страницу пользователей (50 человек) и больше не спрашивал. Если нужный автор не попал в этот список — его было Ð
1½Ðµ найти ни по имени, ни по почте, никак. Переключили на серверный поиск <code>GET /api/v2/users/search</code> — теперь находится каждый зарегистрированный пользователь.</li> <li data-num="840"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Поиск по имени, фамилии, email, нику и био</strong> — ввели «журналист» — нашли автора, у которого это слово в описании профиля. Ввели часть email — тоже найдётся (адрес показывается замаскированным: <code>a***@gmail.com</code>). Шесть полей вместо одного.</li> <li data-num="841"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Клавиатурная навигация</strong> — стрелки ↑↓ перемещают по списку, Enter выбирает, Escape закрывает. Активный результат подсвечивается и прокручивается в видимую область — работает и мышкой, и клавиатурой, и вперемешку.</li> <li data-num="842"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Спиннер вместо молчания</strong> — пока идёт поиск, иконка лупы превращается в спиннер прямо в поле ввода. Один символ — подсказка «Введите минимум 2 символа». Нет результатов — честное «Авторы не найдены», а не зависшая пустота.</li> <li data-num="843"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Ошибки объясняются</strong> — при превышении лимита запросов (30 в минуту) появляется понятный тост «Подождите немного» вместо молчаливого пустого списка.</li> <li data-num="844"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Escape не ломает интерфейс</strong> — нажатие Escape в поле поиска закрывает только выпадающий список, не трогая панель участников и модалку создания группы. Клик вне компонента тоже корректно закрывает dropdown.</li> <li data-num="845"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>ARIA-разметка</strong> — <code>role=&quot;combobox&quot;</code> на поле ввода, <code>role=&quot;listbox&quot;</code> на списке, <code>role=&quot;option&quot;</code> и <code>aria-selected</code> на каждом результате. Скринридер озвучивает навигацию по списку.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.66.0</span><span class="changelog-meta__date">13-03-2026</span></span><h2 id="v9660-13-03-2026--эмодзи-в-сообщениях-одна-кнопка--64-эмоции">Эмодзи в сообщениях: одна кнопка — 64 эмоции</h2></summary> <div class="changelog-section__body"> <p>Переписка на Дискурсе стала выразительнее. В панели инструментов редактора сообщений появилась пятая кнопка — смайлик, за которым прячется аккуратная панель с 64 эмодзи в четырёх категориях. Без внешних библиотек, без задержек загрузки — всё встроено и работает мгновенно.</p> <ul> <li data-num="835"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Эмодзи-пикер в сообщениях</strong> — нажмите смайлик в тулбаре, выберите эмодзи из сетки, оно вставится прямо в текст на позицию курсора. Четыре вкладки — «Часто», «Лица», «Жесты», «Символы» — чтобы нужное нашлось за секунду, а не за минуту скроллинга.</li> <li data-num="836"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Плавное появление</strong> — панель выезжает с лёгким fade и сдвигом, а не возникает из ниоткуда. Кнопка подсвечивается, пока пикер открыт — всегда понятно, что к чему.</li> <li data-num="837"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Закрывается как ожидаешь</strong> — клик снаружи, Escape, нажатие другой кнопки туÐ
1»Ð±Ð°Ñ€Ð° — панель закроется. Повторный клик по смайлику — тоже. Никаких зависших попапов.</li> <li data-num="838"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мобильные и тач</strong> — на телефонах эмодзи не «залипают» при касании. Hover-эффект увеличения работает только с мышкой, а для пальцев — тактильная подсветка при нажатии.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.65.0</span><span class="changelog-meta__date">13-03-2026</span></span><h2 id="v9650-13-03-2026--ctrlh-найти-и-заменить-прямо-в-редакторе">Ctrl+H: найти и заменить прямо в редакторе</h2></summary> <div class="changelog-section__body"> <p>Авторы просили — сделали. Теперь в редакторе материалов работает полноценный поиск с заменой, как в настоящем текстовом редакторе. Никаких расширений, никаких внешних инструментов — всё внутри, под рукой.</p> <ul> <li data-num="831"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Поиск и замена по Ctrl+H</strong> — нажмите комбинацию в любом месте редактора, и в правом верхнем углу появится компактная панель. Вводите запрос — все совпадения мгновенно подсвечиваются жёлтым, текущее — оранжевым. Стрелки или Enter/Shift+Enter перемещают между находками, счётчик «3 из 12» показывает, где вы. «Заменить» меняет одно вхождение, «Все» — разом все (и один Ctrl+Z откатывает всю пакетную замену целиком).</li> <li data-num="832"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Выделил → нашёл</strong> — если перед Ctrl+H выделить фрагмент текста, он автоматически подставится в поле поиска. Повторное нажатие Ctrl+H закрывает панель. Escape тоже закрывает — фокус возвращается в текст.</li> <li data-num="833"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Регистр под контролем</strong> — кнопка «Aa» переключает режим поиска: с учётом регистра или без. Когда совпадений нет — красная надпись «Нет совпадений», а не молчаливая пустота.</li> <li data-num="834"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Коллаборация без сюрпризов</strong> — если соавтор редактирует текст, пока у вас открыт поиск, позиции совпадений пересчитываются перед каждой заменой. Подсветка — локальная, соавтор её не видит. В режиме предпросмотра замена заблокирована — только просмотр.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.64.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9640-12-03-2026--один-retry-на-всех-ошибки-больше-не-теряются-в-переводе">Один retry на всех: ошибки больше не теряются в переводе</h2></summary> <div class="changelog-section__body"> <p>Пять разных мест в коде пытались повторить запрос при сбое — каждое по-своему, с разными задержками, разным поведением и разными багами. Теперь вместо этого зоопарка — единый механизм с экспоненциальной задержкой и точной настройкой. А рядом появился центр обработки ошибок, который сам решает: залогировать, показать пользователю или подождать.</p> <ul> <li data-num="825"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Единый retry для всех запросов</strong>
1 — <code>apiClient.withRetry()</code> заменил пять рукописных реализаций в ремарках, загрузке изображений, версионировании и сохранении форм. Экспоненциальная задержка, настраиваемый предикат «стоит ли повторять» и колбэк на каждую попытку — всё в одном месте. Если сервер на секунду икнул — платформа спокойно повторит, и автор даже не заметит.</li> <li data-num="826"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Центр обработки ошибок</strong> — <code>apiErrorHandler</code> берёт на себя рутину: распарсить ответ сервера, записать в лог, показать тост. Один вызов вместо трёх-пяти строк в каждом catch-блоке. Поддерживает троттлинг уведомлений по ключу — если сервер лежит минуту, вы увидите одно предупреждение, а не двадцать.</li> <li data-num="827"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тост с кнопкой действия</strong> — при ошибке сохранения статьи появляется «Повторить» прямо в уведомлении. Один клик — и повторная попытка, без необходимости искать кнопку на странице. Раньше этот паттерн был скопирован в два файла — теперь один переиспользуемый хелпер.</li> <li data-num="828"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Отменённые запросы не порождают призраков</strong> — при навигации между страницами незавершённые запросы тихо отменяются. Раньше отменённый запрос мог проскочить в обработчик ошибок и показать пустой тост. Теперь <code>parseError</code> возвращает <code>message: null</code> для canceled — ни лога, ни уведомления.</li> <li data-num="829"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Текст кнопки в тосте экранируется</strong> — даже если когда-нибудь текст кнопки придёт с сервера, XSS-инъекция через него невозможна.</li> <li data-num="830"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Минус зависимость <code>$timeout</code></strong> — remarksService и versioningService больше не тянут Angular&#39;овский <code>$timeout</code> для задержек retry. Задержки живут внутри <code>withRetry</code> через нативный <code>setTimeout</code>, а digest-цикл запускается через <code>$q</code> — как и должно быть.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.63.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9630-12-03-2026--минус-90-кб-ui-bootstrap-на-покой">Минус 90 КБ: ui-bootstrap на покой</h2></summary> <div class="changelog-section__body"> <p>Страницы Дискурса стали загружаться быстрее — из основного бандла убрана библиотека angular-ui-bootstrap целиком. 277 КБ кода, из которого использовались три компонента. Три. Из сорока. Теперь вместо них — точечные замены по 1–2 КБ каждая, написанные под задачи платформы.</p> <ul> <li data-num="819"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>–90 КБ из стартового бандла</strong>
1 — angular-ui-bootstrap удалён из vendor-чанка. Всё, что грузится при первом заходе, стало легче — это ускоряет и парсинг, и первую отрисовку. Особенно заметно на мобильных и слабых соединениях.</li> <li data-num="820"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Поповеры голосования без балласта</strong> — четыре поповера в кнопках рейтинга работают через <code>simplePopover</code> (1.5 КБ) вместо полной ui-bootstrap tooltip/popover-системы (25 КБ). Позиционирование, outsideClick, шаблоны внутри — всё на месте, только без тридцати семи неиспользуемых модулей рядом.</li> <li data-num="821"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Теги в редакторе: свой typeahead</strong> — автодополнение тем работает через <code>simpleTypeahead</code> (~0.5 КБ) с debounce, стрелочной навигацией и подсветкой совпадений. Stale-ответы при быстром вводе отсекаются по счётчику генераций — показываются только актуальные результаты.</li> <li data-num="822"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Утечки памяти закрыты</strong> — scope&#39;ы dropdown-элементов уничтожаются при каждом обновлении, <code>$timeout</code> отменяются при уничтожении директив, scroll-обработчики снимаются. В старой библиотеке за этим следил фреймворк; теперь — ручной контроль, зато точечный.</li> <li data-num="823"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Скролл не убивает поповер</strong> — если рейтинг открыт и пользователь прокрутил страницу, поповер закрывается аккуратно. Но скролл <em>внутри</em> самого поповера (длинный список голосов) — не закрывает. Раньше ui-bootstrap не различал эти два случая.</li> <li data-num="824"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>XSS-защита в заголовках поповеров</strong> — <code>popover-title</code> экранируется перед вставкой в DOM. В текущих шаблонах заголовки статичны, но теперь защита есть на уровне директивы.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.62.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9620-12-03-2026--ctrls-для-черновиков-поиск-в-«читайте-также»-и-доведённые-до-блеска-групповые-чаты">Ctrl+S для черновиков, поиск в «Читайте также» и доведённые до блеска групповые чаты</h2></summary> <div class="changelog-section__body"> <p>Релиз, после которого редактор перестаёт чувствоваться как черновик, а групповые чаты — как MVP. Expo-материалы теперь сохраняются по Ctrl+S, диалог «Читайте также» сам ищет статьи по наÐ
1·Ð²Ð°Ð½Ð¸ÑŽ, а групповой чат получил мгновенное переименование и защиту от потери данных при закрытии.</p> <ul> <li data-num="797"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ctrl+S / Cmd+S в редакторе Искусства</strong> — привычная комбинация работает из любого места на странице: из поля заголовка, из тела статьи, из формы обложки. Для существующих материалов — мгновенное сохранение на сервер. Для новых — запускает первое ручное сохранение. Ничего не нужно запоминать — пальцы уже знают, что делать.</li> <li data-num="798"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Поиск материалов платформы в «Читайте также»</strong> — вместо копирования ссылок вручную начните вводить название статьи: редактор найдёт подходящие публикации и подставит заголовок, описание и ссылку. Стрелками по списку, Enter — выбрано. Пока идёт поиск — три анимированные строки-скелетона, а не пустота.</li> <li data-num="799"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Мгновенное переименование группы</strong> — optimistic UI: название обновляется в интерфейсе сразу, не дожидаясь ответа сервера. Если сервер откажет — откат к прежнему значению с понятным сообщением об ошибке.</li> <li data-num="800"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Подтверждение при закрытии «Читайте также»</strong> — если вы что-то поменяли в диалоге и нажали Escape, появится <code>confirm</code> с вопросом «Закрыть без сохранения?». Случайный Escape больше не уничтожает работу.</li> <li data-num="801"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Внутренние ссылки в «Читайте также»</strong> — ссылки на статьи Дискурса (начинающиеся с <code>/</code>) открываются в том же окне, а не в новой вкладке. Читатель остаётся на платформе.</li> <li data-num="802"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Автосохранение озвучивается скринридером</strong> — <code>aria-live</code> регион тихо сообщает «Черновик сохранён» или «Ошибка автосохранения». Без визуального шума, только для тех, кому это нужно.</li> <li data-num="803"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Гонка автосохранения при закрытии устранена</strong> — payload собирается до уничтожения редактора, а не после. Двойной запрос (beforeunload + $destroy) заблокирован флагом <code>
1_expoFinalSaveDone</code>. Ни одна правка не теряется.</li> <li data-num="804"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Единый парсер ошибок в сообщениях</strong> — <code>conversationCtrl</code> перешёл на централизованный <code>apiClient.getErrorMessage()</code>. Ошибки вида <code>{ data: { errors: [{ message }] } }</code> теперь корректно парсятся — раньше показывался фоллбэк.</li> <li data-num="805"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>File input аватара работает стабильно</strong> — таймаут привязки <code>$timeout(fn, 0)</code> вместо магических 50мс. Angular гарантирует, что DOM готов после digest.</li> <li data-num="806"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Один <code>$watchGroup</code> вместо двух <code>$watch</code></strong> — отслеживание <code>title</code> и <code>thumborId</code> для автосохранения: один digest-callback вместо двух. Мелочь, но в редакторе с десятками watcher&#39;ов — каждый на счету.</li> <li data-num="807"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>ARIA combobox в автокомплите «Читайте также»</strong> — полный паттерн: <code>role=&quot;combobox&quot;</code>, <code>aria-expanded</code>, <code>aria-activedescendant</code>, <code>role=&quot;listbox&quot;</code> с <code>role=&quot;option&quot;</code>. Скринридер озвучивает каждый шаг навигации по результатам — вслепую работать с автокомплитом так же удобно, как зрячему.</li> <li data-num="808">[CSS] <strong>Тёмная тема для блока «Читайте также»</strong> — в dark mode блок больше не светится белым пятном посреди тёмного редактора. Фон, заголовок, разделители, ссылки и кнопка редактирования — всё подхватывает CSS-переменные тёмной темы.</li> <li data-num="809">[CSS] <strong>Убраны <code>!important</code> из настроек группы</strong> — поле «дни истории» перебивает Bootstrap через специфичность селектора, а не грубой силой.</li> <li data-num="810"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>SSR не режет «Читайте также» пополам</strong> — Cloudflare Worker при обрезке body до 10 КБ теперь проверяет, не оборвался ли блок <code>.read-also</code> на середине. Если HTML неполный — блок убирается целиком, а не показывает битую разметку поисковику.</li> <li data-num="811"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Escape закрывает dropdown корректно</strong> — раньше нажатие Escape при открытом выпадающем списке «Ничего не найдено» закрывало весь диалог, игнорируя dropdown. Теперь Escape сначала закрывает любой видимый список, и только повторное нажатие — сам диалог.</li> <li data-num="812"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Поиск отменяется при закрытии диалога</strong> — если закрыть «Читайте также» во время поиска, in-flight запрос прерывается через AbortController. Никаких orphan-запросов и race conditions.</li> <li data-num="813"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Двойной слеш в ссылках убран</strong> — если <code>link</code> из API уже начинался с <code>/</code>, диалог добавлял второй. Теперь проверяет перед вставкой.</li> <li data-num="814"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>CORS больше не блокирует автозаголовки</strong> — вставили <code>https://discours.io/articles/...</code> в поле URL — раньше ловили <code>TypeError: Failed to fetch</code>. Теперь любой URL платформы (discours.io, dscrs.site) автоматически превращается в относительный путь — и fetch идёт на свой сервер, без CORS-стены.</li> <li data-num="815"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Вставил ссылку — получил заголовок</strong> — скопировали URL статьи из адресной строки и вставили в поле ссылки? Через секунду заголовок и описание подтянутся сами. А URL при вставке мгновенно нормализуется: <code>https://discours.io/articles/culture/kino</code> → <code>/articles/culture/kino</code>. Работает и при потере фокуса, и при нажатии «Вставить блок».</li> <li data-num="816"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Поиск стал в 10 раз легче</strong> — параметр <code>
1noBody=1</code> отсекает тело статей из ответа API. Для автокомплита достаточно заголовка, описания и ссылки — а не 50 КБ HTML на каждый результат.</li> <li data-num="817">[CSS] <strong>Тёмная тема для автокомплита</strong> — выпадающий список результатов поиска, skeleton-загрузка, кнопки перемещения и удаления, «+ Добавить материал» — всё подхватывает dark mode. Раньше белый dropdown на тёмном фоне слепил.</li> <li data-num="818"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Dropdown не перекрывает кнопки</strong> — ширина списка результатов теперь равна ширине поля заголовка, а не растянута до края карточки. Кнопки ↑↓× остаются видимыми и кликабельными.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.61.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9610-12-03-2026--csp-мониторинг-глубокий-focus-management-и-защита-от-гонок-в-сообщениях">CSP-мониторинг, глубокий focus management и защита от гонок в сообщениях</h2></summary> <div class="changelog-section__body"> <p>Платформа стала безопаснее, доступнее и устойчивее к ошибкам пользователя. Cloudflare Worker получил инструменты для мониторинга XSS-атак в реальном времени. Модалки по всему сайту перестали терять фокус клавиатуры. А интерфейс сообщений больше не позволяет запутаться при создании группы.</p> <ul> <li data-num="794"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>CSP-отчёты без правки кода</strong> — Cloudflare Worker теперь поддерживает <code>report-uri</code>, <code>report-to</code> и <code>Reporting-Endpoints</code> (CSP Level 2 + Reporting API v2). Адрес коллектора задаётся через env-переменные <code>CSP_REPORT_URI</code> и <code>CSP_REPORT_GROUP</code> — подключить Sentry, Report URI или свой эндпоинт можно за минуту, без деплоя нового кода. Фундамент для автоматического обнаружения инъекций и XSS на продакшене.</li> <li data-num="795"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Focus trap в четырёх модалках</strong> — модалка комментариев в редакторе, диалог «Читайте также», запрос на соавторство и создание групповой беседы — все четыре теперь удерживают Tab/Shift+Tab внутри себя. Фокус не убегает за пределы окна, при открытии встаёт на нужное поле, при закрытии — возвращается на место. Единый <code>createFocusTrap</code> из <code>utils/focusTrap.js</code> заменил ручные реализации — код стал компактнее и надёжнее.</li> <li data-num="796"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Поиск блокируется при создании группы</strong> — пока идёт создание групповой беседы или добавление участника, поле поиска авторов блокируется через <code>disabled</code>-биндинг. Невозможно случайно открыть чужой диалог, запустить параллельный запрос или запутаться в двух действиях сразу. Работает и на странице сообщений, и внутри беседы.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.60.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9600-12-03-2026--автосохранение-в-искусстве-умные-ошибки-и-ловушки-фокуса">Автосохранение в Искусстве, умные ошибки и ловушки фокуса</h2></summary> <div class="changelog-section__body"> <p>Работы в разделе «Искусство» больше не пропадают. Редактор теперь сам сохраняет черновик каждые 10 секунд — тихо, без кнопок, без вашего участия. А если интернет пропал, текст всё равно останется в локальном хранилище. Параллельно прокачали доступность модальных окон и научили платформу объяснять ошибки на человеческом языке.</p> <ul> <li data-num="785"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Автосохранение для Искусства</strong> — музыка, видео, фотографии, литература — все типы expo-материалов теперь автоматически сохраняются на сервер. 10 секунд тишины после правки — и черновик уже на сервере. Если связь оборвалась — до двух повторных попыток. Если материал удалён (404/410) — автосохранение отключается само. Тост «Черновик сохранён» появл
1яется не чаще раза в 30 секунд — не мельтешит.</li> <li data-num="786"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Заголовок и обложка тоже сохраняются</strong> — поменяли название или обложку — автосохранение подхватит и это. Раньше отслеживались только правки в теле текста, а мета-поля требовали ручного сохранения.</li> <li data-num="787"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Локальные бэкапы не копятся вечно</strong> — старые localStorage-резервные копии удаляются через 30 дней, а свежих хранится не больше 10 штук. Очистка запускается при каждом открытии редактора — браузер не захламляется, даже если вы работаете над десятками материалов.</li> <li data-num="788"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ошибки — по-русски и по делу</strong> — новый централизованный классификатор ошибок API: «Нет соединения с сервером», «Необходима авторизация», «Слишком много запросов — подождите немного» — вместо технического мусора. Каждая ошибка получает тип (network, auth, rate_limit и т.д.) и набор флагов для точной обработки в коде.</li> <li data-num="789"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Фокус не убегает из модалок</strong> — Tab и Shift+Tab теперь циклически ходят по кнопкам и полям внутри модальных окон, а не улетают за пределы диалога. При закрытии фокус возвращается туда, где был до открытия. Работает в модалках комментариев, оглавления, вставки видео и HTML-редактора.</li> <li data-num="790"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Tab в HTML-редакторе</strong> — focus trap больше не перехватывает Tab в CodeMirror, где он нужен для отступов. Если элемент уже обработал нажатие — ловушка не вмешивается.</li> <li data-num="791"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Автосохранение не конфликтует с ручным</strong> — если вы нажали «Сохранить» или «Опубликовать», автосохранение терпеливо ждёт. После ручного сохранения трекинг изменений обновляется — лишних запросов не будет.</li> <li data-num="792"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Пустой заголовок сохраняется корректно</strong> — раньше очистка заголовка не попадала в payload автосохранения. Теперь пустая строка — тоже значение.</li> <li data-num="793"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Финальное сохранение при уходе</strong> — при закрытии вкладки или навигации прочь из редактора несохранённые правки уходят на сервер в последний момент. Защита от двойного срабатывания (beforeunload + $destroy) не даёт отправить один и тот же запрос дважды.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.59.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9590-12-03-2026--групповые-чаты-доведены-до-ума-аватары-настройки-надёжность">Групповые чаты доведены до ума: аватары, настройки, надёжность</h2></summary> <div class="changelog-section__body"> <p>Групповые чаты получили всё, чего не хватало для полноценной работы. Владелец группы теперь управляет названием, аватаром и видимостью истории прямо из панели участников — без лишних страниц и переключений. А под капотом — серьёзный ремонт: загрузка аватара раньше молча ломалась в продакшене, теперь работает железобетонно.</p> <ul> <li data-num="778"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Свой аватар группы</strong> — загрузите любую картинку, и она заменит автоматический коллаж из фото участников. Аватар отображается везде: в шапке чата, в списке диалогов, в панели участников. Во время загрузки на превью крутится спиннер — видно, что процесс идёт.</li> <li data-num="779"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Переименование группы на лету</strong> — клик по карандашу, новое название, Enter — готово. Escape отменяет. Всё inline, без модалок и перезагрузок.</li> <li data-num="780"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Настройки видимости истории</strong> — владелец решает, что увидит новый участник: всю переписку, последние N дней или ничего. Остальные участники видят текущие настройки в read-only режиме.</li> <li data-num="781"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Загрузка аватара в продакшене</strong>
1 — <code>angular.element(this).scope()</code> возвращает <code>undefined</code> при отключённом debug info. Файловый input теперь привязан программно через <code>addEventListener</code> — работает в любом режиме. Панель на <code>ng-if</code> пересоздаёт DOM при каждом открытии — обработчик перепривязывается автоматически.</li> <li data-num="782"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка загрузки блокируется при аплоаде</strong> — HTML <code>&lt;label&gt;</code> игнорирует атрибут <code>disabled</code>. Заменено на CSS <code>pointer-events: none</code> — во время загрузки нельзя случайно открыть файловый диалог повторно.</li> <li data-num="783"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Утечки памяти закрыты</strong> — все event listeners (file input, keydown, editor input) корректно снимаются при уничтожении контроллера. Pending-таймауты отменяются.</li> <li data-num="784"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Единый хелпер ошибок</strong> — повторяющийся паттерн извлечения сообщения из ответа API (10+ мест) вынесен в <code>extractErrorMessage()</code>. Код контроллера стал на ~60 строк короче и проще для поддержки.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.58.0</span><span class="changelog-meta__date">12-03-2026</span></span><h2 id="v9580-12-03-2026--рассылки-нового-уровня-умная-доставка-точная-аналитика-защита-от-сбоев">Рассылки нового уровня: умная доставка, точная аналитика, защита от сбоев</h2></summary> <div class="changelog-section__body"> <p>Рассылочная система Дискурса повзрослела. Теперь она знает, кому письмо точно не дойдёт — и не тратит на это ни одной попытки. Считает не просто «сколько раз открыли», а «сколько людей открыли». Сама останавливается, если почтовый сервер лёг. И умеет повторить отправку тем, кому не повезло с первого раза — в один клик из админки.</p> <ul> <li data-num="764"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Клик-трекинг</strong> — каждая ссылка в письме теперь отслеживается. В карточке кампании видно, сколько раз читатели перешли по ссылкам. Mailto, tel и служебные ссылки (отписка, трекинг) не затрагиваются — всё чисто.</li> <li data-num="765"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Уникальные открытия и клики</strong> — раньше один читатель, открывший письмо пять раз, давал «5 открытий». Теперь в админке два числа: «Открытий (всего)» и «Открытий (уник.)». Наконец-то можно понять, какой процент аудитории действительно читает рассылку.</li> <li data-num="766"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Повторная отправка неудавшихся</strong> — если часть писем не дошла, адреса сохраняются автоматически. В админке появл
1яется кнопка «Повторить неудавшиеся» — нажали, и письма уходят только тем, кому не повезло. Без дубликатов для остальных.</li> <li data-num="767"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Suppression-лист</strong> — hard bounce, жалобы и ручные блокировки теперь в единой базе. Адрес из suppression-листа никогда не получит рассылку снова — ни из какой кампании. Управление — прямо в админке.</li> <li data-num="768"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Уведомление оператора</strong> — после каждой массовой отправки на почту приходит отчёт: сколько отправлено, сколько ошибок, сколько bounce. Не нужно сидеть и ждать в админке — результат сам найдёт вас.</li> <li data-num="769"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Умная доставка по доменам</strong> — письма к Gmail, Outlook, Яндексу и Mail.ru раскладываются равномерно по батчам с паузами. Это снижает риск попасть под rate-limit крупных провайдеров и повышает доставляемость.</li> <li data-num="770"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>MX-валидация</strong> — перед отправкой проверяются DNS-записи домена получателя. Если у домена нет почтового сервера — адрес не попадёт в рассылку. Меньше бесполезных попыток, быстрее отправка, чище репутация.</li> <li data-num="771"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Circuit breaker</strong> — если SMTP-сервер перестал отвечать и подряд идут 10+ ошибок, отправка автоматически останавливается. Оставшиеся адреса сохраняются для retry — ни одно письмо не потеряется.</li> <li data-num="772"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Soft bounce с повтором</strong> — если почтовый сервер говорит «попробуйте позже» (greylisting, rate-limit), письмо автоматически повторяется через 2 секунды. Раньше такие адреса просто терялись.</li> <li data-num="773"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Hard bounce → suppression</strong> — невалидные адреса (коды 550–553) теперь не только удаляются из подписок, но и добавляются в suppression-лист. Даже если кто-то подпишется снова с тем же ящиком — рассылка не уйдёт на мёртвый адрес.</li> <li data-num="774"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Адаптивный таймаут зависших отправок</strong> — раньше кампания считалась «зависшей» через 15 минут, даже если в ней 5000 получателей. Теперь таймаут пропорционален размеру аудитории — большие рассылки не прерываются преждевременно.</li> <li data-num="775"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Коллекция NewsletterSuppressions</strong> — email, причина (hard_bounce/complaint/manual), привязка к кампании. Unique-индекс, автонормализация email, дедупликация при старте.</li> <li data-num="776"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Коллекция NewsletterTrackingEvents</strong> — дедупликация открытий и кликов через campaignId + SHA
1-256 хеш email. TTL 90 дней, не накапливается бесконечно.</li> <li data-num="777"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Snapshot failed-получателей</strong> — неудавшиеся адреса сохраняются в NewsletterAudienceSnapshots с kind <code>production_failed</code>. TTL 30 дней. Используются для retry.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.57.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9570-11-03-2026--база-сервиса-рассылки-готова-персонализация-управление-подписками-трекинг">База сервиса рассылки готова: персонализация, управление подписками, трекинг</h2></summary> <div class="changelog-section__body"> <p>Дискурс наконец умеет рассылать письма по-настоящему. Пять именованных аудиторий, персональное обращение по имени, страница управления подписками вместо грубой кнопки «Отписаться», отслеживание открытий и автоматическое удаление невалидных адресов — всё это работает из админки в пару кликов и готово к первой отправке на 7000 подписчиков.</p> <ul> <li data-num="752"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Пять аудиторий</strong> — подписчики разделены на читателей, авторов, доноров, редакторов и партнёров. Каждая кампания отправляется именно тем, кому она адресована. В админке — кнопка «Статистика списков» с мгновенной разбивкой по аудиториям.</li> <li data-num="753"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Персонализация писем</strong> — шаблонные переменные <code>{{name}}</code>, <code>{{email}}</code>, <code>{{unsubscribe_url}}</code>, <code>{{web_version_url}}</code> подставляются для каждого получателя. Имя берётся из базы подписчиков — письмо начинается с «Привет, Алиса!», а не с безликого «Уважаемый подписчик».</li> <li data-num="754"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Страница управления подписками</strong> — вместо одной кнопки «Отписаться» читатель видит чекбоксы всех рассылок, на которые подписан. Можно убрать авторскую рассылку, но оставить дайджест для читателей — и наоборот. Подтверждение через POST защищает от случайных переходов и сканеров почтовых клиентов.</li> <li data-num="755"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Трекинг открытий</strong> — в каждое письмо встраивается невидимый пиксель. Счётчик открытий обновляется атомарно и виден в карточке кампании в админке. Данные анонимны — считаем только общее число, без привязки к конкретному получателю.</li> <li data-num="756"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>One-Click Unsubscribe</strong> — заголовок <code>List-Unsubscribe-Post</code> (RFC 8058) позволяет Gmail и другим клиентам показать кнопку «Отписаться» прямо в интерфейсе почты — без перехода на сайт.</li> <li data-num="757"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Hard bounce: автоочистка</strong> — если SMTP-сервер отвечает «mailbox not found» (коды 550–553), адрес автоматически удаляется из всех списков. Репутация домена не страдает от повторных попыток доставки на мёртвые ящики.</li> <li data-num="758"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Веб-версия без сырых переменных</strong> — в публичном архиве письма (<code>/newsletter/archive/...</code>) шаблонные переменные <code>{{name}}</code>, <code>{{email}}</code> заменяются, а не показываются как литеральный текст. Двойная защита: при формировании снимка и при отдаче страницы.</li> <li data-num="759"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Email без регистра</strong> — все операции с email (подписка, отписка, поиск, bounce) используют case-insensitive поиск через regex. <code>[email protected]</code> и <code>[email protected]</code> — один и тот же подписчик.</li> <li data-num="760"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Полноценная система рассылок</strong> — создание кампаний, тестовая и массовая отправка, планирование по расписанию, восстановление зависших отправок, дублирование кампаний. Состояния: draft → sending_test → tested → scheduled → sending → sent. Чанковые снапшоты аудитории (по 1000 адресов), параллельная отправка с настраиваемой конкурентностью.</li> <li data-num="761"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Именованные списки в EmailSubscriptions</strong> — поле <code>lists[]</code> с валидацией на уровне схемы. Индекс для быстрой фильтрации. API подписки автоматически добавляет в список «readers».</li> <li data-num="762"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Rate limiting подписки</strong> — 5 запросов в минуту на IP (или email). Проверка выполняется до обращения к базе — нельзя зондировать чужие email без лимита.</li> <li data-num="763"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>CSP и sandbox</strong> — страницы отписки, архива и превью защищены строгим <code>Content-Security-Policy</code>: <code>script-src &#39;none&#39;</code>, <code>sandbox</code>, <code>no-referrer</code>. Вредоносный HTML в теле письма не выполнит скрипт.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.56.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9560-11-03-2026--обновление-системы-подписок-мгновенный-отклик-точные-счетчики-и-другие-фиксы">Обновление системы подписок: мгновенный отклик, точные счетчики и другие фиксы</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="746"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Подписка в одном месте — обновляется везде.</strong> Нажали «Подписаться» на странице автора или тега — кнопка в модалке «Мои подписки» и на ленте уже показывает «Подписаны». Отписались в модалке — с вашей страницы подписок запись исчезает сразу, без перезагрузки. Один клик, одно состояние по всей платформе.</li> <li data-num="747"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Окно подписок открывается быстрее.</strong> Код модалки «Мои подписки» подгружается только когда вы её открываете (с ленты или профиля). Страницы автора и ленты стали легче — меньше ждёте при первом заходе.</li> <li data-num="748"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопка «Подписаться/Отписаться» отзывается мгновенно.</strong> В модалке подписок состояние меняется сразу после клика; если сервер ответил ошибкой — всё аккуратно возвращается назад. Никакого «повесилось и непонятно, подписан я или нет».</li> <li data-num="749"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кнопки подписки стали доступнее.</strong> Для скринридеров и клавиатурной навигации у кнопок «Подписаться/Отписаться» проставлены состояния «нажата» и «занята» — удобнее тем, кто не пользуется мышью.</li> <li data-num="750"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Комментарии обновляются без глюков.</strong> Добавили, отредактировали или удалили комментарий — список под материалом подхватывает изменения. Кэш сбрасывается там, где нужно, так что вы всегда видите актуальную ветку.</li> <li data-num="751"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ошибки от сервера — по-русски и по делу.</strong> При сбоях подписки, комментариев или сохранения материала показывается осмысленный текст из ответа API, а не общее «что-то пошло Ð
1½Ðµ так».</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.55.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9550-11-03-2026--групповые-чаты-цитаты-управление-участниками">Групповые чаты, цитаты, управление участниками</h2></summary> <div class="changelog-section__body"> <p>Сообщения на Дискурсе больше не только тет-а-тет. Теперь можно создать групповой чат с любым количеством участников, отвечать на конкретное сообщение цитатой, управлять участниками — и всё это в знакомом интерфейсе, без нового приложения.</p> <ul> <li data-num="736"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Групповые чаты</strong> — прямо из списка сообщений можно создать группу: дайте ей название, добавьте участников — и чат готов. Аватар группы автоматически собирается из фотографий участников в мозаику 2×2. В шапке видно название и число участников, по клику — полный список.</li> <li data-num="737"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Превратите диалог в группу</strong> — любой личный диалог можно расширить до группы одной кнопкой. История переписки сохраняется, добавляется новый участник, и вы продолжаете с того же места. Никаких «перешлите всё вручную».</li> <li data-num="738"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ответ с цитатой</strong> — как в Telegram: наведите на сообщение, нажмите ↩ — и ваш ответ отправится с цитатой оригинала. Получатель сразу видит контекст. Работает и в личных, и в групповых чатах.</li> <li data-num="739"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Управление группой</strong> — владелец может добавлять и удалять участников, назначать других владельцев, настраивать видимость истории для новичков (вся / последние N дней / ничего) и удалить группу целиком. Участник может покинуть чат в любой момент. Вход и выход фиксируются системным сообщением — «Алиса добавила Бориса».</li> <li data-num="740"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Клавиатурные шорткаты в чате</strong> — Escape закрывает панель ответа, список участников или диалог расширения. Панели не накладываются друг на друга. Новые сообщения плавно прокручивают ленту вниз — и при отправке, и при получении.</li> <li data-num="741"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Поиск авторов работает с callback&#39;ом</strong> — компонент поиска авторов теперь корректно вызывает переданный callback при выборе. Раньше в некоторых контекстах (добавление участника группы, расширение диалога) клик по автору открывал личный чат вместо нужного действия. Исправлено.</li> <li data-num="742"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Подписка: «Вы уже подписаны!»</strong> — при повторной подписке на рассылку вместо молчания или ошибки — дружелюбное «Этот email уже есть в нашей рассылке». Модалка сбрасывает состояние при закрытии: откроете снова — чистая форма. Ошибки сети и rate-limit отображаются понятным текстом. <strong>Подписки и чаты — что изменилось для вас:</strong></li> <li data-num="743"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>API групповых чатов</strong> — шесть новых endpoints: создание группы, управление участниками и владельцами, обновление метаданных, удаление. Мутация 1-на-1 в группу, системные сообщения о входе/выходе, фильтрация истории по дате вступления — всё серверно. Уведомления для групп: «Новое сообщение в группе &quot;Редакция&quot;».</li> <li data-num="744"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Цитаты в сообщениях</strong> — при ответе с цитатой сервер проверяет, что оригинал существует в том же диалоге, и возвращает превью (автор + первые 200 символов). Пакетная загрузка цитат в ленте — без N+1 запросов.</li> <li data-num="745"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Каскадное удаление</strong> — при удалении группы удаляются все связанные уведомления, а не только тип «messages». Никаких сирот в базе.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.54.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9540-11-03-2026--прощай-bower-всё-на-npm-модалка-вернулась-настройки-под-защитой">Прощай, Bower: всё на npm, модалка вернулась, настройки под защитой</h2></summary> <div class="changelog-section__body"> <p>Одна из старейших зависимостей платформы — пакетный менеджер Bower — полностью удалён. 25 AngularJS-библиотек переехали в npm, а вместе с ними ушли сотни файлов, отдельный шаг установки и целый пласт legacy-кода. Модалка «Пригласить соавторов» снова работает на странице черновиков, а настройки безопасности профиля стали надёжнее.</p> <ul> <li data-num="726"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Bower удалён навсегда</strong> — 25 пакетов (Angular, Bootstrap, CodeMirror, Videogular и др.) мигрированы в npm. Удалены <code>bower.json</code>, <code>.bowerrc</code>, <code>bower_components/</code> и скрипт <code>run-bower.js</code>. Одна команда <code>npm install</code> вместо двух — быстрее CI, проще онбординг, меньше точек отказа.</li> <li data-num="727"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Модалка «Пригласить соавторов» на /drafts</strong> — стили и JS-компонент загружались только в редакторе, а на странице черновиков модалка появл
1ялась без оформления. Стили перенесены в общий бандл, компонент — в lazy-чанк приватных страниц. Всё работает.</li> <li data-num="728"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Черновики не грузят лишнего</strong> — модалка приглашения выделена в отдельный webpack-чанк (41 KiB). Раньше <code>/drafts</code> тянул 356 KiB редакторного кода — теперь нет.</li> <li data-num="729"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Фон не прокручивается за модалкой</strong> — при открытии модалки приглашения прокрутка страницы блокируется. Никакого случайного скролла под оверлеем.</li> <li data-num="730"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Смена email требует подтверждения</strong> — теперь при изменении email в настройках профиля нужно ввести текущий пароль. Раньше email можно было сменить без проверки — теперь нет. Понятные сообщения об ошибках вместо загадочных 403.</li> <li data-num="731"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сортировка комментариев «Старые»</strong> — раньше режим «Старые» полагался на порядок из API. Теперь комментарии явно сортируются по дате — порядок всегда правильный, независимо от бэкенда.</li> <li data-num="732"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уведомления видны поверх логина</strong> — z-index тостов поднят выше модалки входа. Уведомление больше не прячется за оверлеем.</li> <li data-num="733"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Webpack: точный vendor-чанк</strong> — вместо «всё из bower_components» vendor-бандл теперь собирается из явного списка пакетов. Предсказуемое кэширование, никаких сюрпризов при обновлении зависимостей.</li> </ul> <p><strong>Backend:</strong></p> <ul> <li data-num="734"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Multi-stage Docker</strong> — build tools (g++, make, python) больше не попадают в production-образ. Контейнер стал легче и безопаснее.</li> <li data-num="735"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Флаг hasPassword в API</strong> — профиль пользователя теперь сообщает, установлен ли пароль. Фронтенд больше не пытается заглянуть в <code>services.password.bcrypt</code> — данные не утекают на клиент.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.53.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9530-11-03-2026--картинки-всегда-через-https-storybook-на-диете-деплой-одной-командой">Картинки всегда через HTTPS, Storybook на диете, деплой одной командой</h2></summary> <div class="changelog-section__body"> <p>Все изображения в статьях теперь автоматически загружаются по защищённому каналу — даже если автор вставил HTTP-ссылку. Визуальная библиотека компонентов стала чище и легче: 500+ инлайн-стилей вынесены в переиспользуемые классы. А бэкенд научился деплоиться одним вызовом из терминала — без CI-минут и без магии.</p> <ul> <li data-num="716"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Картинки всегда через HTTPS</strong> — Cloudflare Worker теперь добавляет <code>upgrade-insecure-requests</code> в заголовки безопасности. Все изображения, шрифты и другие ресурсы внутри статей автоматически загружаются по HTTPS, даже если автор указал HTTP-ссылку. Вы ничего не заметите — это и есть цель.</li> <li data-num="717"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Комментарии без гонок и утечек</strong> — при быстром переключении между статьями старый запрос комментариев отменяется на лету. Никаких фантомных обновлений в покин
1утом треде, никаких утечек памяти при навигации.</li> <li data-num="718"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Storybook: чистый код, быстрая сборка</strong> — 500+ инлайн-стилей во всех 23 историях заменены на 200+ переиспользуемых <code>.sb-*</code> CSS-классов. Сборка ускорилась втрое (30 → 10 сек), а стили теперь меняются в одном месте — вместо поиска по десяткам файлов.</li> <li data-num="719"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Живые Tiptap-истории</strong> — два новых каталога в Storybook: расширения редактора (упоминания, сноски, спойлеры, карточки ссылок с настоящими мини-редакторами) и модалки (комментарии, HTML-редактор, вставка видео). Дизайнер видит компоненты как на ладони, без запуска всей платформы.</li> <li data-num="720"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>CSS открытой публикации</strong> — доработаны стили обсуждений и ремарок. Вкладки не скачут, отступы предсказуемы, заголовки не переопределяются случайными правилами.</li> </ul> <p><strong>Backend:</strong></p> <ul> <li data-num="721"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Деплой одной командой</strong> — <code>bash deploy.sh</code> собирает Meteor, строит Docker-образ, пушит в Docker Hub и разворачивает на сервере через SSH. <code>--dry-run</code> покажет план, <code>--skip-build</code> задеплоит уже собранное, <code>--setup</code> настроит сервер с нуля. Без лимитов CI-минут, без сюрпризов.</li> <li data-num="722"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Docker: лимиты памяти и активный healthcheck</strong> — контейнер ограничен 2 ГБ, при зависании — автоматический перезапуск по <code>/api/v2/health</code>. CI-пайплайн больше не ждёт фиксированные 15 секунд — опрашивает готовность каждые 5 сек и стартует, как только сервер ответил.</li> <li data-num="723"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Диалоги стали доступнее</strong> — фокус на «Отмена» при открытии, закрытие по Escape, циклический Tab внутри модалки, ARIA-разметка. Для тех, кто работает с клавиатуры или скринридером — ощутимое улучшение.</li> <li data-num="724"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Защита загрузки изображений</strong> — новые тесты на SSRF: приватные IP, внутренние сети, невалидный base64, <code>javascript:</code>-протокол и <code>ftp://</code> — всё отклоняется. 84 новых строки тестов не дадут регрессиям проскочить.</li> <li data-num="725"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>500+ строк тестов</strong> — история версий (create/restore/delete/stats/auto), SSRF-защита, smoke-тесты фронтенд-маршрутов. Каждый сценарий проверяет и happy path, и ошибки — 401, 404, 400, 429.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.52.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9520-11-03-2026--окно-входа-летает-storybook-расцвёл-админка-не-теряет-правки">Окно входа летает, Storybook расцвёл, админка не теряет правки</h2></summary> <div class="changelog-section__body"> <p>Вход на платформу стал мгновенным: модалка логина загружается только когда нужна, а код летит к вам ещё до клика. Визуальная библиотека компонентов выросла вдвое, обзавелась живыми Tiptap-редакторами и Ð
1½Ð°ÑƒÑ‡Ð¸Ð»Ð°ÑÑŒ ловить визуальные регрессии автоматически. А в админке теперь невозможно случайно потерять набранный текст — она предупредит о несохранённых изменениях.</p> <ul> <li data-num="705"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Вход без ожидания</strong> — модалка логина вынесена из стартового бандла и загружается по требованию. Первая страница стала легче на ~6 KiB (brotli) — быстрее отрисовка, быстрее интерактивность. Задержки при клике нет: код предзагружается при наведении курсора, тач-контакте или фокусе клавиатурой.</li> <li data-num="706"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Умная предзагрузка всех разделов</strong> — при наведении на любую внутреннюю ссылку платформа начинает подгружать код будущей страницы. На медленных и мобильных сетях (2G, Save-Data) предзагрузка деликатно пропускается. На нормальном соединении — <code>requestIdleCallback</code>, без влияния на отзывчивость.</li> <li data-num="707"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Сервер недоступен? Платформа справится</strong> — новый circuit breaker в HTTP-слое: при 5+ подряд ошибках сервера показывается понятный тост «Сервер временно недоступен» вместо каскада пустых экранов. Платформа сама пингует сервер каждые 15 секунд и восстанавливает работу автоматически.</li> <li data-num="708"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Storybook: 23 истории + визуальные тесты</strong> — два новых каталога: Tiptap-расширения (25 расширений с живыми редакторами — упоминания, сноски, спойлеры, карточки ссылок) и модалки редактора (комментарии, HTML-редактор, вставка видео). Все 21 существующая история обновлена до единого стиля. Подключён <code>@storybook/test-runner</code> + <code>jest-image-snapshot</code> — теперь каждый PR автоматически сравнивает скриншоты компонентов с эталоном и ловит визуальные поломки до деплоя.</li> <li data-num="709"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>CSS открытой публикации</strong> — доработаны стили обсуждений и ремарок. Вкладки не скачут, отступы предсказуемы.</li> </ul> <p><strong>Backend:</strong></p> <ul> <li data-num="710"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Защита от потери правок в админке</strong> — при случайном уходе со страницы редактирования (клик, закрытие вкладки, перезагрузка) появляется предупреждение «Есть несохранённые изменения». 20 минут набранного текста больше не пропадут от одного неосторожного клика.</li> <li data-num="711"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Миниатюры в списке материалов</strong> — в админке теперь видно обложку статьи прямо в таблице, без открытия карточки. Быстрее ориентироваться, проще находить нужный материал.</li> <li data-num="712"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <strong>Диалоги стали доступнее</strong> — фокус на «Отмена», закрытие по Escape, циклический Tab, ARIA-разметка. Для тех, кто работает с клавиатуры или скринридером — заметное улучшение.</li> <li data-num="713"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Деплой одной командой</strong> — <code>deploy.sh</code> собирает Meteor локально, строит Docker-образ, деплоит через SSH. Есть <code>--dry-run</code>, <code>--skip-build</code>, <code>--setup</code> для настройки сервера. Без лимитов CI-минут.</li> <li data-num="714"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Docker: лимиты памяти и healthcheck</strong> — контейнеры ограничены 2 GiB, при зависании — автоматический перезапуск по <code>/api/v2/health</code>. CI-пайплайн больше не ждёт фиксированные 15 секунд — активно опрашивает готовность и стартует быстрее.</li> <li data-num="715"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Тесты: 430+ новых строк</strong> — история версий (create/restore/delete/stats), SSRF-защита загрузки изображений, smoke-тесты фронтенд-маршрутов. Регрессии ловятся до деплоя.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.51.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9510-11-03-2026--мгновенные-переходы-и-комментарии-без-гонок">Мгновенные переходы и комментарии без гонок</h2></summary> <div class="changelog-section__body"> <p>Платформа научилась предугадывать ваши действия: пока вы только наводите курсор на ссылку, код следующей страницы уже летит к вам. А комментарии под статьёй больше не путаются при быстрых действиях — старый запрос отменяется, побеждает всегда самый свежий.</p> <ul> <li data-num="702"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Страницы грузятся до клика</strong> — при наведении на любую ссылку платформа начинает предзагрузку кода будущей страницы. К моменту клика всё уже готово — переход ощущается мгновенным. Работает на десктопе (hover), мобильных (touch) и при навигации с клавиатуры (Tab).</li> <li data-num="703"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Комментарии без дублей и гонок</strong> — раньше быстрая отправка двух комментариев подряд порождала два параллельных запроса, и устаревший мог перезаписать свежий. Теперь предыдущий HTTP-запрос отменяется на лету, а UI всегда показывает актуальное состояние.</li> <li data-num="704"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Чистый уход со страницы</strong> — если вы переходите на другую статью, пока комментарии ещё грузятся, запрос отменяется. Никаких фантомных обновлений в уже покин
1утом компоненте, никаких утечек памяти.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.50.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9500-11-03-2026--прощай-restangular-платформа-сбросила-балласт">Прощай, Restangular: платформа сбросила балласт</h2></summary> <div class="changelog-section__body"> <p>Под капотом — крупнейшая чистка сетевого слоя за всё время: заброшенная библиотека Restangular полностью удалена, запросы к серверу летят напрямую, а бандл стал легче на десятки килобайт.</p> <ul> <li data-num="696"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Restangular удалён навсегда</strong> — unmaintained-библиотека (последнее обновление — 2016) вырезана из bower-зависимостей, конфигов и всего кода. Все 17 сервисов давно работают через лёгкий apiClient поверх штатного $http — теперь мёртвый код убран и формально. Минус ~25 KiB из бандла, минус одна дыра в supply chain.</li> <li data-num="697"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Lodash-шим вместо полной библиотеки</strong> — вместо 70 KiB полного lodash подключается минимальный шим на 3 KiB с 26 базовыми функциями. Экономия ~30 KiB gzip — страницы грузятся быстрее, особенно на мобильных.</li> <li data-num="698"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>buildConfig: единый конструктор запросов</strong> — дублирующийся код настройки timeout/params в каждом методе apiClient заменён общим хелпером. Меньше копипасты — меньше шансов на рассинхрон при доработках.</li> <li data-num="699"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>isCanceled: распознавание отменённых запросов</strong> — apiClient теперь умеет отличать отменённый запрос от реальной ошибки. Сервисы могут тихо проглотить abort вместо показа ложного toast «Ошибка сервера».</li> <li data-num="700"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>restangular.js → http-interceptors.js</strong> — файл с HTTP-интерцепторами (circuit breaker, auth-заголовки, мониторинг 401) переименован по смыслу. Новый разработчик больше не гадает «мы используем Restangular или нет?» — ответ однозначный.</li> <li data-num="701"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Документация в ногу с кодом</strong> — обновлены ARCHITECTURE.md, CLAUDE.md, README, ROADMAP и плановые документы (MOBILE_APP, REACT-UPGRADE). Диаграмма архитектуры теперь показывает реальный стек, а не призрак удалённой зависимости.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.49.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9490-11-03-2026--финальная-полировка-перед-большим-релизом">Финальная полировка перед большим релизом</h2></summary> <div class="changelog-section__body"> <p>Решены десятки багов, проведены масштабные оптимизации. Платформа стала устойчивее к сбоям сети, админка — безопаснее, а лаги, невидимые утечки памяти и гонки состояний ушли в прошлое.</p> <ul> <li data-num="681"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Stripe под защитой от спама</strong> — эндпоинт создания платёжных сессий теперь ограничен по IP (5 запросов в минуту). Злоумышленник больше не может забить очередь фейковыми сессиÑ
1Ð¼Ð¸ и помешать реальным донорам.</li> <li data-num="682"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Приглашения без гонок</strong> — принятие и отклонение инвайтов теперь атомарное: если вы нажали «Принять» в двух вкладках одновременно, второй запрос получит 409 вместо дублирования доступа.</li> <li data-num="683"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Удаление пользователей с полной очисткой</strong> — новый диалог в админке: выбираете, что делать с комментариями, публикациями и сообщениями. Данные удаляются каскадом, но если любой шаг провалится — аккаунт останется нетронутым. Целостность данных прежде всего.</li> <li data-num="684"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Минимум 3 администратора</strong> — нельзя случайно снять роль админа, если это оставит платформу без достаточного числа управляющих.</li> <li data-num="685"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Повторная загрузка при сбое сети</strong> — если кусок интерфейса не прогрузился (обрыв соединения, деплой новой версии), платформа сама повторит попытку дважды с нарастающей паузой. Реже увидите белый экран при переходе между разделами.</li> <li data-num="686"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Меньше дублей в памяти</strong> — общие модули (донаты, комментарии, подписки) собраны в отдельный чанк и загружаются один раз, даже если нужны нескольким страницам.</li> <li data-num="687"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Привязки компонентов: <code>=</code> → <code>&lt;</code></strong> — блок комментариев под статьёй перешёл на одностороннюю привязку данных. Меньше лишних перерасчётов — быстрее отклик.</li> <li data-num="688"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Слайдер «Похожие материалы» больше не течёт</strong> — при переходе на другую страницу слайдер корректно уничтожается. Раньше оставался в памяти — теперь чисто.</li> <li data-num="689"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Дубли в «Искусстве»</strong> — при пагинации в разделе Expo элементы больше не повторяются, даже если сервер вернул пересекающиеся результаты.</li> <li data-num="690"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Конфликт инвайтов — понятная ошибка</strong> — если статус приглашения уже изменился в другом окне, вы увидите «Статус приглашения уже изменился» вместо загадочного сбоя.</li> </ul> <p><strong>Backend:</strong></p> <ul> <li data-num="691"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Sentry на сервере</strong> — серверные ошибки теперь ловятся и группируются в одном дашборде вместе с фронтендом. Полная картина здоровья платформы в одном окне.</li> <li data-num="692"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Пагинация авторов и подписок</strong> — публикации <code>authorsForCard</code> и <code>subscriptions</code> теперь поддерживают <code>limit</code>/<code>skip</code>. Страницы с длинными списками авторов и подписок загружаются быстрее и не тянут всё разом.</li> <li data-num="693"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>CSP для рассылок</strong> — страницы превью, архива и отписки от рассылки получили строгие заголовки безопасности: <code>script-src &#39;none&#39;</code>, <code>sandbox</code>, <code>no-referrer</code>. Вредоносная вставка в письмо не пробьётся.</li> <li data-num="694"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Rollbar удалён</strong> — заменён на Sentry во всех точках: <code>$exceptionHandler</code>, логин/логаут, обработка ошибок. Одна зависимость меньше, один инструмент мониторинга вместо двух.</li> <li data-num="695"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Интеграционные тесты</strong> — 5 новых наборов: пользователи, коллаборация, загрузка внешних изображений, история версий, отмена рассылок. Ловим регрессии до деплоя.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.48.0</span><span class="changelog-meta__date">11-03-2026</span></span>
1<h2 id="v9480-11-03-2026--сторож-на-вахте-мониторинг-линтинг-порядок">Сторож на вахте: мониторинг, линтинг, порядок</h2></summary> <div class="changelog-section__body"> <p>Платформа получила глаза в продакшене, закрыла лазейку в сетевой защите и навела порядок в маршрутах — теперь сборка сама ловит рассинхрон, а старые данные не копятся вечно.</p> <ul> <li data-num="675"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Sentry: ошибки больше не уходят в пустоту</strong> — продакшн-мониторинг через <code>@sentry/browser</code> с автозагрузкой source maps в CI. Каждый краш превращается в читаемый стектрейс с номером версии — чиним до того, как пользователь напишет в поддержку.</li> <li data-num="676"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>IPv6: закрыт обход SSRF-защиты</strong> — адреса с ведущими нулями (<code>2001:0db8::1</code>) раньше проскакивали мимо фильтра приватных диапазонов. Добавлена нормализация сегментов и два ранее пропущенных диапазона (Teredo, Discard). Загрузка внешних картинок и OG-превью теперь безопасна для любого формата IPv6.</li> <li data-num="677"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>RSS-контент подкастов</strong> — хрупкий regex для <code>&lt;style&gt;</code> тегов пропускал вредоносное содержимое с символом <code>&lt;</code> внутри. Исправлен на надёжный паттерн; лишняя regex-чистка убрана — DOMPurify и так справляется.</li> <li data-num="678"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Единый реестр маршрутов</strong> — 47 путей платформы теперь проверяются автоматически: скрипт <code>validate:known-paths</code> сверяет канонический список с Cloudflare Worker при каждой сборке. Забыл добавить маршрут в Worker? Сборка упадёт с понятной ошибкой — не пользователь на проде.</li> <li data-num="679"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>TTL-индекс для снапшотов рассылки</strong> — старые аудиторные данные кампаний удаляются автоматически через 30 дней. MongoDB больше не раздувается от одноразовых снапшотов.</li> <li data-num="680"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Prettier 3</strong> — обновление с 2.6 до 3.8. Все файлы проходят линтинг. Форматирование стало быстрее и точнее.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.47.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9470-11-03-2026--приглашения-с-защитой-и-сроком-годности">Приглашения с защитой и сроком годности</h2></summary> <div class="changelog-section__body"> <p>Приглашения к совместному редактированию стали безопаснее: истёкшие больше не дают тихий доступ к чужому черновику, а вместо глухой ошибки — понятный экран с кнопкой «Получить доступ».</p> <ul> <li data-num="673"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Истёкшие приглашения больше не пропускают</strong> — раньше pending-инвайт с просроченным <code>expiresAt</code> всё ещё давал доступ к редактору через прямой API-вызов. Теперь сервер проверяет срок в четырёх точках доступа и в эндпоинте accept/decline — просроченное приглашение возвращает 410 Gone.</li> <li data-num="674"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Экран «Приглашение истекло»</strong> — при попытке принять истёкший инвайт из уведомлений вместо невнятной ошибки вы увидите понятное сообщение и кнопку «Получить доступ», которая сразу откроет форму запроса нового приглашения у автора.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.46.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9460-11-03-2026--живые-комментарии-и-лёгкий-редактор">Живые комментарии и лёгкий редактор</h2></summary> <div class="changelog-section__body"> <p>Редактор стал отзывчивее: стили больше не воюют друг с другом, длинные списки комментариев не тормозят, а чужие комментарии появл
1яются мгновенно — без перезагрузки страницы.</p> <ul> <li data-num="670"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>CSS без войны <code>!important</code></strong> — правило шрифтов для редактора переведено на <code>:where()</code> с нулевой специфичностью. Убраны 10+ точечных <code>!important</code>-хаков в стилях цитат, сносок, карточек ссылок, автокомплита и тултипов. Стили стали предсказуемыми — любой компонент внутри редактора задаёт свой размер без костылей.</li> <li data-num="671"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Пагинация в боковой панели</strong> — при 50+ комментариях или правках в редакторе DOM больше не раздувается: показываются первые 20 элементов, остальные подгружаются по кнопке «Показать ещё». Навигация по клику на комментарий в тексте автоматически раскрывает нужную страницу.</li> <li data-num="672"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Комментарии в реальном времени</strong> — теперь, когда соавтор добавляет, редактирует или принимает комментарий, вы видите это мгновенно. Данные летят через тот же WebSocket, что и совместное редактирование — никакого polling, никаких задержек.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.45.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9450-11-03-2026--легче-на-старте-умнее-на-краю">Легче на старте, умнее на краю</h2></summary> <div class="changelog-section__body"> <p>Платформа стала быстрее для читателей и удобнее для разработки: тяжёлые модули грузятся только когда нужны, а 4300-строчный монолит Cloudflare Worker разобран на 9 аккуратных модулей.</p> <ul> <li data-num="666"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ленивая загрузка модалок и сервисов</strong> — подписки, донаты и сервис комментариев больше не тормозят первый экран: они подгружаются по требованию. Начальный бандл похудел, страница отрисовывается быстрее.</li> <li data-num="667"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Окно входа не блокирует рендер</strong> — loginModal теперь создаётся в DOM только при открытии (ng-if вместо ng-show). Меньше работы при загрузке — быстрее интерактивность.</li> <li data-num="668"><span class="changelog-tag changelog-tag--DX" data-tag-alias="DX">DEV</span> <strong>Worker: из монолита — в модули</strong> — единый файл на 4300 строк разбит на 9 ES-модулей (config, api, payments, html-utils, json-ld, rewriters, proxy, handlers, index). Сборка через esbuild с tree-shaking: выходной файл ужался на 37%. Навигация по коду, отладка и ревью стали в разы проще.</li> <li data-num="669"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Slug-экстракторы в Worker</strong> — убрано ошибочное декодирование URL перед разбором пути. Символ <code>%2F</code> в slug больше не ломает маршрутизацию — статьи с необычными адресами открываются корректно.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.44.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9440-11-03-2026--прощай-restangular-фундамент-стал-легче-и-быстрее">Прощай, Restangular: фундамент стал легче и быстрее</h2></summary> <div class="changelog-section__body"> <p>Крупнейшая техническая реформа года: платформа избавилась от заброшенной библиотеки Restangular, перешла на нативные вызовы и сбросила лишний вес. 52 файла переписаны — ни одна кнопка не пострадала.</p> <ul> <li data-num="662"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Restangular удалён</strong> — заменён лёгким apiClient поверх штатного $http. Одна заброшенная библиотека меньше в бандле, запросы к серверу летят напрямую — без лишних обёрток и конвертаций.</li> <li data-num="663"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Lodash: нативные аналоги</strong>
1 — <code>shuffle</code>, <code>debounce</code>, <code>find</code>, <code>filter</code>, <code>map</code> и ещё десяток lodash-функций заменены на чистый JavaScript в 25+ файлах. Меньше кода — быстрее старт.</li> <li data-num="664"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Circuit breaker сохранён</strong> — при недоступности сервера сайт по-прежнему покажет понятный тост и переключится на кеш, а фоновый ping вернёт всё обратно при восстановлении.</li> <li data-num="665"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авторизация без швов</strong> — auth-заголовки теперь инжектируются единым $http-интерсептором во все запросы: редактор, комментарии, загрузка картинок — без ручной передачи токенов.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.43.0</span><span class="changelog-meta__date">11-03-2026</span></span><h2 id="v9430-11-03-2026--storybook-песочница-и-порядок">Storybook: песочница и порядок</h2></summary> <div class="changelog-section__body"> <p>Дизайн-система вышла на новый уровень: компоненты стали интерактивными, стили — консистентными, а разработка — быстрее.</p> <ul> <li data-num="658"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Песочница компонентов</strong> — рейтинг, подписка и уведомления теперь можно крутить прямо в Storybook: меняйте состояния, тексты и варианты через Controls. Отдельная категория «Песочница» — не мешается с обзорными stories.</li> <li data-num="659"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>CSS-утилиты для демо</strong> — <code>.sb-card</code>, <code>.sb-row--lg</code>, <code>.sb-label</code>, <code>.sb-hint</code> и ещё десяток классов вместо россыпи inline-стилей. Storybook стал чище, а новые stories пишутся в разы быстрее.</li> <li data-num="660"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Самодостаточные карточки</strong> — <code>.sb-card--shadow</code> и <code>.sb-card--wide</code> больше не забывают фон и padding, если использованы без базового класса. SCSS placeholder <code>%sb-card-base</code> убирает дублирование.</li> <li data-num="661"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мёртвый код</strong> — удалён неиспользуемый <code>.sb-inline</code>, лишний <code>gap: 0</code>, бесполезные inline-стили. Каждый класс теперь работает.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.42.0</span><span class="changelog-meta__date">10-03-2026</span></span><h2 id="v9420-10-03-2026--быстрее-удобнее-отзывчивее">Быстрее, удобнее, отзывчивее</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="655"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Сжатие бандла: -80% трафика</strong> — все скрипты и стили теперь отдаются в gzip и brotli. Страницы грузятся заметно быстрее, особенно на мобильных и слабых соединениях — главный JS-файл похудел с 626 КБ до 128 КБ.</li> <li data-num="656"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Счётчик непрочитанных в заголовке вкладки</strong> — если у вас есть новые уведомления, вы увидите <code>(3) Дискурс — ...</code> прямо на вкладке браузера. Не нужно возвращаться на сайт, чтобы узнать, ответил Ð
1»Ð¸ кто-то на ваш комментарий.</li> <li data-num="657"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Сортировка комментариев</strong> — под статьёй появились кнопки «Старые / Новые / Лучшие». Быстро найдите свежую дискуссию или самый оценённый отклик — без бесконечной прокрутки.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.41.0</span><span class="changelog-meta__date">10-03-2026</span></span><h2 id="v9410-10-03-2026--комплексный-аудит-безопасности">Комплексный аудит безопасности</h2></summary> <div class="changelog-section__body"> <p>Полная проверка платформы: код, API, заголовки, зависимости. Закрыты все найденные уязвимости без потери функциональности.</p> <ul> <li data-num="641"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>XSS в просмотре версий</strong> — HTML версий и diff теперь проходит через DOMPurify-санитизацию</li> <li data-num="642"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Open redirect</strong> — защита всех точек записи/чтения URL возврата через localStorage; блокировка внешних и protocol-relative URL</li> <li data-num="643"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Валидация censoredLink</strong> — redirect при цензуре валидируется как same-origin; централизованный хелпер <code>safeRedirect()</code> с fallback</li> <li data-num="644"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Валидация Stripe</strong> — redirect после оплаты проверяет подлинность домена платежной платформы</li> <li data-num="645"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Валидация redirect в Worker</strong> — защита canonical-редиректов от protocol-relative URL в обоих обработчиках (LCP + API fallback); строгая проверка hostname</li> <li data-num="646"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Логирование</strong> — убрано логирование тел ответов внешних сервисов (Mailgun), которые могли содержать ключи</li> <li data-num="647"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Email-валидация</strong> — RFC 5321-совместимая проверка формата email во всех API-эндпоинтах</li> <li data-num="648"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Input limits</strong> — ограничения длины поисковых запросов и полей заявок; защита от DoS через раздувание regex/документов</li> <li data-num="649"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Пароли</strong> — упрощена и расширена проверка спецсимволов (любой не-буквенный символ)</li> <li data-num="650"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Санитизация контента</strong> — все raw <code>ng-bind-html</code> (баннеры, цитаты, описания, punchline) переведены на <code>trustedHtml</code> (DOMPurify)</li> <li data-num="651"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Cookies</strong>
1 — <code>uploadSessionParser</code> получил <code>httpOnly</code>, <code>secure</code>, <code>sameSite: &#39;strict&#39;</code> — паритет с основной сессией</li> <li data-num="652"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>charset=utf-8</strong> — все JSON-ответы Worker (donation, Stripe) явно указывают кодировку</li> <li data-num="653"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Shared validation</strong> — <code>escapeRegExp</code> и <code>isValidEmail</code> вынесены в единый модуль <code>/imports/validation</code>; убраны дубли в 4 файлах</li> <li data-num="654"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>npm audit fix</strong> — применены безопасные обновления зависимостей</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.40.0</span><span class="changelog-meta__date">09-03-2026</span></span><h2 id="v9400-09-03-2026--распознание-ссылок-тихий-режим-сравнение-версий-storybook-и-система-рассылок">Распознание ссылок, тихий режим, сравнение версий, storybook и система рассылок</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="630"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Распознание ссылок в карточки — в редакторе вставка URL на пустую строку даёт OG-превью (title, описание, картинка, домен). Новые расширения link-card.js и link-paste-handler.js, на бэкенде — OG API.</li> <li data-num="631"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Тихий режим (Zen) — Ctrl+Alt+Q и кнопка в панели: скрываются курсоры, комментарии на полях и правки; в этом режиме нельзя добавлять комментарии и открывать сайдбар.</li> <li data-num="632"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Diff версий — в панели версий режим «Сравнение»: diff выбранной версии с текущим текстом (<code>htmldiff-js</code>, <code>&lt;ins&gt;/&lt;del&gt;</code>).</li> <li data-num="633"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Производительность — один проход по документу extractAll() вместо трёх; виртуальный скролл в margin view при &gt;20 карточках (в DOM только элементы в зоне видимости + буфер).</li> <li data-num="634"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Стили редактора — один большой SCSS разбит на модули: core, toolbar, content, collab, links, quiet-mode; старый файл сохранён как .bak.</li> <li data-num="635"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Lazy load главной — для маршрута / подгружается чанк home.js (MagazineIndexCtrl, hero/quotes/podcast/theme баннеры и т.д.), эти модули убраны из основного бандла.</li> <li data-num="636"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Storybook — добавлены конфиг и 21 сторис (Colors, Typography, Buttons, NotificationItem, Banners и др.).</li> <li data-num="637"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Worker — правки: tag API при не-JSON возвращает status: 0; исправлена подстановка пустого alt у картинок в SSR.</li> </ul> <p><strong>
1Backend:</strong></p> <ul> <li data-num="638"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Рассылки (Newsletter) — коллекция NewsletterCampaigns, сервис newsletterService.js (черновики, тестовая отправка, планирование, отправка по расписанию, отписка по подписанному токену, preview/archive). Стартапы: unsubscribe (HTTP /newsletter/unsubscribe), scheduler (CronLock раз в минуту), preview, archive. В админке — ресурс с действиями Test Send, Schedule, Preview и т.д.</li> <li data-num="639"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> OG API — GET /api/v2/og?url=... для редакторских link cards: таймаут, защита от приватных хостов, DNS-проверка, кэш, rate limit.</li> <li data-num="640"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Админка — setup.js разгружен: ресурсы вынесены в resources/*.js, хелперы в admin-helpers.js, аудит в admin-audit.js, загрузка картинок в admin-image-upload.js; добавлены ресурсы comments, content-item, events, feedback, newsletter, system, tools, user.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.39.0</span><span class="changelog-meta__date">08-03-2026</span></span><h2 id="v9390-08-03-2026--ubuntu-18-→-20-→-22-→-24-надёжнее-и-быстрее">Ubuntu 18 → 20 → 22 → 24: надёжнее и быстрее</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="628"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Дискурс переехал на современный и устойчивый серверный фундамент. Модернизация системы до Ubuntu 24.04.4 LTS с новым ядром 6.8.0-101-generic, обновлены все 435 пакетов. Масштабное обновление невидимого, но крайне важного слоя платформы.</li> <li data-num="629"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кэшированные ответы при падениях через Cloudflare Caching. Даже в случае атак и падений сайт увереннее переживает сбои, показывая материалы из кэша вместо ошибок.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.38.0</span><span class="changelog-meta__date">08-03-2026</span></span><h2 id="v9380-08-03-2026--оптимизация-загрузки-картинок-авторизации-и-загрузки-главной">Оптимизация загрузки картинок, авторизации и загрузки главной</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="624"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Вход без нервов</strong> — логин, соцсети и возврат в сессию работают стабильнее, с понятными сообщениями и без зависших окон</li> <li data-num="625"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Картинки не теряются</strong> — вставка, загрузка и галереи в редакторе стали надёжнее даже при слабой сети</li> <li data-num="626"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Редактор бережнее к автору</strong> — более умные подписи, аккуратные упоминания и меньше лишних рисков для приватных черновиков</li> <li data-num="627"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Ускорее загрузки блоков</strong> — цитатный баннер и интерфейс грузят браузер мягче и ощущаются плавнее</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.37.0</span><span class="changelog-meta__date">07-03-2026</span></span><h2 id="v9370-07-03-2026--meteor-34-реформа-технического-фундамента-бэкенда">
1Meteor 3.4: реформа технического фундамента бэкенда</h2></summary> <div class="changelog-section__body"> <p>Дискурс совершил большой технологический рывок: платформа переехала с бэкенда поколения 2015 на современный стек 2026.</p> <ul> <li data-num="620"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Современный бэкенд 2026</strong> — большой шаг вперёд в скорости, стабильности и будущем развитии платформы</li> <li data-num="621"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Вход без срывов</strong> — авторизация, регистрация и восстановление сессии работают увереннее после переезда</li> <li data-num="622"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Обновления видны сразу</strong> — свежая версия сайта быстрее доходит до читателей без застрявшего кеша</li> <li data-num="623"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Легче и быстрее страницы</strong> — спецпроекты, цитаты и баннеры открываются экономнее и отзывчивее</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.36.0</span><span class="changelog-meta__date">06-03-2026</span></span><h2 id="v9360-06-03-2026--дашборд-редакции-тихие-уведомления-и-точный-редактор">Дашборд редакции, тихие уведомления и точный редактор</h2></summary> <div class="changelog-section__body"> <p>Админка получила полноценный дашборд: метрики платформы, свежие комментарии, ремарки и публикации — всё на одном экране. Уведомления больше не засыпают тостами при возврате на сайт — теперь они собираются в одну компактную сводку. Клики по комментариям и правкам в редакторе стали точнее.</p> <ul> <li data-num="616"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Дашборд редакции</strong> — публикации, авторы, комментарии и ремарки за 30 дней в одном месте с живым обновлением</li> <li data-num="617"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Умные уведомления</strong> — при возврате на сайт вместо каскада тостов — одна сводка «У вас N новых уведомлений»</li> <li data-num="618"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Точные клики в редакторе</strong> — при вложенных комментариях и правках клик теперь попадает в нужный, а не в охватывающий</li> <li data-num="619"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>«Обновлён» вместо «Опубликован»</strong> — при переиздании материала сообщение соответствует действию</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.35.0</span><span class="changelog-meta__date">06-03-2026</span></span><h2 id="v9350-06-03-2026--дискурс-теперь-в-tor">Дискурс теперь в Tor</h2></summary> <div class="changelog-section__body"> <p>Платформа получила собственное зеркало в сети Tor. Читайте и публикуйте без ограничений — даже если основной сайт заблокирован в вашем регионе. Полная копия Дискурса, работающая через анонимную сеть.</p> <ul> <li data-num="613"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Tor-зеркало платформы</strong> — Дискурс доступен по адресу <code>dscrsfyco6k7uhx2y5s57a54r26gdfs3vl72kf3fneavcoy5b3villqd.onion</code> через Tor Browser</li> <li data-num="614"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Полная функциональность</strong> — все материалы, навигация и API работают через зеркало без ограничений</li> <li data-num="615"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Приватность читателей</strong> — весь трафик проходит через анонимную сеть, API-запросы не выходят за пределы зеркала</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.34.0</span><span class="changelog-meta__date">06-03-2026</span></span><h2 id="v9340-06-03-2026--«читайте-также»-одной-кнопкой-шапка-без-глитчей">«Читайте также» одной кнопкой, шапка без глитчей</h2></summary> <div class="changelog-section__body"> <p>Блок «Читайте также» вставляется через удобный диалог — вставьте ссылку на любую статью. Шапка больше не ломается на экранах с нестандартным масштабированием (125%, 150%). Безопасность платформы усилена на нескольких уровнях.</p> <ul> <li data-num="608"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Читайте также»</strong> — диалог вставки с подсказками, принимает любую ссылку на материал</li> <li data-num="609"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>
1Шапка на HiDPI-экранах</strong> — устранены наложения меню при дробном масштабировании</li> <li data-num="610"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>CSRF-защита админки</strong> — все действия в панели администратора теперь проверяются на подлинность источника запроса; подделка запросов со сторонних сайтов невозможна</li> <li data-num="611"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>XSS-защита диалогов редактора</strong> — пользовательский ввод в диалогах вставки экранируется, предотвращая внедрение вредоносного кода</li> <li data-num="612"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Четырёхслойная защита админки</strong> — изоляция cookie (SameSite strict + HttpOnly + Secure), проверка источника запросов, сессионная авторизация и rate-limiting на входе</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.33.0</span><span class="changelog-meta__date">05-03-2026</span></span><h2 id="v9330-05-03-2026--живые-обсуждения-мгновенный-сброс-превью-и-надёжная-почта">Живые обсуждения, мгновенный сброс превью и надёжная почта</h2></summary> <div class="changelog-section__body"> <p>Обсуждения в редакторе стали по-настоящему живыми: все участники получают уведомления о новых ответах, а при принятии или отклонении комментария — автор узнаёт первым. Превью статей в Telegram, Facebook и ВКонтакте обновляются в один клик из админки. Email-уведомления стали надёжнее — письма больше не теряются молча.</p> <ul> <li data-num="603"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Уведомления участникам обсуждения</strong> — новый ответ в треде → все участники получают уведомление с прямой ссылкой в редактор</li> <li data-num="604"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>«Комментарий принят / отклонён»</strong> — автор комментария узнает, когда его приняли или отклонили</li> <li data-num="605"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Сброс превью в соцсетях</strong> — из админки можно обновить карточку статьи в Telegram, Facebook и VK одной кнопкой</li> <li data-num="606"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Авторизация через VK</strong> — вход через ВКонтакте снова работает после переезда API на новый домен</li> <li data-num="607"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Письма доходят надёжнее</strong> — email-уведомления не помечаются как отправленные, пока реально не уйдут</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.32.0</span><span class="changelog-meta__date">05-03-2026</span></span><h2 id="v9320-05-03-2026--уведомления-ведут-прямо-к-ответу-меню-на-всех-экранах">Уведомления ведут прямо к ответу, меню на всех экранах</h2></summary> <div class="changelog-section__body"> <p>Клик по уведомлению о комментарии или правке теперь открывает редактор и прокручивает точно к нужному ответу — с подсветкой. Сайт переживает сбои сервера. Набор текста больше не подвисает даже на длинных статьях.</p> <ul> <li data-num="598"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Переход к ответу из уведомления</strong> — клик по уведомлению раскрывает нужный ответ в боковой панели или на полях — с подсветкой</li> <li data-num="599"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Защита от сбоев сервера</strong> — при недоступности бэкенда сайт показывает уведомление и восстанавливается автоматически</li> <li data-num="600"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Типографика без задержек</strong> — кавычки, тире и спецсимволы обрабатываются в фоновом потоке</li> <li data-num="601"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Шапка на планшетах</strong> — меню корректно работает на всех размерах экрана</li> <li data-num="602"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Понятное истечение сессии</strong>
1 — «Сессия истекла» вместо «До свидания!»</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.31.0</span><span class="changelog-meta__date">04-03-2026</span></span><h2 id="v9310-04-03-2026--сервер-стал-надёжнее-и-безопаснее">Сервер стал надёжнее и безопаснее</h2></summary> <div class="changelog-section__body"> <p>Большая волна улучшений серверной части после обновления стека. Авторизация через соцсети теперь работает на всех зеркалах и превью-версиях платформы. Ошибки API больше не возвращают непонятную HTML-страницу — клиент всегда получает понятный ответ. Комментарии и ремарки защищены от потери данных при одновременном редактировании. Email-дайджест стал компактнее. Добавлена защита от спама на ключевых точках входа.</p> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.30.0</span><span class="changelog-meta__date">03-03-2026</span></span><h2 id="v9300-03-03-2026--платформа-обновлена-со-стека-2016-на-2024-быстрее-надежнее-современнее">Платформа обновлена со стека 2016 на 2024: быстрее, надежнее, современнее</h2></summary> <div class="changelog-section__body"> <p>Масштабная техническая революция под капотом. Серверный фреймворк обновлён с версии 2016 года до актуальной версии 2024-го — это как поменять двигатель автомобиля, пока машина едет. Старая панель администрирования (Orion, снята с поддержки в 2017-м) полностью заменена современной React-платформой AdminJS. Убраны тысячи строк устаревшего кода, заменены уязвимые зависимости, подключён новейший драйвер MongoDB. Платформа стала заметно отзывчивее, надёжнее и защищённее — без единого видимого изменения для пользователей.</p> <p><strong>Фундаментальное обновление стека:</strong></p> <ul> <li data-num="591"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Meteor 1.8 → 2.16</strong> — серверный фреймворк обновлён с версии 2018 года до актуальной (поддержка до 2024+). Это даёт доступ ко всем современным возможностям Node.js, улучшенной работе с async/await, свежим патчам безопасности и более низкому потреблению памяти</li> <li data-num="592"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>MongoDB-драйвер 3.x → 4.x</strong> — обновлён официальный драйвер базы данных. Новая версия быстрее работает с большими выборками, поддерживает транзакции и исправляет ряд давних ошибок с кодировкой и таймаутами соединений</li> </ul> <p><strong>Панель редакции — полностью новая:</strong></p> <ul> <li data-num="593"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>
1AdminJS вместо Orion</strong> — старая панель администрирования на устаревшей библиотеке Blaze (снята с поддержки в 2017 году) заменена на современный AdminJS на React. Интерфейс стал быстрее, читабельнее и поддерживает актуальные браузеры</li> <li data-num="594"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>~5 000 строк Legacy-кода удалено</strong> — весь клиентский код старой Orion-панели вычищен из кодовой базы. Меньше кода = меньше точек отказа и быстрее холодный старт сервера</li> </ul> <p><strong>Безопасность и зависимости:</strong></p> <ul> <li data-num="595"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уязвимые пакеты заменены</strong> — удалены устаревшие и заброшенные Meteor-пакеты: Orion (2017), saucecode:rollbar, nimble:restivus, aldeed:tabular и ряд других. Все замены имеют активную поддержку</li> <li data-num="596"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>bcrypt 3.x → 5.x</strong> — библиотека хеширования паролей обновлена до актуальной версии с исправлениями безопасности</li> <li data-num="597"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Собственный почтовый модуль</strong> — пакет <code>lookback:emails</code> (заброшен, не совместим с Meteor 2.x) заменён собственной реализацией с тем же API. Email-уведомления и дайджесты работают стабильно без сторонних зависимостей</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.23.0</span><span class="changelog-meta__date">03-03-2026</span></span><h2 id="v9230-03-03-2026--правильные-превью-в-соцсетях-авторы-в-поиске-старые-ссылки-работают">Правильные превью в соцсетях, авторы в поиске, старые ссылки работают</h2></summary> <div class="changelog-section__body"> <p>Статьи теперь корректно выглядят при репосте в Telegram, Facebook и ВКонтакте — заголовки со спецсимволами больше не ломаются. Страницы авторов заметно улучшили позиции и вид в поиске: у каждого автора свой уникальный заголовок и карточка в Google/Яндекс. Старые ссылки на материалы теперь быстрее перенаправляют на правильные страницы. Бэкенд ускорен: убраны скрытые лишние запросы к базе данных при каждом обращении к платформе.</p> <p><strong>Соцсети и SEO:</strong></p> <ul> <li data-num="579"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Превью без артефактов</strong> — заголовки с кавычками, амперсандами и спецсимволами (<code>«</code>, <code>&amp;</code>, <code>—</code>) теперь отображаются корректно в карточках Telegram, Facebook и других соцсетей. Раньше они превращались в <code>&amp;</code>, <code>&quot;</code> и прочий технический мусор</li> <li data-num="580"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Canonical без трекинга</strong> — параметры <code>?utm_source=</code>, <code>?fbclid=</code>, <code>?ref=</code> и прочие трекинг-метки больше не попадают в канонический адрес страницы и тег <code>og:url</code>. Поисковики перестанут видеть страницу и её расшаренные варианты как разные URL</li> <li data-num="581"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Черновики не утекают в превью</strong> — материалы в статусе «черновик» или «по ссылке» теперь надёжно исключены из кеша социальных ботов. Раньше превью черновика могло зависнуть в кеше на несколько часов</li> <li data-num="582"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Обновления видны сразу</strong> — OG-теги после деплоя платформы становятся актуальными мгновенно. Прежде боты могли ещё несколько часов показывать устаревшее превью</li> <li data-num="583"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Авторы в поиске</strong> — у каждого профиля автора теперь свой уникальный заголовок страницы и данные для поисковых систем (JSON-LD Person schema). Яндекс и Google смогут различать авторов и правильно показывать их профили в результатах поиска</li> <li data-num="584"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Старые ссылки работают</strong> — 301-редиректы для устаревших форматов URL (русские названия категорий <code>/articles/kultura/</code>, старые разделы экспо <code>/expo/painting/</code>). Ссылки из старых публикаций и соцсетей больше не ведут в никуда</li> <li data-num="585"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Технические URL не в индексе</strong> — страницы с параметрами <code>?modal=</code>, <code>?lng=</code>, <code>?fbclid=</code> и другими служебными метками исключены из индексации через <code>noindex</code> и <code>robots.txt</code>. Устраняет дублирование контента, которое снижало позиции в поиске</li> </ul> <p><strong>Быстрее и стабильнее:</strong></p> <ul> <li data-num="586"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Статичные страницы из edge-кеша</strong>
1 — <code>/about</code>, <code>/donate</code>, <code>/help</code> и другие страницы с фиксированным контентом теперь отдаются с серверов Cloudflare, а не с origin-хоста. Все UTM-варианты URL шарят одну запись кеша — нагрузка на origin снижена</li> <li data-num="587"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Меньше запросов к базе данных</strong> — при каждом API-запросе от авторизованного пользователя устранён лишний запрос к базе при проверке прав доступа (администратор/автор). Ускорение затрагивает все действия с материалами, комментариями, подписками и профилями</li> <li data-num="588"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Email-уведомления не ломаются молча</strong> — дополнительная проверка MAIL_URL при старте сервера: если настройки некорректны, в логах появится понятная ошибка вместо тихого сбоя</li> </ul> <p><strong>Редактор:</strong></p> <ul> <li data-num="589"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сноски в старых материалах</strong> — статьи с сносками в устаревшем формате автоматически конвертированы. Сноски корректно отображаются и работают в актуальном редакторе</li> <li data-num="590"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Точный скролл к курсору коллеги</strong> — клик по аватарке соавтора в шапке редактора теперь стабильно прокручивает страницу к его текущей позиции</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.22.2</span><span class="changelog-meta__date">02-03-2026</span></span><h2 id="v9222-02-03-2026--повтор-сохранения-загрузка-через-зеркало-быстрее-везде">Повтор сохранения, загрузка через зеркало, быстрее везде</h2></summary> <div class="changelog-section__body"> <p>Ошиблись соединением в момент сохранения? Теперь одна кнопка «Повторить» в уведомлении — и материал снова отправляется, без перезагрузки страницы. Картинки и аудио загружаются через зеркало: стабильнее при сбоях хранилища. Удалили тяжёлую библиотеку дат (~50 KB меньше) — страницы грузятся чуть быстрее, особенно с медленным интернетом. Шаблоны комментариев, правок и уведомлений стали потреблять меньше памяти браузера.</p> <p><strong>Редактор:</strong></p> <ul> <li data-num="575"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопка «Повторить» при ошибке сохранения</strong> — если материал не удалось сохранить (нет сети, ошибка сервера), в уведомлении появляется кнопка мгновенного повтора. Работает для черновиков и публикаций</li> <li data-num="576"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Загрузка картинок и аудио через зеркало</strong> — медиафайлы загружаются надёжнее: при проблемах с основным хранилищем автоматически используется резервный адрес</li> </ul> <p><strong>Быстрее и легче:</strong></p> <ul> <li data-num="577"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>−50 KB в бандле</strong> — библиотека moment.js полностью заменена на компактный dayjs. Меньше данных при каждом открытии страницы</li> <li data-num="578"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Комментарии, правки и уведомления рендерятся быстрее</strong> — оптимизированы шаблоны: панель редактора и лента уведомлений теперь требуют меньше ресурсов при обновлении и прокрутке</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.22.1</span><span class="changelog-meta__date">02-03-2026</span></span><h2 id="v9221-02-03-2026--мгновенная-повторная-загрузка-скелетоны-и-мобильная-клавиатура">Мгновенная повторная загрузка, скелетоны и мобильная клавиатура</h2></summary> <div class="changelog-section__body"> <p>Сайт запоминает шрифты, стили и изображения — повторные визиты загружаются практически мгновенно. В редакторе вместо пустого пространства теперь плавный скелетон, пока комментарии подгружаются. На мобильных устройствах поля ввода наконец-то показывают правильную клавиатуру: почтовую для email, цифровую для сумм.</p> <p><strong>Мгновенные повторные визиты:</strong></p> <ul> <li data-num="562"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Офлайн-кеширование ресурсов</strong> — шрифты, стили, скрипты и изображения сохраняются в кеше браузера. При повторном визите страница загружается за доли секунды, даже при нестабильном соединении
1</li> <li data-num="563"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кеширование иллюстраций</strong> — загруженные обложки и фотографии из редакционных материалов больше не скачиваются повторно</li> </ul> <p><strong>Редактор — приятнее и отзывчивее:</strong></p> <ul> <li data-num="564"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Скелетон при загрузке комментариев</strong> — вместо пустой панели отображается анимированная заглушка, пока комментарии грузятся из API</li> <li data-num="565"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Горячие клавиши в подсказках</strong> — при наведении на кнопки тулбара видны сочетания клавиш: Ctrl+B, Ctrl+I, Ctrl+K и другие</li> <li data-num="566"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тултипы скрыты на мобильных</strong> — карточки комментариев на полях больше не перекрывают текст на сенсорных экранах</li> </ul> <p><strong>Мобильная клавиатура — та, что нужна:</strong></p> <ul> <li data-num="567"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Email-клавиатура</strong> — все поля для ввода почты (логин, регистрация, подписка, обратная связь) показывают клавиатуру с <code>@</code> и <code>.com</code> на мобильных устройствах</li> <li data-num="568"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Цифровая клавиатура</strong> — поля сумм пожертвований показывают цифровую клавиатуру</li> </ul> <p><strong>Доступность и навигация:</strong></p> <ul> <li data-num="569"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Формы поиска</strong> — навигация по сайту и экранные читалки корректно распознают поисковые поля</li> <li data-num="570"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Пагинация</strong> — стрелки и текущая страница озвучиваются экранными читалками</li> <li data-num="571"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Призыв к диалогу</strong> — залогиненные читатели видят приглашение оставить первый комментарий</li> </ul> <p><strong>Стабильность и чистота:</strong></p> <ul> <li data-num="572"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Замена Rollbar на облегчённый модуль</strong> — удалён неиспользуемый обработчик ошибок, экономия ~2 KB в бандле</li> <li data-num="573"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Уведомления об ошибках</strong> — в production пользователь видит деликатный toast вместо молчаливого сбоя</li> <li data-num="574"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Устранение мерцания при загрузке</strong> — главный CSS больше не загружается асинхронно, что исключает вспышку нестилизованного контента</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.22.0</span><span class="changelog-meta__date">01-03-2026</span></span><h2 id="v9220-01-03-2026--комментарии-на-полях-форматирование-в-обсуждениях-упоминания-с-именами-и-многое-другое">Комментарии на полях, форматирование в обсуждениях, @упоминания с именами и многое другое</h2></summary> <div class="changelog-section__body"> <p>Большое обновление совместной работы в редакторе. Комментарии и правки теперь отображаются на полях — как заметки на рукописи. В обсуждениях появилось форматирование текста: жирный, курсив, Ctrl+B/I. @Упоминания стали умнее — показывают имя автора вместо технического slug, а автокомплит с аватарками работает везде: в комментариях, ответах, ремарках и обсуждениях правок.</p> <p><strong>Комментарии на полях — как заметки на рукописи:</strong></p> <ul> <li data-num="530"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Режим «Комментарии на полях»</strong> — на широких экранах (≥1280px) комментарии и правки отображаются в колонке справа от текста, точно напротив отмеченного фрагмента. Каждая карточка привязана к своей позиции и плавно следует за прокруткой</li> <li data-num="531"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Раскрытие по клику</strong> — карточка раскрывается с полным текстом, ответами и полем для ответа; остальные автоматически сворачиваются</li> <li data-num="532"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Плавная синхронизация</strong> — позиции карточек пересчитываются при прокрутке (60 fps) и изменении размера окна без рывков и мерцания</li> <li data-num="533"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопка в панели инструментов</strong> — быстрое включение и отключение режима полей. Выбор сохраняется между сессиÑ
1Ð¼Ð¸</li> </ul> <p><strong>Форматирование в комментариях и ответах:</strong></p> <ul> <li data-num="534"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Жирный и курсив</strong> — в комментариях, ответах и ремарках теперь можно использовать форматирование: <code>**жирный</code>** и <code>*курсив*</code> отображаются как полноценные стили</li> <li data-num="535"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ctrl+B / Ctrl+I</strong> — привычные клавиатурные сочетания работают во всех полях ввода: в комментариях, ответах на комментарии и правки, в ремарках и уведомлениях</li> <li data-num="536"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Визуальный редактор</strong> — при нажатии Ctrl+B или Ctrl+I выделенный текст оборачивается в <code>**</code> или <code>*</code> прямо в поле ввода — видно, что форматирование применилось</li> </ul> <p><strong>@Упоминания — имена вместо slug:</strong></p> <ul> <li data-num="537"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Имена в упоминаниях</strong> — при выборе автора из автокомплита вставляется <code>@Имя Фамилия</code> вместо технического <code>@slug</code>. Читать и понимать, кто упомянут, стало намного проще</li> <li data-num="538"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Автокомплит везде</strong> — подсказки с аватарками и именами при наборе <code>@</code> работают во всех полях: в комментариях редактора, ответах на правки, ремарках и модалках</li> <li data-num="539"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кликабельные упоминания</strong> — <code>@Имя</code> автоматически превращается в ссылку на профиль автора. Работает в комментариях, ответах и ремарках. Старый формат <code>@slug</code> тоже поддерживается</li> </ul> <p><strong>Редактирование комментариев и ответов:</strong></p> <ul> <li data-num="540"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Редактирование своих комментариев</strong> — автор может отредактировать текст своего комментария прямо в боковой панели, без необходимости удалять и писать заново</li> <li data-num="541"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Редактирование ответов</strong> — свои ответы в ветках обсуждений тоже можно исправить. Кнопка-карандаш появляется при наведении</li> <li data-num="542"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Удаление ответов</strong> — автор может удалить свой ответ; администратор и автор материала — любой ответ</li> </ul> <p><strong>Обсуждения правок — полноценные треды:</strong></p> <ul> <li data-num="543"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ответы на правки</strong> — каждая предложенная правка теперь имеет свою ветку обсуждения. Автор и рецензент могут обсудить конкретное изменение, не смешивая контекст с другими правками</li> <li data-num="544"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Форматирование и @упоминания в обсуждениях</strong> — все возможности форматирования и упоминаний доступны и в ответах на правки</li> </ul> <p><strong>Публичный доступ к редактору — комментирование без регистрации:</strong></p> <ul> <li data-num="545"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Анонимное комментирование</strong> — если для материала настроен публичный доступ по ссылке, незарегистрированные читатели могут оставлять комментарии и ремарки, не создавая аккаунт</li> <li data-num="546"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Баннер «Войдите для рецензирования»</strong> — анонимный пользователь видит предложение авторизоваться для доступа к режиму правок</li> <li data-num="547"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Иконка для анонимных авторов</strong> — вместо пустого места отображается визуальный индикатор анонимного участника</li> <li data-num="548"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Автосохранение не срабатывает у читателей</strong> — пользователи в режиме «только чтение» больше не создают ненужных запросов на сервер</li> </ul> <p><strong>Боковая панель — умнее и стабильнее:</strong></p> <ul> <li data-num="549"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Сворачиваемые комментарии</strong> — в боковой панели комментарии по умолчанию свёрнуты: видны автор, цитата и счётчик ответов. Клик раскрывает полный текст и ветку</li> <li data-num="550"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Карточки не схлопываются</strong> — раскрытые комментарии и правки сохраняют состояние при обновлении данных. Раньше карточка могла свернуться в момент чтения</li> <li data-num="551"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Счётчик ответов</strong> — под каждым свёрнутым комментарием видно количество ответов</li> <li data-num="552"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Многострочные ответы</strong> — поле ответа автоматически растягивается. Отправка по Ctrl+Enter</li> </ul> <p><strong>SVG-иконки в шапке:</strong></p> <ul> <li data-num="553"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Поиск, карандаш, сообщения, колокольчик</strong> — все иконки в шапке заменены на нативные SVG. Чёткие на Retina-экранах, мгновенно отображаются без загрузки шрифта</li> </ul> <p><strong>Уведомления — больше контекста:</strong></p> <ul> <li data-num="554"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Длиннее превью</strong> — в уведомлениях отображается больше текста комментария, чтобы сразу понять суть без перехода в редактор</li> <li data-num="555"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Умные ссылки</strong> — уведомления о правках и комментариях в редакторе ведут на нужную страницу, а не на страницу материала</li> </ul> <p><strong>Админка — кликабельные ссылки в таблице материалов:</strong></p> <ul> <li data-num="556"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Заголовки, авторы и теги кликабельны</strong> — в таблице материалов заголовок ведёт на статью, имя автора — на профиль, теги — на страницу темы</li> <li data-num="557"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Slug для всех тегов</strong> — автоматическая миграция генерирует адреса для тегов, у которых они отсутствовали</li> <li data-num="558"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Адаптивная ширина страниц</strong> — формы и списки получили оптимальную ширину вместо растягивания на весь экран</li> </ul> <p><strong>Стабильность и производительность:</strong></p> <ul> <li data-num="559"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Корректная проверка доступа</strong> — серверная валидация прав на редактирование правок и обсуждений работает единообразно с комментариями</li> <li data-num="560"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Защита от утечек памяти</strong> — кэши комментариев и просмотренных элементов очищаются при навигации между материалами</li> <li data-num="561"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Защита от XSS</strong> — удалён устаревший код, обходивший HTML-санитизацию. Ð
1’се пользовательские данные проходят экранирование</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.21.0</span><span class="changelog-meta__date">27-02-2026</span></span><h2 id="v9210-27-02-2026--умные-кнопки-редактора-чистые-сообщения-и-мгновенные-исправления">Умные кнопки редактора, чистые сообщения и мгновенные исправления</h2></summary> <div class="changelog-section__body"> <p>Кнопки публикации теперь говорят на человеческом языке: если материал уже опубликован, автор видит «Сохранить» и «Обновить публикацию» вместо абстрактного «Опубликовать». Проверка грамматики наконец-то мгновенно убирает подчёркивание после принятия исправления. Мобильная версия редактора стала заметно аккуратнее — контент, шапка и панель инструментов выровнены до пикселя.</p> <p><strong>Редактор — кнопки, которые не путают:</strong></p> <ul> <li data-num="519"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>«Сохранить» вместо «Опубликовать»</strong> — для уже опубликованных материалов главная кнопка в шапке редактора теперь показывает «Сохранить», а в выпадающем меню — «Обновить публикацию». Раньше автор видел «Обновить» на обоих элементах и не мог быстро отличить сохранение черновика от обновления публикации</li> <li data-num="520"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Диалог подтверждения адаптирован</strong> — при обновлении опубликованного материала диалог чётко спрашивает «Обновить публикацию?» с пояснением «Изменения станут доступны читателям». Дл
1я новых материалов — «Опубликовать?» как прежде</li> <li data-num="521"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Иконка на кнопке «Пригласить»</strong> — аккуратная SVG-иконка пользователя с плюсом делает кнопку визуально понятнее</li> </ul> <p><strong>Проверка грамматики — подчёркивание исчезает мгновенно:</strong></p> <ul> <li data-num="522"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Исправленные ошибки больше не подчёркиваются</strong> — после применения замены из LanguageTool волнистое подчёркивание убирается сразу. Раньше декорация «выживала» после замены текста и оставалась видимой до следующей полной перепроверки документа</li> </ul> <p><strong>Сообщения — чистый список диалогов:</strong></p> <ul> <li data-num="523"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Пустые диалоги скрыты</strong> — в списке сообщений больше не отображаются диалоги, в которых ещё никто не написал. Только реальные переписки</li> </ul> <p><strong>Мобильная версия редактора — пиксель к пикселю:</strong></p> <ul> <li data-num="524"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Контент выровнен с шапкой</strong> — на мобильных и планшетах боковые отступы контента совпадают с отступами шапки редактора. Больше никакого визуального «сдвига» между шапкой и текстом</li> <li data-num="525"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Кнопка «Пригласить» убрана из шапки</strong> — на мобильных это действие доступно в выпадающем меню «•••», шапка стала компактнее</li> <li data-num="526"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Панель инструментов на весь экран</strong> — на планшетах (≤768px) выпадающее меню теперь фиксированное и растянуто на ширину экрана с плавными скруглёнными краями. Не обрезается и не прячется за хедером</li> <li data-num="527"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Облачки сообщений шире</strong> — на мобильных максимальная ширина облачка увеличена с 80% до 92% — длинные сообщения удобнее читать</li> </ul> <p><strong>Загрузка редактора — без лишних задержек:</strong></p> <ul> <li data-num="528"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мгновенный экран авторизации</strong> — если пользователь не залогинен или не имеет доступа к материалу, экран с предложением войти появляется сразу, а не после 8-секундного таймера загрузки</li> </ul> <p><strong>История версий — понятные метки:</strong></p> <ul> <li data-num="529"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Человеческие названия версий</strong> — «Ключевой этап» → «Крупное изменение», «Публикация» → «Сохранение при публикации», «Старая версия» → «Сохранение». Имя «Система» в авторе версии больше не отображается — если версия создана автоматически, указ
1ывается только дата</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.20.0</span><span class="changelog-meta__date">26-02-2026</span></span><h2 id="v9200-26-02-2026--мгновенная-загрузка-редактора-правильные-ссылки-и-проверка-грамматики">Мгновенная загрузка редактора, правильные ссылки и проверка грамматики</h2></summary> <div class="changelog-section__body"> <p>Редактор больше не мигает белым экраном при открытии — вместо этого пользователь видит плавный скелетон, имитирующий текст, пока контент загружается. Ссылки в уведомлениях и письмах теперь ведут точно на нужный материал с учётом рубрики. Проверка грамматики LanguageTool наконец-то работает без сбоев в продакшене.</p> <p><strong>Загрузка редактора — никакого белого экрана:</strong></p> <ul> <li data-num="512"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Скелетон вместо пустоты</strong> — при открытии редактора вместо белого прямоугольника отображаются анимированные строки-заглушки, имитирующие текст. Работает для всех типов материалов: статьи, литература, фото, музыка, альбомы, видео</li> <li data-num="513"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Плавное появление заголовка</strong> — шапка редактора мягко проявляется после загрузки данных вместо резкого «прыжка» из ниоткуда</li> <li data-num="514"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Устранён белый экран между скелетоном и редактором</strong> — система дожидается полной загрузки стилей перед снятием скелетона. Раньше на медленном соединении мог проскакивать белый экран на 0.5–2 секунды</li> <li data-num="515"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Скелетон уважает <code>prefers-reduced-motion</code></strong> — анимация мерцания отключается для пользователей, предпочитающих минимум движения</li> <li data-num="516"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Тёмная тема</strong> — скелетон корректно отображается и в тёмном режиме</li> </ul> <p><strong>Уведомления и письма — правильные ссылки:</strong></p> <ul> <li data-num="517"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Ссылки ведут куда надо</strong> — приглашения, заявки на соавторство и ответы на правки теперь содержат полный путь к материалу с учётом рубрики. Раньше ссылка могла вести на несуществующую страницу, если slug совпадал в разных рубриках</li> </ul> <p><strong>LanguageTool — снят последний барьер:</strong></p> <ul> <li data-num="518"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Проверка грамматики работает в продакшене</strong> — добавлено разрешение CSP для запросов к <code>api.languagetool.org</code>. Раньше браузер молча блокировал запросы, и проверка просто не запускалась</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.19.0</span><span class="changelog-meta__date">25-02-2026</span></span><h2 id="v9190-25-02-2026--соавторство-оптимизированные-шрифты-и-hardened-security">Соавторство, оптимизированные шрифты и hardened security</h2></summary> <div class="changelog-section__body"> <p>Читатели могут запросить доступ к редактированию прямо со страницы материала. Все иконки действий в заголовках получили туÐ
1»Ñ‚ипы, клавиатурную навигацию и единый визуальный язык. Font Awesome ужался с 113 КБ до 9.5 КБ — при этом добавились иконки embed-провайдеров, которые раньше показывались пустыми квадратами. Под капотом — закрытие XSS-вектора через числовые HTML-сущности, устранение невидимых ссылок в режиме превью и переход на AngularJS best practices.</p> <p><strong>Соавторство — запрос доступа в один клик:</strong></p> <ul> <li data-num="489"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопка «Запросить доступ к редактированию»</strong> — на страницах материалов и экспозиции. Иконка рукопожатия, тултип с пояснением, отображается только авторизованным пользователям без редакторского доступа</li> <li data-num="490"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Тултипы на иконках действий</strong> — «Редактировать», «Запросить доступ», «Открыть в админке» — единый стиль с плавной анимацией, стрелкой-указателем и z-index без конфликтов</li> <li data-num="491"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Единый миксин <code>title-icon-styles</code></strong> — DRY: один источник правды для иконок в заголовках статей, экспозиции, профилей авторов, событий и тегов</li> <li data-num="492"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Текст баннера</strong> — «Хотите сотрудничать» → «Больше сотрудничать» — точнее передаёт суть</li> </ul> <p><strong>Font Awesome — в 12 раз легче и полнее:</strong></p> <ul> <li data-num="493"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Субсетирование шрифтов</strong> — полный FA (113 КБ) заменён точечным субсетом (9.5 КБ woff2). 125 глифов: 112 solid + 13 brands — ровно те, что используются на сайте</li> <li data-num="494"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>9 embed-иконок больше не пустые квадраты</strong> — добавлены Vimeo, SoundCloud, Spotify, Telegram, RuTube (play-circle), Datawrapper (chart-bar), Typeform (poll-h), itch.io (gamepad), Imgur (images)</li> <li data-num="495"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Legacy-алиасы</strong> — добавлены <code>fa-clock-o</code> и <code>fa-refresh</code> (→ <code>fa-sync-alt</code>) для обратной совместимости со старым контентом</li> <li data-num="496"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> <strong>Автоматическая сборка</strong> — <code>scripts/copy-assets.js</code> больше не перезаписывает субсетированные шрифты полными из bower</li> </ul> <p><strong>Безопасность — ещё один слой защиты:</strong></p> <ul> <li data-num="497"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Числовые HTML-сущности для <code>&lt;</code>, <code>&gt;</code>, <code>&amp;</code> больше не декодируются</strong> — <code>decodeSafeEntities()</code> в <code>stripTagsAndTruncate</code> пропускает <code>&lt;</code>/<code>&gt;</code>/<code>&amp;</code> и их hex-аналоги. Закрывает вектор реконструкции HTML-тегов через <code>&lt;script&gt;</code> в контексте <code>ng-bind-html</code></li> <li data-num="498"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Валидация contentItem ID</strong> — regex-проверка на допустимые символы (латиница, кириллица, цифры, дефисы) + защита от path traversal (<code>..</code>) в <code>remarksService</code></li> <li data-num="499"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Санитизация href в превью</strong>
1 — <code>decodeSafeEntities()</code> вызывается до href-экранирования, чтобы <code>&quot;</code> не вырвался за пределы атрибута</li> </ul> <p><strong>AngularJS — правильные паттерны:</strong></p> <ul> <li data-num="500"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Невидимая ссылка «Редактировать» в режиме превью</strong> — <code>ng-if</code> перенесён с <code>&lt;i&gt;</code> на <code>&lt;a&gt;</code>, вся ссылка целиком скрывается вместо рендеринга пустого кликабельного элемента</li> <li data-num="501"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**$timeout</code> вместо <code>setTimeout</code> в retry-логике ремарок** — повторные попытки корректно отменяются при уничтожении скоупа, третий параметр <code>false</code> оптимизирует digest</li> <li data-num="502"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Синхронизация <code>remarkWindowOpen</code></strong> — <code>socialTextDirective</code> на <code>post-body</code> (вне скоупа <code>SocialSelectionCtrl</code>) теперь использует <code>$rootScope.remarkWindowOpen</code> — обработчики touch/mouseup корректно отвязываются на мобильных</li> </ul> <p><strong>Доступность (A11Y):</strong></p> <ul> <li data-num="503"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**:focus-visible</code> на иконках действий** — клавиатурная навигация показывает тултипы при фокусе, не только при hover</li> <li data-num="504"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**prefers-reduced-motion</code>** — анимации тултипов и иконок отключаются для пользователей, предпочитающих минимум движения</li> <li data-num="505"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**aria-hidden=&quot;true&quot;</code> на декоративных иконках и тултипах** — скринридеры читают <code>aria-label</code> ссылки, а не «fa-pencil-alt Редактировать»</li> <li data-num="506"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Консистентные <code>aria-label</code></strong> — «Редактировать страницу автора» → «Редактировать профиль» — совпадает с <code>title</code> и тултипом</li> </ul> <p><strong>Производительность и стабильность:</strong></p> <ul> <li data-num="507"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Отправка ремарок</strong> — на мобильных больше не должно быть проблем с необходимостью несколько раз нажимать на кнопку</li> <li data-num="508"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Мобильные сообщения</strong> — адаптивная верстка, чтобы поле для сообщения оставалось в пределах экрана</li> <li data-num="509"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>DOMParser — один на компонент</strong> — <code>notificationItem</code> создаёт <code>DOMParser</code> один раз вместо нового экземпляра на каждое уведомление</li> <li data-num="510"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>z-index оверлея ремарок</strong>
1 — <code>.send-remark-overlay</code> получил <code>position: fixed</code> и <code>z-index: 9999998</code> (ниже <code>.social-selection</code> на 1) — корректное наложение слоёв</li> <li data-num="511"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> <strong>SCSS-переменные вместо хардкода</strong> — <code>$color-black</code>, <code>$color-tag</code>, <code>$font-1</code> в миксине иконок</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.18.0</span><span class="changelog-meta__date">24-02-2026</span></span><h2 id="v9180-24-02-2026--баннеры-соучастия-в-лентах-и-languagetool-на-стероидах">Баннеры соучастия в лентах и LanguageTool на стероидах</h2></summary> <div class="changelog-section__body"> <p>Лента получила два новых баннера, превращающих пассивного читателя в активного участника. LanguageTool стал надёжнее стали: параллельные проверки больше не убивают друг друга, навигация по ошибкам безопасна даже при закрытии редактора. Под капотом — 28 точечных фиксов: от критических багов маршрутизации до полноценной keyboard accessibility.</p> <p><strong>Лента — баннеры соучастия:</strong></p> <ul> <li data-num="468"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Баннер «Есть идеи?»</strong> — в сайдбаре ленты: четыре быстрых действия (предложить идею, обсудить, предложить тему, начать сотрудничество) — каждое открывает форму обратной связи с нужной темой. Элегантный дизайн с SVG-иконками, тёмная тема, мобильная адаптация</li> <li data-num="469"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Баннер «База знаний»</strong> — отображается на вкладке «Редакция»: пять ссылок на ключевые материалы платформы (гайд, принципы, правила, кодекс, библиотека инструментов). Быстрый онбординг для новых авторов</li> </ul> <p><strong>LanguageTool — критические фиксы:</strong></p> <ul> <li data-num="470"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Параллельные чанки больше не убивают друг друга</strong> — длинные тексты (&gt;18 КБ) разбиваются на чанки и проверяются параллельно через <code>Promise.all</code>. Раньше каждый чанк создавал свой <code>AbortController</code> и отменял предыдущие — выживал только последний. Теперь все чанки делят один контроллер</li> <li data-num="471"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Защита от «мёртвого» редактора</strong> — <code>isDestroyed</code> guard в <code>navigateToDecoration()</code> предотвращает обращение к уже уничтоженному редактору из отложенного <code>setTimeout</code></li> <li data-num="472"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Умный кэш-инвалидатор</strong> — дорогостоящий <code>DecorationSet.find()</code> в ProseMirror <code>apply()</code> теперь вызывается только при Yjs-синхронизация
1х и крупных транзакциях (&gt;3 шагов), а не на каждый символ</li> </ul> <p><strong>Лента — маршрутизация и стабильность:</strong></p> <ul> <li data-num="473"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Переключение вкладок больше не пересоздаёт контроллер</strong> — обёртка <code>updateUrl()</code> теперь корректно пробрасывает <code>preventRouteReload</code>, и route-hijack работает как задумано. Без этого каждая смена вкладки убивала и пересоздавала <code>feedCtrl</code> с нуля</li> <li data-num="474"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Устранены двойные API-запросы на вкладке «Редакция»</strong> — нормализация <code>statusChanged</code> предотвращает ложное срабатывание при сравнении <code>null</code> с <code>&#39;current&#39;</code></li> <li data-num="475"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>One-time bindings в комментариях</strong> — <code>::</code> на <code>article.comments</code>, <code>article.newComments</code> и <code>article.link#comments</code> в карточках ленты убирают лишние watcher&#39;ы</li> </ul> <p><strong>Доступность (A11Y):</strong></p> <ul> <li data-num="476"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**:focus-visible</code> на ~15 интерактивных элементах** — клавиатурная навигация с фирменным синим контуром (<code>#1b2fd8</code> / <code>#6b8aff</code> в тёмной теме) на кнопках панели инструментов, тогглах, пунктах меню, вкладках ленты и баннерных ссылках</li> <li data-num="477"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**prefers-reduced-motion</code>** — анимации пульсирующих точек авторизации и вспышки LanguageTool отключаются для пользователей, предпочитающих минимум движения</li> <li data-num="478"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**role=&quot;menuitem&quot;</code> в Tools-панели** — все пункты меню редактора получили семантическую роль для скринридеров</li> <li data-num="479"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>**aria-hidden=&quot;true&quot;</code> на декоративных иконках** — <code>fa-user-plus</code> во всех шаблонах скрыты от ассистивных технологий</li> <li data-num="480"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <strong>Семантические <code>&lt;button&gt;</code> вместо <code>&lt;a href=&quot;&quot;&gt;</code></strong> — баннер «Есть идея?» использует настоящие кнопки для действий, не навигации</li> </ul> <p><strong>Тёмная тема — полное покрытие:</strong></p> <ul> <li data-num="481"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Поиск по базе сносок</strong> — <code>footnote-search</code> получил полноценный dark mode: инпут, результаты, текст элементов, статус загрузки, ошибки — всё читаемо на тёмном фоне</li> <li data-num="482"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Бейджи и индикаторы ленты</strong>
1 — <code>.feed-sidebar__badge</code>, <code>.feed-tabs__badge</code>, <code>.feed-tabs__dot</code> корректно отображаются в тёмной теме</li> <li data-num="483"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Статус соединения в Tools-панели</strong> — компактные индикаторы в футере панели переведены с хардкоженных Ant Design цветов на единые переменные <code>$success-color</code> / <code>$warning-color</code> / <code>$danger-color</code></li> </ul> <p><strong>Код и стабильность:</strong></p> <ul> <li data-num="484"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>**encodeURIComponent</code> на <code>documentName</code>** — в <code>acceptRequest</code> / <code>declineRequest</code> уведомлений. Двоеточие в <code>content-item:&lt;slug&gt;</code> больше не ломает URL</li> <li data-num="485"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>try/catch на <code>enableLanguageTool()</code></strong> — инициализация LanguageTool в <code>$onChanges</code> не роняет редактор, если расширение ещё не готово</li> <li data-num="486"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>DRY в <code>sendFeedbackMessage</code></strong> — дублирующаяся логика открытия модалки из hash-watcher и broadcast-handler вынесена в общую <code>_openWithTopic()</code></li> <li data-num="487"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Error logging в уведомлениях</strong> — catch-блоки <code>acceptRequest</code> / <code>declineRequest</code> логируют ошибки вместо молчаливого проглатывания</li> <li data-num="488"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Генерация <code>.woff</code> шрифтов</strong> — <code>subset-fontawesome.py</code> теперь создаёт и <code>.woff</code>, и <code>.woff2</code> для полного покрытия браузеров</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.17.0</span><span class="changelog-meta__date">23-02-2026</span></span><h2 id="v9170-23-02-2026--бейджи-непрочитанного-в-ленте-литература-без-обёрток-и-хирургическая-стабильность">Бейджи непрочитанного в ленте, литература без обёрток и хирургическая стабильность</h2></summary> <div class="changelog-section__body"> <p>Лента стала живой: бейджи показывают, сколько нового появилось с последнего визита. Литературные тексты публикуются напрямую, без медиа-прослойки. Под капотом — серия хирургических фиксов: утечки таймеров, гонки запросов и кросс-аккаунтные артефакты полностью устранены.</p> <p><strong>Лента — непрочитанное и редакционные фильтры:</strong></p> <ul> <li data-num="454"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Бейджи непрочитанного</strong> — на каждой вкладке ленты отображается количество новых материалов с момента последнего посещения. Точка-индикатор для вкладок, которые ещё ни разу не открывались</li> <li data-num="455"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Редакционные подвкладки inline</strong> — Заявки, Журнал, Чулан отображаются рядом с фильтрами на десктопе, отдельной строкой чипов на мобильных. Сортировка автоматически скрывается для Редакции (всегда «новое»)</li> <li data-num="456"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Предвычисленные отрывки</strong>
1 — <code>_excerpt</code> рассчитывается один раз при загрузке, а не на каждый digest-цикл Angular — минус O(2N) вызовов на каждый рендер</li> </ul> <p><strong>Литература — прямая публикация:</strong></p> <ul> <li data-num="457"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Прямой body</strong> — тексты литературного формата публикуются напрямую в основное тело документа, без промежуточных медиа-объектов</li> <li data-num="458"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Синхронизация медиа</strong> — при переключении типа контента медиа автоматически пересчитываются под новую структуру</li> <li data-num="459"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Полная интеграция Tools-панели</strong> — ночной режим, автотипограф, счётчики слов/символов и кнопка приглашений теперь доступны и в шаблоне литературы</li> </ul> <p><strong>Стабильность:</strong></p> <ul> <li data-num="460"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Утечка таймеров рейтинга</strong> — popover-таймеры корректно отменяются при уходе со страницы (<code>$destroy</code>). Поиск попапа через <code>aria-describedby</code> вместо глобального DOM-сканирования — нулевой риск коллизий</li> <li data-num="461"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Изоляция ленты по аккаунту</strong> — ключи <code>lastSeen</code> в localStorage привязаны к userId, исключая смешение меток непрочитанного при переключении аккаунтов</li> <li data-num="462"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Чистая сортировка уведомлений</strong> — <code>_sortTimestamp</code> хранится как non-enumerable свойство, не загрязняя объекты при сериализации и <code>$watch</code></li> <li data-num="463"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Кэш подписок</strong> — при просмотре собственных подписок кэш заполняется для ВСЕХ элементов, а не только текущего фильтра — переключение вкладок больше не теряет статусы</li> </ul> <p><strong>Производительность:</strong></p> <ul> <li data-num="464"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Дебаунс счётчиков ленты</strong> — несколько одновременных вызовов <code>loadUnreadCounts()</code> коалесцируются в один запрос, предотвращая дублирование API-вызовов при инициализации</li> </ul> <p><strong>UX:</strong></p> <ul> <li data-num="465"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Автоскрытие «Запросить доступ»</strong> — после отправки запроса кнопка мгновенно исчезает на всех страницах, состояние сохраняется между сессиями в localStorage</li> <li data-num="466"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>
1Анимированные точки авторизации</strong> — вместо статичного спиннера — живые пульсирующие точки «Авторизация...» в модалке входа</li> <li data-num="467"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Умный popover рейтинга</strong> — <code>auto bottom</code> вместо фиксированного направления: попап автоматически выбирает сторону с максимальным пространством</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.16.0</span><span class="changelog-meta__date">23-02-2026</span></span><h2 id="v9160-23-02-2026--запросы-на-соавторство-умные-сноски-и-мощные-инструменты-редактора">Запросы на соавторство, умные сноски и мощные инструменты редактора</h2></summary> <div class="changelog-section__body"> <p>Этот релиз трансформирует подход к созданию совместных материалов и работе с текстом. Теперь читатели могут проактивно присоединяться к работе над статьями, а авторы получили новые профессиональные инструменты: от проверки грамматики на лету до глобальной базы источников.</p> <p><strong>Совместная работа — проактивное соавторство:</strong></p> <ul> <li data-num="440"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Запросы на участие (Request Contribution)</strong> — любой авторизованный читатель теперь может запросить доступ к материалу. В удобном модальном окне можно выбрать желаемую роль (Соавтор, Редактор, Рецензент) и приложить мотивационное письмо.</li> <li data-num="441"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Мгновенное управление заявками</strong> — авторы видят новые запросы прямо в шторке уведомлений (колокольчик) и могут принять или отклонить заявку в один клик, без перехода в настройки.</li> <li data-num="442"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Email-нотификации</strong> — система автоматически рассылает стильные письма авторам о новых заявках на участие с указанием запрашиваемой роли.</li> <li data-num="443"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Кнопки запроса повсюду</strong> — запросить соавторство можно из четырёх точек: заголовок статьи, баннер shared-черновика, подвал с именем автора и страница «Нет доступа» — кнопка всегда под рукой, когда это нужно</li> </ul> <p><strong>Умные сноски и работа с источниками:</strong></p> <ul> <li data-num="444"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Глобальная база сносок</strong> — все сноски платформы теперь индексируются. При добавлении новой сноски в редакторе работает мгновенный полнотекстовый поиск с автодополнением по базе уже существующих сносок. Идеально для переиспользования источников и ссылок!</li> <li data-num="445"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Фоновая миграция</strong> — индексация десятков тысяч существующих сносок выполнена через высокопроизводительный <code>bulkWrite</code> без малейшего влияния на работу базы данных.</li> </ul> <p><strong>Панель инструментов редактора нового поколения:</strong></p> <ul> <li data-num="446"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Плавающая Tools-панель</strong> — новая элегантная шторка в шапке редактора, объединяющая все ключевые настройки и компактный индикатор статуса соединения.</li> <li data-num="447"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Проверка орфографии и грамматики (LanguageTool)</strong> — интеллектуальная проверка текста на лету. Красная волна для опечаток, оранжевая — для стилистики и пунктуации. Клик по подчёркиванию открывает попап с вариантами исправления в один клик. Поддерживает игнорирование правил и слов.</li> <li data-num="448"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Ночной режим (Dark Mode)</strong> — полноценная тёмная тема для редактора, переключаемая прямо из новой панели инструментов. Бережёт зрение при работе по ночам.</li> <li data-num="449"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <strong>Автотипограф и аналитика</strong> — запуск автотипографа и живые счётчики слов/символов теперь всегда под рукой.</li> </ul> <p><strong>UX, Стабильность и Безопасность:</strong></p> <ul> <li data-num="450"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <strong>Идеальная синхронизация LanguageTool</strong> — умная защита от сдвига позиций подчёркиваний при быстрой печати и мгновенное скрытие попапов-подсказок, если автор продолжает набор текста. Никаких «залипаний» интерфейса.</li> <li data-num="451"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Изоляция компонентов редактора</strong> — полная архитектурная защита от DOM-конфликтов. Если на странице встроено несколько редакторов (основной текст и подписи к фото), их тулбары, попапы и фоновые запросы работают абсолютно независимо друг от друга.</li> <li data-num="452"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <strong>Защита от спама (Rate Limiting)</strong> — на уровне бэкенда встроен строгий лимит на отправку запросов соавторства, защищающий авторов от флуда.</li> <li data-num="453"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <strong>Оптимизированные индексы MongoDB</strong> — создание новых <code>sparse</code> и <code>background</code> индексов для быстрого поиска сносок и маршрутизации запросов без блокировок при деплое.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.15.0</span><span class="changelog-meta__date">22-02-2026</span></span><h2 id="v9150-22-02-2026--мгновенная-актуализация-ролей-при-открытии-платформы">Мгновенная актуализация ролей при открытии платформы</h2></summary> <div class="changelog-section__body"> <p>Авторы, авторизовавшиеся давно до обновлений, теперь сразу видят сайт со всеми возможностями без перелогинивания.</p> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.14.0</span><span class="changelog-meta__date">22-02-2026</span></span><h2 id="v9140-22-02-2026--коллаборации-в-один-клик-и-seo-без-компромиссов">Коллаборации в один клик и SEO без компромиссов</h2></summary> <div class="changelog-section__body"> <p>Приглашения к соавторству теперь управляются прямо из черновиков — без лишних переходов. Google видит тот же контент, что и читатели — никакого скрытого текста, никакого cloaking. А галереи и визуальные материалы больше не выглядят «пустыми» в поисковой выдаче.</p> <p><strong>Коллаборации — управление приглашениями:</strong></p> <ul> <li data-num="428"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Принятие и отклонение приглашений прямо со вкладки «Совместные» на странице черновиков — кнопки «Принять» / «Отклонить» с индикатором загрузки и защитой от двойного клика</li> <li data-num="429"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Повторная отправка приглашений — для истёкших и отклонённых инвайтов кнопка «Отправить повторно» с анимацией и автообновлением списка</li> <li data-num="430"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Обратный отсчёт до истечения приглашения — «Ждет подтверждения (осталось 2 дня)», красная метка при истечении срока</li> <li data-num="431"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Корректный автор в уведомлениях об ответе на приглашение — инвайтер видит аватар и имя приглашённого, а не своё</li> </ul> <p><strong>SEO — полная перестройка логики для поисковых ботов:</strong></p> <ul> <li data-num="432"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> Поисковые боты (Google, Bing, Yandex) теперь получают тот же HTML, что и пользователи — устранён скрытый <code>#seo-prerender</code> блок, который Google мог расценить как cloaking</li> <li data-num="433"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> OG-теги инжектируются для ВСЕХ посетителей — правильный title, description и image видны при первом же запросе, до загрузки JavaScript</li> <li data-num="434"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> Черновики и превью защищены от индексации — <code>&lt;meta name=&quot;robots&quot; content=&quot;noindex&quot;&gt;</code> + заголовок <code>X-Robots-Tag</code> + приватный кеш</li> <li data-num="435"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> Умная генерация описаний — для галерей и контента без текста description собирается из метаданных (раздел, автор, теги), обрезается по границе слова с многоточием. Больше никакого «thin content» в Google Search Console</li> <li data-num="436"><span class="changelog-tag changelog-tag--SEO" data-tag-alias="SEO">SEO</span> JSON-LD разметка (Article, Video, Music, Image) использует тот же интеллектуальный description вместо пустых строк</li> </ul> <p><strong>UX и типографика:</strong></p> <ul> <li data-num="437"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Иконка «Открыть» в черновиках заменена на <code>fa-file-alt</code> — визуально понятнее</li> <li data-num="438"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Неразрывные пробелы в диалогах редактора — «Покин
1уть\\u00A0страницу?», «удалить\\u00A0материал?» больше не разрываются на две строки в самом некрасивом месте</li> <li data-num="439"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Метрики в карточках черновиков выровнены — единый gap, консистентные размеры иконок</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.13.0</span><span class="changelog-meta__date">21-02-2026</span></span><h2 id="v9130-21-02-2026--упоминания-сквозная-система-от-редактора-до-уведомлений">@упоминания: сквозная система от редактора до уведомлений</h2></summary> <div class="changelog-section__body"> <p>Набрал <code>@</code> — увидел коллег. Упомянул — они узнали. Система @mention работает в каждой точке ввода текста на платформе: в теле статьи, в комментариях редактора, в ремарках читателей, в ответах на правки и прямо из уведомлений — с единым автокомплитом и мгновенными нотификациями.</p> <p><strong>Редактор статей — @mention как часть текста:</strong></p> <ul> <li data-num="411"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Tiptap Mention Node — атомарный inline-элемент <code>@Имя</code> в теле публикации. Неразрывный блок: нельзя частично удалить или отредактировать, Backspace удаляет целиком. Рендерится как кликабельная ссылка на профиль автора</li> <li data-num="412"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Автокомплит в редакторе — при вводе <code>@</code> + 2 символа появляется dropdown с аватарками, именами и path&#39;ами пользователей. Навигация стрелками, выбор Enter/Tab, закрытие Escape. Debounce 200ms, поиск по API</li> <li data-num="413"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Позиционирование dropdown у курсора — popup автоматически отражается вверх, если не помещается снизу, и сдвигается от правого края экрана</li> </ul> <p><strong>Комментарии, ремарки, ответы — @mention везде:</strong></p> <ul> <li data-num="414"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Директива <code>mention-autocomplete</code> — универсальный атрибут для любого <code>&lt;textarea&gt;</code> или <code>&lt;input&gt;</code> с <code>ng-model</code>. Подставляет <code>@path</code> в текст при выборе из dropdown</li> <li data-num="415"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> @mention в модальном окне комментария — отдельная vanilla JS реализация (модалка создаётся через DOM API, вне Angular), с автоматическим cleanup при закрытии</li> <li data-num="416"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> @mention в ответах на комментарии — поле ввода ответа в сайдбаре редактора</li> <li data-num="417"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> @mention в ремарках читателей — textarea выделения текста (socialSelection)</li> <li data-num="418"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> @mention в ответах из уведомлений — textarea inline-ответа прямо в карточке уведомления</li> </ul> <p><strong>Уведомления — «вас упомянули»:</strong></p> <ul> <li data-num="419"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Новый тип уведомлений <code>mentions</code> — фиолетовая метка «Упоминание» в ленте</li> <li data-num="420"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Вкладка «Упоминания» на странице уведомлений с бейджем непрочитанных</li> <li data-num="421"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Контекстные сообщения: «Иван упомянул(а) вас в комментарии к «Название статьи»» — с различением комментария, ответа, ремарки и обсуждения правки</li> <li data-num="422"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Email-дайджест: упоминания включены в сводку (настраиваемо через <code>mentions_email</code> в профиле)</li> </ul> <p><strong>Рендеринг @mention в тексте:</strong></p> <ul> <li data-num="423"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Фильтр <code>mentionLinks</code> — автоматическое превращение <code>@path</code> в кликабельные ссылки на профиль при отображении комментариев и ответов</li> <li data-num="424"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Стили <code>.tiptap-mention</code> и <code>.mention-link</code> — голубые ссылки с hover-эффектом, единый визуальный язык для всех упоминаний</li> </ul> <p><strong>Бэкенд:</strong></p> <ul> <li data-num="425"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <code>extractMentionedUsers()</code> — серверный парсинг <code>@path</code> из текста с декодированием HTML entities и batch-запросом пользователей из MongoDB</li> <li data-num="426"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <code>createMentionNotifications()</code> — создание уведомлений для каждого упомянутого (исключая автора сообщения)</li> <li data-num="427"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Интеграция в 4 POST-обработчика: комментарии, ответы на комментарии, ремарки, ответы в обсуждениях правок</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.12.1</span><span class="changelog-meta__date">20-02-2026</span></span><h2 id="v9121-20-02-2026--безопасность-производительность-и-стабильность-
1pre-release-polish">Безопасность, производительность и стабильность: pre-release polish</h2></summary> <div class="changelog-section__body"> <p><strong>Безопасность:</strong></p> <ul> <li data-num="397"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Санитизация <code>doc.body</code> в уведомлениях — HTML-теги стрипаются до plain text, обрезка до 300 символов. Закрыт вектор XSS через комментарии и ремарки в email-дайджесте</li> <li data-num="398"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Проверка прав на <code>/collab/suggestion-action</code> — только автор, соредакторы и админы могут принимать/отклонять правки. Раньше любой авторизованный пользователь мог вызвать endpoint</li> <li data-num="399"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Whitelist для <code>notificationPreferences</code> — только 12 допустимых boolean-ключей сохраняются в профиль, произвольные данные отсекаются</li> <li data-num="400"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>_escapeHtml</code> в toast-уведомлениях теперь экранирует одинарные кавычки (<code>&#39;</code> → <code>&#39;</code>)</li> </ul> <p><strong>Производительность:</strong></p> <ul> <li data-num="401"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Счётчики и флаги уведомлений (<code>notificationsCount</code>, <code>hasUnreadForActiveType</code>, <code>hasAnyForActiveType</code>) перевычисляются в <code>$watch</code> — один раз за digest вместо 12+ full-scan вызовов из шаблона</li> <li data-num="402"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Fingerprint кэша <code>displayItems()</code> усилен <code>_id</code> крайних элементов — нет stale-кэша при замене элемента</li> <li data-num="403"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Cloudflare Worker: chunked-ответы без <code>content-length</code> больше не буферизуются целиком — streaming-режим через HTMLRewriter без блокировки памяти</li> </ul> <p><strong>Стабильность:</strong></p> <ul> <li data-num="404"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Лимит 500 на <code>_seenNotificationIds</code> в sessionStorage с ротацией старых записей — больше нет бесконечного роста и переполнения квоты</li> <li data-num="405"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> TTL-fallback в дайджесте: уведомления с <code>emailSent: false</code> старше 7 дней помечаются автоматически — больше не зависают навсегда для онлайн-пользователей</li> <li data-num="406"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>linkTitle</code> добавлен в projection публикации notifications — фронтенд получает поле корректно</li> <li data-num="407"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Индексы <code>{ emailSent, createdAt }</code> и <code>{ userId, createdAt }</code> на коллекции Notifications — быстрый дайджест и подписка</li> </ul> <p><strong>UX и доступность:</strong></p> <ul> <li data-num="408"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Порог свайпа удаления увеличен с 60px до 100px — защита от случайного удаления при прокрутке на мобильных</li> <li data-num="409"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <code>aria-live=&quot;polite&quot;</code> на skeleton-заглушке уведомлений — скринридеры объявляют появление контента</li> <li data-num="410"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> Устранено дублирование логики в <code>notificationItem.js</code> — общий хелпер <code>_resolveDocData()</code> вместо двух копий</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.12.0</span><span class="changelog-meta__date">19-02-2026</span></span><h2 id="v9120-19-02-2026--уведомления-коллаборации-ночная-тема-редактора">Уведомления, коллаборации, ночная тема редактора</h2></summary> <div class="changelog-section__body"> <p><strong>Уведомления — полноценный центр активности:</strong></p> <p>Страница уведомлений превратилась из двух вкладок (комментарии / ремарки) в полноценный центр активности с шестью категориями, группировкой по материалам, inline-действиями, мобильными жестами, realtime-toast&#39;ами и умным email-дайджестом.</p> <ul> <li data-num="356"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Лента «Все» — единый поток уведомлений всех типов с цветными метками категорий (комментарий, ремарка, приглашение, публикация, правка, сообщение)</li> <li data-num="357"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Шесть фильтров с бейджами непрочитанных: Все, Комментарии, Ремарки, Приглашения, Публикации, Сообщения</li> <li data-num="358"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Группировка по материалам — уведомления объединяются в collapsible-карточки по публикациям, уменьшая визуальный шум при активных обсуждениях</li> <li data-num="359"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кнопка «Прочитать все» — batch-update непрочитанных для текущего фильтра с optimistic UI и rollback при ошибке</li> <li data-num="360"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Mark as read при клике — переход по уведомлению мгновенно помечает его прочитанным</li> <li data-num="361"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Пагинация «Загрузить ещё» — инкрементальная подгрузка по 50 уведомлений через DDP-подписку с <code>limit</code></li> <li data-num="362"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Удаление уведомлений — кнопка удаления рядом с «отписаться» + свайп влево на мобильных</li> <li data-num="363"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Уведомления о личных сообщениях — новый тип <code>messages</code> с виртуальными карточками диалогов и ссылками на <code>/messages/:id</code></li> <li data-num="364"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Уведомления о принятии/отклонении правок — соавтор получает уведомление при accept/reject его suggestion</li> <li data-num="365"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Inline-действия: кнопки «Принять» / «Отклонить» приглашение прямо в карточке уведомления, «Ответить» на комментарий — без перехода в редактор</li> <li data-num="366"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Realtime toast-уведомления — новое уведомление по WebSocket показывает toast вверху экрана с навигационной ссылкой, дедупликация повторных toast в течение 3 секунд</li> <li data-num="367"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Настройки уведомлений — страница в профиле для включения/выключения email и on-site уведомлений по каждой категории (комментарии, ремарки, приглашения, публикации, правки, сообщения)</li> <li data-num="368"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Умный email-дайджест — адаптивный интервал на основе WebSocket-присутствия: онлайн-пользователям email не отправляется, офлайн &gt;1 часа — сразу, недавно отключился — ждём следующий цикл</li> <li data-num="369"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Мобильная оптимизация: адаптивная верстка карточек, swipe-жесты (влево — удалить, вправо — прочитать) через Pointer Events API, нативный вид фильтр-чипов, <code>prefers-reduced-motion</code> для анимаций</li> <li data-num="370"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Skeleton-заглушка при загрузке уведомлений с <code>aria-busy</code> и контекстные пустые состояния по типу Ñ
1„ильтра</li> <li data-num="371"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Кэширование счётчиков — <code>notificationsCount</code> вычисляется один раз за digest вместо повторных проходов</li> <li data-num="372"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Очистка подписки <code>notifications</code> при уходе со страницы — нет утечки DDP-подписок</li> <li data-num="373"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>loadMore</code> — корректная обработка ошибок подписки (кнопка не залипает)</li> </ul> <p><strong>Совместные материалы — новая вкладка «Совместные»:</strong></p> <p>В списке материалов автора появилась вкладка «Совместные»: все документы, где пользователь участвует через комментарии, правки или приглашения. Карточка показывает роль (соавтор, редактор, комментатор), метрики активности, кнопку перехода в комментарии.</p> <ul> <li data-num="374"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Вкладка «Совместные» в «Мои материалы» с отдельным счётчиком на табе</li> <li data-num="375"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Карточка коллаборации: роль участника, количество комментариев/правок, число участников</li> <li data-num="376"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Быстрый переход «Комментарии» — открывает редактор сразу с боковой панелью комментариев (<code>?sidebar=comments</code>)</li> <li data-num="377"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Сортировка по активности или по наÐ
1·Ð²Ð°Ð½Ð¸ÑŽ</li> <li data-num="378"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> API <code>GET /my/collaborations</code> с пагинацией, поиском и счётчиками</li> </ul> <p><strong>Ночная тема редактора:</strong></p> <p>Полноценный dark mode для редактора — переключается горячей клавишей Ctrl+Shift+N (Night) или сохраняется в localStorage. Покрывает ProseMirror-поле, тулбар, сайдбар комментариев, диалоги, справку горячих клавиш и шапку.</p> <ul> <li data-num="379"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Dark mode: ~700 строк CSS для всех компонентов редактора (ProseMirror, тулбар, сайдбар, шапка, диалоги, TOC)</li> <li data-num="380"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Горячая клавиша Ctrl+Shift+N (Night) + запись в справке горячих клавиш</li> <li data-num="381"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Плавная анимация переключения (<code>dark-transition</code> класс) с сохранением в localStorage</li> <li data-num="382"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Автоматический cleanup класса <code>dark</code> при уходе со страницы редактора</li> </ul> <p><strong>Фильтр комментариев в редакторе:</strong></p> <ul> <li data-num="383"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Переключатель «Все / Открытые / Закрытые» в сайдбаре комментариев — мгновенная фильтрация без перезагрузки</li> <li data-num="384"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Визуальные индикаторы: «решён» (зелёная полоска) и «отклонён» (красная полоска) вместо полупрозрачности</li> <li data-num="385"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Rejected-комментарии остаются в списке как «отклонённые» (а не исчезают) — полная история модерации<
1/li> <li data-num="386"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Кнопки действий скрыты у отклонённых комментариев — нельзя повторно принять/отклонить</li> </ul> <p><strong>Трекинг просмотров — «новые» комментарии и правки:</strong></p> <ul> <li data-num="387"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> localStorage-трекинг просмотренных комментариев/правок (по document) — счётчик на кнопке панели показывает именно непросмотренные, а не неразрешённые</li> <li data-num="388"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Автоматическая отметка «просмотрено» при открытии боковой панели</li> </ul> <p><strong>Производительность и LCP:</strong></p> <ul> <li data-num="389"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Оптимизация srcset: убраны избыточные 2560w-варианты для обложек, добавлены промежуточные размеры (480w, 800w) для компактных карточек — меньше трафика без потери качества</li> <li data-num="390"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> SSR homepage hero: Worker инжектирует полную карточку героя (image + title + author + category) прямо в <code>&lt;main&gt;</code> — LCP-элемент виден на FCP, до загрузки JavaScript</li> <li data-num="391"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>scheduler.yield()</code> для Angular bootstrap — современный API приоритетного yield вместо <code>setTimeout(0)</code>, с graceful fallback</li> <li data-num="392"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>content-visibility: auto</code> для секций слайдера избранного — предотвращение CLS при скролле</li> <li data-num="393"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Inline CSS minification в HTML template — экономия ~3 КиБ без риска поломки proxy-парсеров</li> <li data-num="394"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Strip sourceMappingURL из vendor-бандла — нет 404 для .map-файлов в production</li> </ul> <p><strong>Доступность:</strong></p> <ul> <li data-num="395"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>focus-visible</code> outlines на всех интерактивных элементах сайдбара (табы, кнопки, карточки комментариев, фильтр)</li> <li data-num="396"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> Hover-стилизация для кнопок фильтра и табов сайдбара</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.11.3</span><span class="changelog-meta__date">19-02-2026</span></span><h2 id="v9113-19-02-2026--загрузка-обложек-дублирование-текста-конфликты-версия">Загрузка обложек, дублирование текста, конфликты версия</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="353"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Сохранение и обновление обложек</strong>. Теперь обложки без проблем сохраняются и существующих черновиков и материалов.</li> <li data-num="354"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Оглавление: работает в режиме просмотра публикаций</strong>. Теперь оглавление кликабельно и интерактивно во всех режимах взаимодействия с материалами.</li> <li data-num="355"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <strong>Стилизация оглавлений и вложенных заголовков</strong>. Скругленные рамки в редакторе, режиме предпросмотра и в открытых материалах.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.11.2</span><span class="changelog-meta__date">18-02-2026</span></span><h2 id="v9112-18-02-2026--changelog-полировка-ошибок-видео-и-email">Changelog, полировка ошибок, видео и email</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="345"><strong>Страница Changelog</strong> — публичная история обновлений платформы теперь доступна на <code>/changelog</code>. Плавающее оглавление для навигации, аккордеоны версий, цветные бейджи <code>[NEW]</code> / <code>[FIX]</code> / <code>[SEC]</code>. Сам список обновление автоматически генерируется из записей разработчиков в <code>CHANGELOG.md</code> при каждой сборке.</li> <li data-num="346"><strong>Страница ошибки — не тупик, а развилка</strong> — красивая кнопка «Вернуться на главную», поле поиска, корректное отображение на любых экранах (включая мобильный ландшафт). Header, footer и плеер остаются видимыми на stub-страницах — пользователь не теряет навигацию.</li> <li data-num="347"><strong>Копирование ссылки одним кликом</strong> — при наведении на ссылку в редакторе появл
1яется кнопка копирования с визуальным подтверждением. Работает во всех браузерах, включая старые — с автоматическим fallback.</li> <li data-num="348"><strong>Видео без чёрных полос</strong> — YouTube, Vimeo и RuTube масштабируются 16:9 на любых экранах. Приватные Vimeo (с hash) воспроизводятся в галерее. Spotify и Twitter embed не «прыгают» при загрузке.</li> <li data-num="349"><strong>Профили авторов</strong> — читатели видят только опубликованные материалы, авторы и редакция — все свои тексты, включая поданные на модерацию.</li> <li data-num="350"><strong>Доступ к материалам</strong> — единая страница «Нет доступа» вместо технических ошибок. Материалы на модерации: авторизованным — полный текст, анонимным — превью с предложением войти.</li> <li data-num="351"><strong>Email-уведомления</strong> — верификация, сброс пароля и приглашения авторов переведены на Mailgun. Отказоустойчивость: если почта недоступна, основные операции не прерываются.</li> <li data-num="352"><strong>Безопасность</strong> — точечная санитизация CSS (только опасные свойства, embed-контейнеры работают). Лимит загрузки картинок увеличен до 15 МБ.</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.11.1</span><span class="changelog-meta__date">17-02-2026</span></span><h2 id="v9111-17-02-2026--надёжность-безопасность-и-доступность-post-review-polish">Надёжность, безопасность и доступность: post-review polish</h2></summary> <div class="changelog-section__body"> <p>Главное: форма обратной связи с категориями и deep link’ами, ремарки читателей прямо в сайдбаре редактора (включая «отставшие» без привязки к тексту), подписки на теги с количеством публикаций, плюс двойной Backspace/Delete для удаления блоков и плавная смена SSR→Angular без скачков верстки.</p> <p><strong>Безопасность — ещё один слой защиты:</strong></p> <ul> <li data-num="334"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> XSS defense-in-depth в <code>stripTagsAndTruncate</code>: whitelist протоколов (<code>https:</code>, <code>mailto:</code>, <code>/</code>) для ссылок — <code>javascript:</code> и <code>data:</code> больше не проскочат даже при обходе ngSanitize</li> <li data-num="335"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Tag-closing logic: ложные срабатывания <code>&lt;i</code> → <code>&lt;/i&gt;</code> больше не путают <code>&lt;img&gt;</code> и <code>&lt;iframe&gt;</code> с курсивом</li> </ul> <p><strong>Редактор — курсор на месте:</strong></p> <ul> <li data-num="336"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Разворачивание спойлера с заголовком: курсор больше не прыгает к началу документа — смещение корректно учитывает вставленный параграф заголовка</li> <li data-num="337"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Бейдж «ремарка» вместо debug-артефакта «old» — читатель видит понятную метку</li> <li data-num="338"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>ng-attr-role</code> больше не рендерит литерал <code>&quot;undefined&quot;</code> в DOM — чистый HTML для screen readers</li> </ul> <p><strong>Ремарки — ни одна не потеряется:</strong></p> <ul> <li data-num="339"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Backend: merge InlineComments + legacy Remarks с дедупликацией — во время миграции ремарки из обоих источников объединяются, а не теряются</li> <li data-num="340"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Backend: guard публикации корректно обрабатывает <code>directBody</code> — сравнивает с текущим body вместо слепого пропуска</li> <li data-num="341"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Backend: compound index <code>{documentName, isRemark, createdAt}</code> — запросы ремарок мгновенны даже на больших коллекциях</li> </ul> <p><strong>Доступность — клавиатура работает везде:</strong></p> <ul> <li data-num="342"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> Social-кнопки (VK, Facebook, X, Telegram) получили <code>role=&quot;button&quot;</code> и <code>tabindex=&quot;0&quot;</code> — полноценная клавиатурная навигация во всех четырёх точках: сайдбар статьи, мобильная панель, страница экспо, основной шаблон</li> <li data-num="343"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Тултип на обрезанных названиях материалов в баннере комментариев — наводишь и видишь полный заголовок</li> </ul> <p><strong>Лента — аккуратные карточки:</strong></p> <ul> <li data-num="344"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Ограничение высоты превью в карточках ленты (<code>max-height: 6.4em</code>) — после удаления <code>-webkit-line-clamp</code> текст больше не вываливается за границы карточки</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.11.0</span><span class="changelog-meta__date">16-02-2026</span></span><h2 id="v9110-16-02-2026--категории-обратной-связи-ремарки-в-редакторе-подписки-на-теги">Категории обратной связи, ремарки в редакторе, подписки на теги</h2></summary> <div class="changelog-section__body"> <p><strong>Форма обратной связи — теперь с категориями:</strong></p> <p>Форма «Предложить идею» превратилась в полноценный центр обратной связи. Читатель выбирает тему из выпадающего списка — предложить идею, сообщить о баге, предложить сотрудничество, пожертвования и другие — и видит подсказку-плейсхолдер, адаптированную к выбранной теме. Форма открывается по прямой ссылке с хешем (<code>#bug</code>, <code>#help</code>, <code>#collab</code>), с обратной совместимостью legacy-формата <code>#idea</code>.</p> <ul> <li data-num="305"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кастомный dropdown с категориями тем: идея, помощь, опечатка, пожертвования, баг, сотрудничество, другое</li> <li data-num="306"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Контекстные плейсхолдеры для каждой категории — пользователь сразу понимает, что именно описать</li> <li data-num="307"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Deep link: <code>#bug</code>, <code>#help</code>, <code>#collab</code> и т.д. — открывает модалку с нужной темой</li> <li data-num="308"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Keyboard-навигация (ArrowUp/Down, Escape), ARIA-атрибуты, закрытие по клику вне области</li> <li data-num="309"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Защита от двойной отправки (<code>ng-disabled</code> во время отправки)</li> <li data-num="310"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Нативная валидация email (<code>type=&quot;email&quot;</code>, <code>autocomplete=&quot;email&quot;</code>)</li> <li data-num="311"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Dark mode для dropdown</li> <li data-num="312"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>$timeout</code> при автооткрытии модалки по хешу — DOM гарантированно готов</li> <li data-num="313"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>$onDestroy</code> cleanup для document click listener — нет утечки памяти</li> <li data-num="314"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> Inline-стили вынесены в CSS-классы (<code>form-group--topic</code>, <code>send-idea-submit__btn</code>)</li> </ul> <p><strong>Ремарки читателей — теперь прямо в редакторе:</strong></p> <p>Ремарки (исправления от читателей) приходят прямо в сайдбар комментариев редактора. Автор видит их вместе с обычными комментариями, помеченными бейджом «ремарка» и жёлтой полоской. Можно принять, отклонить, ответить — как с обычными комментариями.</p> <ul> <li data-num="315"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ремарки сохраняются как InlineComment с <code>isRemark: true</code> через <code>POST /collab/remarks/:documentName</code></li> <li data-num="316"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Orphaned-ремарки (без привязки к фрагменту) отображаются с иконкой fa-unlink и тултипом «Не привязана к тексту»</li> <li data-num="317"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Бейдж «ремарка» в хедере комментария + цитируемый текст <code>quotedText</code></li> <li data-num="318"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Текст подтверждения для читателя: «Автор увидит её в редакторе»</li> <li data-num="319"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Backend: поле <code>isRemark</code> добавлено в projection <code>GET /collab/comments</code> — ремарки теперь корректно идентифицируются</li> <li data-num="320"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Валидация contentItem ID (regex от path traversal) + гард на пустой ID перед отправкой</li> <li data-num="321"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Защита от двойной отправки ремарки (<code>remarkIsSending</code> guard)</li> <li data-num="322"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Консистентный <code>.trim()</code> для textSelected, textBefore, textAfter</li> </ul> <p><strong>Подписки — теги с описаниями:</strong></p> <ul> <li data-num="323"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Теги показывают количество публикаций (через новый фильтр <code>pluralize</code>)</li> <li data-num="324"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> SVG-иконка хештега вместо FontAwesome (<code>/partials/icons/hashtag.html</code>)</li> <li data-num="325"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Стилевые переменные <code>$color-tag</code> / <code>$color-tag-dark</code> для единообразного оформления</li> <li data-num="326"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Описания предвычисляются один раз при фильтрации вместо вызова на каждом digest-цикле</li> </ul> <p><strong>Редактор — стабильнее и чище:</strong></p> <ul> <li data-num="327"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Шаблон списка комментариев вынесен в общий partial 
1<code>comment-list.html</code> — убрана дупликация ~110 строк между Article и Literature</li> <li data-num="328"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Иконка хайлайтера: CSS-mask <code>.btn-icon-highlighter</code> → inline SVG + константа <code>ICON_HIGHLIGHTER</code></li> <li data-num="329"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Кеширование <code>_sortTime</code> перед сортировкой комментариев — <code>new Date()</code> вызывается O(n) вместо O(n log n)</li> <li data-num="330"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Orphaned-комментарии: <code>cursor: default</code>, no-op click на строке</li> <li data-num="331"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> Цвета remark-badge и orphaned-border вынесены в SCSS-переменные <code>$color-remark-</code>*</li> <li data-num="332"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> Фильтр <code>pluralize</code> с документацией различий от <code>russianNumber</code></li> <li data-num="333"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> Удалены decaffeinate-комментарии из sendFeedbackMessage и remarksService</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.10.0</span><span class="changelog-meta__date">15-02-2026</span></span><h2 id="v9100-15-02-2026--публикация-без-нервов-валидация-подтверждение-и-умные-ошибки">Публикация без нервов: валидация, подтверждение и умные ошибки</h2></summary> <div class="changelog-section__body"> <p><strong>Post-review фиксы:</strong></p> <ul> <li data-num="276"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Диалог сохранения: «Опубликовать» теперь primary-кнопка (confirm), «Сохранить черновик» — secondary (cancel). Семантика корректна, нет риска случайной публикации через cancel-кнопку</li> <li data-num="277"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Publish: retry при HTTP 409 (optimistic locking race с autosave) — автоматический повтор через 500ms</li> <li data-num="278"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Validation guard: если onValidateBeforePublish вернёт Promise вместо boolean — publish блокируется</li> <li data-num="279"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> SWR снижен 300→120 сек (2 мин) — исправления/правки видны быстрее, при сохранении кэш-преимуществ</li> <li data-num="280"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Backward-compat middleware: fast-path по statusCode &lt; 400 — 99%+ success-ответов не парсятся</li> <li data-num="281"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> localStorage cleanup: MAX_SCAN=500 guard — защита от зависания на устройствах с тысячами ключей</li> <li data-num="282"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Опечатка в donate-success.html: «поддержу» → «поддержку»</li> <li data-num="283"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> captionEditorService/footnoteService: debug helper вместо дублирования console.debug в каждом catch</li> <li data-num="284"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">CODE</span> loadEditorScripts: удалён мёртвый код <strong>EDITOR_CSS_VERSION</strong> (DefinePlugin недоступен)</li> </ul> <p><strong>Публикация — теперь безопасно и понятно:</strong></p> <ul> <li data-num="285"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Двухступенчатая защита от случайной публикации: валидация полей → диалог подтверждения «Опубликовать материал?»</li> <li data-num="286"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Pre-publish валидация: заголовок, рубрика и минимальная длина текста проверяются ДО показа диалога — мгновенная обратная связь</li> <li data-num="287"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Медиа-типы (фото, видео, аудио, галереи) имеют пониженный порог длины текста — 10 символов вместо 50</li> <li data-num="288"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Защита от double-click: кнопка «Опубликовать» блокируется пока диалог открыт или идёт отправка</li> <li data-num="289"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Контекстные ошибки автосохранения вместо generic «Ошибка сохранения»: «Содержимое слишком большое», «Недостаточно прав», «Нет связи с сервером»</li> <li data-num="290"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Индикатор восстановления: после успешного сохранения ошибка автоматически исчезает — не висит до перезагрузки</li> </ul> <p><strong>Редактор — стабильнее и быстрее:</strong></p> <ul> <li data-num="291"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Scroll-обработчик social selection теперь throttled через requestAnimationFrame — 1 DOM-чтение за кадр вместо 60+</li> <li data-num="292"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> One-time <code>$watch</code> unwatching: <code>contentItem</code>, <code>
1tiptapEditor</code> и <code>editorInstance</code> watchers отключаются после инициализации — меньше работы на каждом digest</li> <li data-num="293"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Вставка изображений: retry с exponential backoff (до 3 попыток) — устойчивость к временным сбоям сети</li> <li data-num="294"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> localStorage cleanup: проактивная очистка просроченных бекапов через <code>requestIdleCallback</code>, защита от QuotaExceededError</li> <li data-num="295"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Ретеншн бекапов увеличен до 7 дней — длинные материалы больше не теряются при пересмотре через несколько дней</li> <li data-num="296"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Инвалидация feed-кэшей после публикации: автор мгновенно видит свою статью на главной</li> </ul> <p><strong>Кэширование и сеть:</strong></p> <ul> <li data-num="297"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Stale-while-revalidate увеличен до 5 мин — мгновенные ответы из кэша, ревалидация в фоне</li> <li data-num="298"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Preconnect к Google Tag Manager, <code>fetchpriority=&quot;high&quot;</code> для основного CSS</li> <li data-num="299"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Cloudflare worker: <code>stale-while-revalidate</code> теперь использует единый <code>CONFIG</code> вместо хардкода</li> </ul> <p><strong>Безопасность и качество кода:</strong></p> <ul> <li data-num="300"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>rel=&quot;noopener noreferrer&quot;</code> на всех внешних ссылках (автор, footer, редактор)</li> <li data-num="301"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>loading=&quot;lazy&quot;</code> на изображениях editorial и donate pages</li> <li data-num="302"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> ESLint конфигурация с <code>no-unsanitized</code> plugin + lint-staged интеграция</li> <li data-num="303"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>console.log</code> → <code>console.debug</code> по всей кодовой базе — чистая production-консоль</li> <li data-num="304"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> MAX_RESCHEDULE_COUNT увеличен до 10 с visible warning — автосохранение толерантнее к медленным серверам</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.9.0</span><span class="changelog-meta__date">14-02-2026</span></span><h2 id="v990-14-02-2026--черновики-без-границ-совместное-редактирование-общий-доступ-по-ссылке-и-безопасность-нового-поколения">Черновики без границ: совместное редактирование, общий доступ по ссылке и безопасность нового поколения</h2></summary> <div class="changelog-section__body"> <p><strong>Совместное редактирование — наконец по-настоящему:</strong></p> <ul> <li data-num="236"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Приглашение соавторов прямо из редактора — поиск пользователей, выбор роли (автор, редактор, комментатор), отправка по email</li> <li data-num="237"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Общий доступ по ссылке: «Любой, у кого есть ссылка» / «Только авторизованные» / «Ограниченный» — как в Google Docs, но для Дискурса</li> <li data-num="238"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Копирование ссылки общего доступа одним кликом с fallback для старых браузеров</li> <li data-num="239"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Панель управления приглашениями: статусы, смена ролей, отзыв доступа</li> <li data-num="240"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Три роли с понятными описаниями — «Соавтор: редактирование и публикация», «Редактор: правки и комментарии», «Комментатор: только комментарии»</li> </ul> <p><strong>Черновики — полный workflow:</strong></p> <ul> <li data-num="241"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Сохранение нового материала как черновик (без публикации) — кнопка «Сохранить» создаёт черновик и переходит к полноценному редактору с автосохранением</li> <li data-num="242"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Публикация новых экспо-материалов (фото, видео, музыка, альбомы) через единый <code>sendFormForPublish</code> — больше нет разделения article vs expo</li> <li data-num="243"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Страница «Мои вещи» с фильтрацией по статусам: Черновик, На модерации, В журнале, Опубликован, Отозван</li> <li data-num="244"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span>
1 Поиск по названию материалов с <code>type=&quot;search&quot;</code> — нативный clear-button и мобильная клавиатура</li> <li data-num="245"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Бейджи статусов с цветовой дифференциацией, индикатор общего доступа рядом с заголовком</li> <li data-num="246"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Терминология обновлена: «Отборные» → «В журнале», «Опубликован» → «На модерации» для posted-материалов</li> </ul> <p><strong>Безопасность — DOMPurify вместо blind trust:</strong></p> <ul> <li data-num="247"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Новый фильтр <code>| trustedHtml</code> на базе DOMPurify заменяет старый <code>| unsafe</code> — полная санитизация HTML с whitelist тегов, атрибутов и data-URI</li> <li data-num="248"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> iframe-домены ограничены allowlist&#39;ом (YouTube, Vimeo, Spotify, SoundCloud, Bandcamp, VK, Telegram и др.)</li> <li data-num="249"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> CSS-санитизация через CSSStyleDeclaration API — не обходится CSS-escape&#39;ами в отличие от regex</li> <li data-num="250"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>data:</code> URI заблокированы глобально, <code>expression()</code> в стилях отлавливается</li> <li data-num="251"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> LRU-кэш санитизированного HTML с кэшированием <code>$sce.trustAsHtml()</code> — zero allocation overhead на каждый digest</li> <li data-num="252"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Cloudflare worker: HSTS (<code>max-age=63072000</code>), расширенный <code>isPrivatePage</code> на все приватные маршруты</li> <li data-num="253"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> DOMPurify закреплён на точной версии 3.3.1 — обновления security-critical зависимости только с ревью</li> </ul> <p><strong>Cloudflare worker — edge-безопасность:</strong></p> <ul> <li data-num="254"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> <code>isDraftContent</code> унифицирован: материалы со статусом <code>posted</code> больше не утекают через edge cache</li> <li data-num="255"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Приватные маршруты <code>/edit/</code>, <code>/preview</code>, <code>/editorial</code>, <code>/notifications</code> получили server-side <code>&lt;meta name=&quot;robots&quot; content=&quot;noindex&quot;&gt;</code></li> <li data-num="256"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> HSTS header для принудительного HTTPS на всех поддоменах</li> <li data-num="257"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Предотвращение cache poisoning: draft и preview контент всегда <code>private, no-store, no-cache</code></li> </ul> <p><strong>Производительность:</strong></p> <ul> <li data-num="258"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>$scope.$applyAsync()</code> в success-path баннеров — гарантированный digest после async/await</li> <li data-num="259"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>handleScroll</code> — early return на страницах без редактора (не сканирует DOM каждые 16мс)</li> <li data-num="260"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> fireplace.png (2.9 МБ) → fireplace.webp (251 КБ) — размер логин-страницы уменьшен в 11 раз</li> <li data-num="261"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> SCSS: <code>@mixin tiptap-placeholder</code> и <code>list-styles</code> вынесены в shared <code>_tiptap-mixins.scss</code> — один источник правды</li> <li data-num="262"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>$timeout</code> → <code>$scope.$applyAsync</code> в tiptap-контроллерах — меньше таймеров, быстрее digest</li> <li data-num="263"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>track by</code> в ng-repeat для приглашений, ролей, результатов поиска — меньше перерисовок DOM</li> </ul> <p><strong>Доступность:</strong></p> <ul> <li data-num="264"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> <code>aria-hidden=&quot;true&quot;</code> на всех декоративных SVG в панели приглашений (6 иконок)</li> <li data-num="265"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>ng-attr-aria-pressed</code> на кнопке sidebar — корректное значение при undefined</li> <li data-num="266"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>ng-cloak</code> на экране «Нет доступа» — предотвращает flash of unstyled content</li> <li data-num="267"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> Disabled-кнопки в истории версий: <code>cursor: not-allowed</code> вместо <code>pointer-events: none</code> — сохранены тултипы</li> </ul> <p><strong>Стабильность:</strong></p> <ul> <li data-num="268"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>deferred.resolve(err)</code> → <code>deferred.reject(err)</code> — API-ошибки корректно отклоняют промисы</li> <li data-num="269"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>VERSION_SAVE_ERROR</code> корректно извлекает <code>.message</code> из Error-объектов</li> <li data-num="270"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>CONTENT_PUBLISH_ERROR</code> — JSDoc, обработка Error instances, безопасная обрезка до 200 символов</li> <li data-num="271"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>sendFormForPublish</code> использует <code>$q</code> вместо native <code>Promise</code> — гарантированный digest cycle</li> <li data-num="272"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Дублированный tooltip «Сохранить черновик (Ctrl+S)» объединён в один элемент</li> <li data-num="273"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> 10+ утечек памяти: <code>$rootScope.$on</code>, <code>window.resize</code>, scroll-хэндлеры чистятся в <code>$destroy</code></li> <li data-num="274"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Все <code>__guard_</code>_ хелперы из decaffeinate заменены на idiomatic null checks</li> <li data-num="275"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Restangular: убраны заголовки <code>X-User-Id: undefined</code> для неавторизованных запросов</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.8.0</span><span class="changelog-meta__date">13-02-2026</span></span><h2 id="v980-13-02-2026---уведомления-мечты-черновики-наяву-и-защита-без-компромиссов">Уведомления мечты, черновики наяву и защита без компромиссов</h2></summary> <div class="changelog-section__body"> <p><strong>Уведомления — полностью переосмыслены:</strong></p> <ul> <li data-num="174"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Единая система тостов с типизацией: <code>success</code> (зелёный), <code>error</code> (красный), <code>warning</code> (жёлтый треугольник), <code>info</code> (синий круг) — каждое сообщение визуально передаёт своё значение</li> <li data-num="175"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Дедупликация уведомлений: повторный клик не плодит стопку одинаковых тостов — вместо этого существующий «подмигивает» (nudge-анимация)</li> <li data-num="176"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> <code>ConfirmDialog</code> — стильная замена уродливому <code>window.confirm()</code>: фокус-трап, Escape, ARIA, CSS-анимация, поддержка типов (danger/warning/info)</li> <li data-num="177"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> SVG-иконки в каждом типе уведомления: галочка, крестик, треугольник с восклицательным знаком, i-в-кружке</li> <li data-num="178"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span>
1 Кнопка «×» для ручного dismiss (появляется при наведении, видима для keyboard users)</li> <li data-num="179"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>role=&quot;status&quot;</code>, <code>aria-live=&quot;polite&quot;</code>, <code>aria-atomic=&quot;true&quot;</code> — screen readers озвучивают каждый тост</li> <li data-num="180"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>prefers-reduced-motion</code> отключает все анимации тостов и диалога подтверждения</li> <li data-num="181"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> WCAG AA контраст на всех кнопках: warning-кнопка получила тёмный текст вместо белого на жёлтом</li> <li data-num="182"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Русификация backend-ошибок: «Email must be a valid e-mail address» → «Укажите корректный адрес электронной почты»</li> <li data-num="183"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Тон сообщений нормализован — спокойный редакционный стиль без восклицательных знаков и HTML-тегов</li> </ul> <p><strong>«Мои вещи» — черновики открыты:</strong></p> <ul> <li data-num="184"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Страница «Мои вещи» (черновики и материалы) доступна из меню — больше не за заглушкой</li> <li data-num="185"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кнопка «Сохранить в черновики» в редакторе для незавершённых материалов</li> <li data-num="186"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кнопка «Сохранить версию» с иконкой дискеты восстановлена в шапке и в панели истории</li> <li data-num="187"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Пустой профиль автора ведёт на черновики: «Здесь появятся ваши публикации» → «Мои материалы»</li> </ul> <p><strong>Читатели голосуют за комментарии:</strong></p> <ul> <li data-num="188"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Голосование за комментарии открыто всем зарегистрированным пользователям — не нужно быть автором, чтобы оценить чужое мнение</li> <li data-num="189"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Четыре состояния кнопки рейтинга: попавер (авторы/админы), подсказка «стань автором» (читатели), регистрация (анонимы), заблюренный плейсхолдер (публикации)</li> <li data-num="190"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Кнопки ±1/±2 блокируются во время загрузки — двойной клик больше не дублирует API-запросы</li> </ul> <p><strong>Лента — умнее и быстрее:</strong></p> <ul> <li data-num="191"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Новый период «За три месяца» и явный пункт «За всё время» (дефолт) — все опции равноправны</li> <li data-num="192"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Календарная арифметика через <code>setMonth()</code>/<code>setFullYear()</code> — «за месяц» всегда ровно месяц</li> <li data-num="193"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Счётчик просмотров скрыт от читателей — авторская метрика; <code>canViewEditorialMetrics()</code> применён в 50+ местах</li> </ul> <p><strong>Безопасность — 6 закрытых уязвимостей:</strong></p> <ul> <li data-num="194"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> XSS-уязвимость в дедупликации уведомлений: <code>innerHTML</code> → безопасный <code>DOMParser</code></li> <li data-num="195"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Серверная валидация значения голоса: <code>[-1, 0, 1]</code> для публикаций, <code>[-2..2]</code> для кармы</li> <li data-num="196"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Ранняя валидация типа объекта через <code>
1VOTE_TYPE_CONFIG</code> — неизвестные типы отсеиваются до проверки разрешений</li> <li data-num="197"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> CSP-safe обработчик ссылок в уведомлениях с guard от отсутствия jQuery</li> <li data-num="198"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Grid directive: DI-аннотация через inline array — не ломается при минификации</li> <li data-num="199"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Danger-диалоги фокусируют cancel-кнопку — защита от случайного подтверждения деструктивного действия</li> </ul> <p><strong>Модалка входа — мгновенные переключения:</strong></p> <ul> <li data-num="200"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <code>ng-show</code> → <code>ng-if</code> — лишние DOM-элементы удаляются, экономя ресурсы</li> <li data-num="201"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Ошибка «Такого аккаунта нет» переехала прямо под поле ввода</li> <li data-num="202"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Flex-layout двупанельного режима: обе панели растягиваются равномерно, без overflow</li> <li data-num="203"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>aria-labelledby</code> корректен на всех экранах модалки; убран конфликт <code>role=&quot;alert&quot;</code> + <code>aria-live</code></li> </ul> <p><strong>Печать — колонтитул на каждой странице:</strong></p> <ul> <li data-num="204"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Печатный колонтитул через нативный <code>&lt;thead&gt;</code> повторяется вверху каждой страницы</li> <li data-num="205"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>position: fixed</code> → <code>&lt;table&gt;/&lt;thead&gt;</code> — браузер сам учитывает высоту при разбиении</li> <li data-num="206"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>&lt;header&gt;</code> полностью скрыт при печати — убраны остаточные артефакты</li> <li data-num="207"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>role=&quot;presentation&quot;</code> на print-обёртке — скринридеры не объявляют вспомогательную таблицу</li> </ul> <p><strong>Навигация и страница ошибки:</strong></p> <ul> <li data-num="208"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <code>/editorial</code> → <code>/feed/editorial</code> — единый маршрут со старыми редиректами</li> <li data-num="209"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Сетевые ошибки: «?» + текст «Проверьте соединение» + кнопка «Обновить страницу»</li> </ul> <p><strong>Редактор — правки объединяются правильно:</strong></p> <ul> <li data-num="210"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>findAdjacentSuggestion</code> → смежные правки в единый <code>&lt;span&gt;</code>, убран баг «подсветка по символу»</li> <li data-num="211"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>Cmd+A</code> / <code>Ctrl+A</code> принудительно устанавливает DOM Selection в Safari и Chrome</li> <li data-num="212"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>cleanHTML</code> сохраняет классы видеоэмбедов — видео не исчезают при сохранении
1</li> <li data-num="213"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Иконка хайлайтера: CSS-SVG вместо FontAwesome — легче и стабильнее</li> </ul> <p><strong>Стабильность — 12 закрытых багов:</strong></p> <ul> <li data-num="214"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>feedbackMessages.post()</code>: ошибка теперь корректно reject&#39;ит промис — нет ложных «сообщение отправлено»</li> <li data-num="215"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>comment.js</code>: API-ошибка при удалении комментария перехватывается и показывается пользователю</li> <li data-num="216"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>subscriptionService</code>: 409 → info «Вы уже подписаны», остальные ошибки → error</li> <li data-num="217"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>resetPasswordCtrl</code>: кнопка submit блокируется при пустом подтверждении пароля</li> <li data-num="218"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>resetPasswordCtrl</code>: backend-ошибки извлекаются из <code>e.data.message</code> вместо generic текста</li> <li data-num="219"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>privateEditorialCtrl</code>: resize-listener отвязывается при <code>$destroy</code> — устранена утечка памяти</li> <li data-num="220"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>editAuthorCtrl</code>: навигационный guard с флагом <code>_navigatingAway</code> — нет бесконечного цикла подтверждений</li> <li data-num="221"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>editAuthorCtrl</code>: безопасная экстракция Angular-пути для hashbang и HTML5 mode</li> <li data-num="222"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Lazy Notification wrappers (<code>s3uploader</code>, <code>loginService</code>): проксируют все аргументы и <code>primary()</code></li> <li data-num="223"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Dedup sticky timers очищаются при <code>clearAll()</code> — повторная нотификация не подавляется</li> <li data-num="224"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>verifyEmailCtrl</code>: <code>\\n</code> → пробел (невидимый перенос в HTML заменён на рабочий текст)</li> <li data-num="225"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Font Awesome: <code>.woff</code>-fallback для Safari — субсеттированные шрифты не пропадают в WebKit</li> <li data-num="226"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> <code>:focus-visible</code> добавлен на save-version button, notification unsubscribe, popover links</li> <li data-num="227"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> <code>transition: all</code> → конкретные свойства в editorHeader — предотвращает лишние reflow</li> <li data-num="228"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>sendFeedbackMessage</code>: добавлен <code>.catch()</code> для <code>feedbackMessages.post()</code> — reject больше не падает без обработки</li> <li data-num="229"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>editAuthorCtrl</code>: флаг <code>_navigatingAway</code> сбрасывается после навигации через <code>$timeout</code></li> <li data-num="230"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Lazy Notification wrappers: добавлен guard <code>typeof svc[type] === &#39;function&#39;</code> — защита от преждевременного вызова</li> <li data-num="231"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> CSS <code>:has()</code> fallback через класс <code>.counters-sibling</code> — совместимость со старым Firefox</li> <li data-num="232"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Мобильные уведомления: <code>top: 0 !important</code> — тосты прижаты к верху экрана без заÐ
1·Ð¾Ñ€Ð°</li> <li data-num="233"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Dark mode CSS custom properties для нотификаций: <code>prefers-color-scheme: dark</code> адаптирует фоны</li> <li data-num="234"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> <code>NOTIFICATION_MESSAGES</code> мигрированы в <code>editContentItemCtrl</code>, <code>newComment</code>, <code>comment</code>, <code>loginModal</code>, <code>loginService</code>, <code>editorPocCtrl</code>, <code>draftsCtrl</code>, <code>s3uploader</code></li> <li data-num="235"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> CHANGELOG: уточнено что дефолт фильтра — «За всё время», не «За три месяца»</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.7.0</span><span class="changelog-meta__date">12-02-2026</span></span><h2 id="v970-12-02-2026---правки-живут-вечно-suggestion-persistence-переосмысленные-комментарии-и-печать">Правки живут вечно: suggestion-persistence, переосмысленные комментарии и печать</h2></summary> <div class="changelog-section__body"> <p><strong>Главное — правки больше не теряются:</strong></p> <ul> <li data-num="151"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Suggestion marks переживают перезагрузку страницы — даже без HocusPocus. Новое поле <code>rawDraftBody</code> хранит полный HTML с track-changes разметкой; при загрузке редактор восстанавливает все pending-правки точно в том виде, в каком автор их оставил</li> <li data-num="152"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Оптимизированный single-pass pipeline сохранения: один DOMParser + cloneNode вместо трёх раздельных парсингов — быстрее и экономнее по памяти</li> <li data-num="153"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Backend Collab API: GET возвращает <code>rawBody</code>, PUT принимает <code>rawBody</code>, publish корректно очищает оба draft-поля через <code>$unset</code></li> </ul> <p><strong>Комментарии — новый уровень:</strong></p> <ul> <li data-num="154"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Предзагрузка комментариев и suggestion-тредов из API при первом открытии — текст комментариев виден мгновенно, без «пустой» вспышки</li> <li data-num="155"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Обсуждения правок (suggestion threads) загружаются с сервера и мержатся с Y.js — треды не теряются при отсутствии HocusPocus</li> <li data-num="156"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> CommentBubbleMenu автоматически персистит комментарии в backend — каждый комментарий, созданный через пузырь, сохраняется на сервере</li> <li data-num="157"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Retry-логика с <code>MAX_API_RETRIES</code> (3) — защита от бесконечных циклов при недоступном API</li> <li data-num="158"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Кнопки действий переехали в шапку карточки комментария: компактные иконки с плавным reveal текста при наведении (<code>max-width</code> transition + <code>@media (hover: hover)</code>)</li> <li data-num="159"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> «Принять все» / «Отклонить все» правки — стилизованные bootbox-диалоги вместо нативных <code>confirm()</code></li> </ul> <p><strong>Вход и главная:</strong></p> <ul> <li data-num="160"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> VK приоритетнее Facebook в ряду OAuth-кнопок — отражает реальные предпочтения аудитории</li> <li data-num="161"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Увеличена ширина формы входа (460px), отрицательные margins для ряда кнопок — дышащая компоновка</li> <li data-num="162"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Hero-баннер: «объединяться» → «создавать» — точнее передаёт миссию платформы</li> </ul> <p><strong>Печать:</strong></p> <ul> <li data-num="163"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Полноценные print-стили: скрыт весь UI-chrome (хедер, футер, кнопки, баннеры), логотип Discours на первой странице, корректные поля (<code>1.5cm × 1.5cm</code>, 2.5cm снизу для колофона), <code>orphans: 3 / widows: 3</code>, <code>page-break-inside: avoid</code> для изображений и подписей</li> </ul> <p><strong>Редактор — стабильность тулбаров:</strong></p> <ul> <li data-num="164"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Тулбары изображений, галерей и эмбедов больше не «проваливаются» под соседние блоки — <code>z-index: 100</code> + класс <code>toolbar-visible</code> на figure/gallery/sub-node при показе тулбара</li> <li data-num="165"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Ссылки в шапке редактора не окрашиваются в синий после посещения (<code>:visited/:active/:focus</code> reset)</li> </ul> <p><strong>Безопасность и надёжность:</strong></p> <ul> <li data-num="166"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Autosave PUT требует <code>canDirectEditContentItem</code> — comment-only авторы не могут перезаписать черновик</li> <li data-num="167"><span class="changelog-tag changelog-tag--SEC" data-tag-alias="SEC">SEC</span> Санитизация <code>replies</code> в bulk comment sync и single-comment PUT — структура и текст каждого ответа валидируются</li> <li data-num="168"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>destroyed</code>-guard на всех async callbacks — навигация во время API-запросов не вызывает ошибок</li> <li data-num="169"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Autosave reschedule при isSaving — пропущенный save перепланируется, данные не теряются</li> <li data-num="170"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Dedup в <code>onCommentAdd</code> — комментарий не дублируется в sidebar</li> <li data-num="171"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Publish записывает <code>updatedBy</code> — bodyHistory корректно атрибутирует автора</li> <li data-num="172"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>lastEditedBy</code> — типизированная sub-schema вместо <code>blackbox: true</code></li> </ul> <p><strong>Accessibility:</strong></p> <ul> <li data-num="173"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> Все кнопки-иконки комментариев имеют <code>aria-label</code>, <code>title</code> и <code>.btn-label</code> — доступны screen readers и информативны на touch-устройствах</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.6.0</span><span class="changelog-meta__date">12-02-2026</span></span><h2 id="v960-12-02-2026---галерея-спойлеры-oauth-черновики-и-обновлённый-вход">Галерея, спойлеры, OAuth, черновики и обновлённый вход</h2></summary> <div class="changelog-section__body"> <p><strong>Редактор — новые инструменты:</strong></p> <ul> <li data-num="121"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> 🖼️ Галерея изображений — полноценная карусель прямо в редакторе: drag одного файла создаёт картинку, нескольких — слайдшоу с навигацией, точками, подписями к каждому слайду и реордерингом</li> <li data-num="122"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> 🔒 Спойлер-блок (подкат) — сворачиваемый блок с кастомным заголовком; в опубликованной статье рендерится как нативный <code>&lt;details&gt;</code> с кнопкой «↓ Развернуть» / «↑ Свернуть» — без единой строчки JS</li> <li data-num="123"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Клавиатурный шорткат <code>Ctrl+Shift+H</code> для мгновенной вставки спойлера</li> <li data-num="124"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Конвертация одиночного изображения в галерею по нажатию «+» на тулбаре картинки</li> <li data-num="125"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Мульти-аплоад при вставке/перетаскивании нескольких изображений — автоматическое создание галереи</li> </ul> <p><strong>Вход и авторизация:</strong></p> <ul> <li data-num="126"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> 🔑 OAuth через Google, GitHub, Facebook и VK — один клик для входа</li> <li data-num="127"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Двупанельный экран входа с атмосферной картинкой при «парадном» входе из хедера</li> <li data-num="128"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Спиннер на конкретной OAuth-кнопке — видно, какой провайдер обрабатывается</li> <li data-num="129"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Безопасный обмен токена на одноразовый linkId при привязке провайдера к аккаунту (токен не утекает через URL/Referer)</li> </ul> <p><strong>Черновики:</strong></p> <ul> <li data-num="130"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Страница «Мои материалы» с поиском, фильтрацией по статусу и типу, сортировкой</li> <li data-num="131"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Модальное окно подтверждения удаления с focus trap и Escape — вместо нативного <code>confirm()</code></li> <li data-num="132"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Превью тела материала через <code>stripTagsAndTruncate</code> — единообразно с лентой</li> </ul> <p><strong>UX и визуал:</strong></p> <ul> <li data-num="133"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Sticky footer — подвал всегда прижат к низу, даже на коротких страницах</li> <li data-num="134"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Прогресс-бар загрузки редактора вместо спиннера — визуально чище, ощущение скорости</li> <li data-num="135"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Плавный fade-in при входе в режим предпросмотра (150ms, начинается с opacity: 0.3)</li> <li data-num="136"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Per-button loading на кнопках «Отправить» / «Сохранить черновик» — кнопка не прыгает</li> <li data-num="137"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Excerpt в ленте увеличен до 400 символов с CSS line-clamp (4 строки на мобильном, 6 на десктопе)</li> <li data-num="138"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Обновлённый текст hero-баннера</li> </ul> <p><strong>Стабильность:</strong></p> <ul> <li data-num="139"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Удалён устаревший postPreview-компонент — preview теперь inline через TipTap-редактор</li> <li data-num="140"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>updatingPath</code> при переключении editor↔preview больше не сбрасывает body-классы и не показывает оверлей</li> <li data-num="141"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Ребилд preview при прямой навигации на /preview URL — retry через 600ms + 1500ms для медленных сетей</li> <li data-num="142"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> <code>renderHTML</code> галереи переведён на DOMOutputSpec-массив — стабильная сериализация в getHTML/collab/SSR</li> <li data-num="143"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Shimmer-анимация на логин-картинке останавливается после загрузки; при ошибке панель скрывается целиком</li> <li data-num="144"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Backdrop-селектор <code>+</code> → <code>~</code> — корректная работа с verify-email модалкой между элементами</li> </ul> <p><strong>Accessibility:</strong></p> <ul> <li data-num="145"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">
1A11Y</span> Галерея: <code>role=&quot;region&quot;</code>, <code>aria-roledescription=&quot;carousel&quot;</code>, <code>aria-live</code> счётчик слайдов</li> <li data-num="146"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> Модалка удаления: focus trap, Escape, <code>aria-modal</code>, <code>tabindex</code>, scale-анимация</li> <li data-num="147"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> Спойлер: нативный <code>&lt;details&gt;/&lt;summary&gt;</code> для published — работает без JS, доступен screen readers</li> </ul> <p><strong>Рефакторинг:</strong></p> <ul> <li data-num="148"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Общая утилита <code>uploadMultipleImages()</code> — DRY для параллельной загрузки файлов</li> <li data-num="149"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> <code>editorHeader.publish()</code> — корректная обработка Angular <code>&amp;</code> binding (убран мёртвый fallback)</li> <li data-num="150"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Маршруты preview объединены с editor — единый контроллер, меньше кода</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.5.0</span><span class="changelog-meta__date">11-02-2026</span></span><h2 id="v950-11-02-2026---молниеносная-загрузка-lazy-split-шаблонов-скелетоны-prefetch-и-плавные-переходы">Молниеносная загрузка: lazy-split шаблонов, скелетоны, prefetch и плавные переходы</h2></summary> <div class="changelog-section__body"> <p><strong>Производительность — радикальное ускорение загрузки:</strong></p> <ul> <li data-num="83"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Lazy-split шаблонов: ~126 KB шаблонов вынесены из критического бандла в 5 ленивых чанков (editor, expo, podcasts, private, misc) — главная страница загружается ощутимо быстрее</li> <li data-num="84"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Lazy-load сервисов: captionEditorService, versioningService, quoteCardExporterService, podcastsInitialData загружаются только на страницах, где они нужны</li> <li data-num="85"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Prefetch редактора при наведении на ссылку «Редактировать» — к моменту клика чанк уже в кеше, открытие мгновенное</li> <li data-num="86"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Prefetch editor.css для залогиненных пользователей — CSS загружается в фоне до перехода в редактор</li> <li data-num="87"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Expo: данные поста загружаются в route resolve — изображения начинают рендериться сразу, без ожидания контроллера</li> <li data-num="88"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Expo галерея: оптимизация fetchpriority для карусели — первое изображение high, остальные auto (не low)</li> <li data-num="89"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Expo миниатюры: уменьшены с 200px до 150px, добавлены width/height для стабильного layout</li> <li data-num="90"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Deduplication import() для collab-модулей — параллельные вызовы не порождают дублирующих сетевых запросов</li> <li data-num="91"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> loadEditorScripts.js переведён на defer — не блокирует парсинг HTML</li> </ul> <p><strong>UX — плавность и обратная связь:</strong></p> <ul> <li data-num="92"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Route loading bar — тонкая полоска прогресса при переходах между lazy-страницами (как GitHub/YouTube)</li> <li data-num="93"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Editor skeleton — при открытии редактора вместо белого экрана показывается скелетон с shimmer-анимацией, точно повторяющий layout реального редактора</li> <li data-num="94"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Плавный fade-in для всех изображений: expo, статьи, галереи, обложки — через <code>ng-image-onload</code> + <code>is-loaded</code>/<code>is-failsafe</code></li> <li data-num="95"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Lazy-render reveal — секции, загруженные по IntersectionObserver, плавно появл
1яются с анимацией</li> <li data-num="96"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Защита мета-полей в редакторе — beforeunload предупреждает о несохранённых изменениях заголовка, тегов, категории, обложки и описания</li> <li data-num="97"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Offline-детекция при ошибке загрузки роута — вместо бесконечных перезагрузок показывается кнопка «Попробовать снова»</li> <li data-num="98"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Подкасты в header — dropdown с прямыми ссылками на все подкасты (Мир в историях, Nota Bene, Учи матчасть, Публичные призывы, Нерусские земли)</li> <li data-num="99"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Header dropdown: анимация fade-in, border-left акцент, улучшенная типографика пунктов меню</li> <li data-num="100"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> TOC-панель: плавное скрытие/появление через opacity + visibility (вместо резкого display:none)</li> <li data-num="101"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> IntersectionObserver: rootMargin увеличен до 400px — контент начинает грузиться раньше при прокрутке</li> </ul> <p><strong>Стабильность и надёжность:</strong></p> <ul> <li data-num="102"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Unified header dropdown — убрано дублирование по login-state (было 2 одинаковых меню)</li> <li data-num="103"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> InviteCollaborators: ng-if вместо ng-class предотвращает FOUC до загрузки editor.css</li> <li data-num="104"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Asteroid: подавление ожидаемой ошибки «No login token» при первом подключении</li> <li data-num="105"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Route error recovery: MAX_RELOADS снижен до 2, после чего — retry-кнопка (не бесконечный цикл)</li> <li data-num="106"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> ExpoItemCtrl: robust error guard для postData (queryOne resolves с error-объектом, не reject&#39;ит)</li> <li data-num="107"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span>
1 after-post: ng-show вместо ng-if — гарантирует инициализацию при edge cases с ng-include onload</li> <li data-num="108"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> lazyRouteLoading сбрасывается в $destroy — предотвращает зависший loading bar</li> <li data-num="109"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Editor skeleton корректно скрывает footer overlay и site header при загрузке</li> <li data-num="110"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> hideSiteHeader автоматически восстанавливается при ошибке загрузки роута редактора</li> </ul> <p><strong>Accessibility:</strong></p> <ul> <li data-num="111"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> prefers-reduced-motion: все новые анимации (fade-in, shimmer, loading bar, lazy-reveal) отключаются для пользователей, предпочитающих минимальную анимацию</li> <li data-num="112"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> Skeleton: aria-label, aria-busy, role=&quot;status&quot; для screen readers</li> <li data-num="113"><span class="changelog-tag changelog-tag--A11Y" data-tag-alias="A11Y">A11Y</span> Expo изображения: placeholder background-color (#f6f6f6) вместо пустоты при загрузке</li> </ul> <p><strong>Рефакторинг:</strong></p> <ul> <li data-num="114"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> compile-styles.js: DRY — общий compileEntry() и cleanCssOptions для всех CSS entry points</li> <li data-num="115"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> generate-templates.js: --split режим с CRITICAL_OVERRIDES для кросс-зависимых шаблонов</li> <li data-num="116"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> routes.js: showRouteMessage() — единая функция для всех error/retry сообщений</li> <li data-num="117"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> podcastsInitialData: <code>angular.module(&#39;discours.constants&#39;)</code> вместо глобального <code>app</code></li> <li data-num="118">
1[CONFIG] .gitattributes: eol=lf для всех source-файлов (предотвращает CRLF-предупреждения на Windows)</li> <li data-num="119">[CONFIG] .gitignore: templates-<em>.js, analyze-</em>.js</li> </ul> <p><strong>Backend:</strong></p> <ul> <li data-num="120"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Hidden-категории (напр. «Редактируем») скрыты для всех пользователей, включая авторизованных</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.4.0</span><span class="changelog-meta__date">10-02-2026</span></span><h2 id="v940-10-02-2026---html-редактор-с-codemirror-тема-monokai-умное-форматирование-и-горячие-клавиши">HTML-редактор с CodeMirror: тема Monokai, умное форматирование и горячие клавиши</h2></summary> <div class="changelog-section__body"> <p><strong>Новый HTML-редактор в стиле TextAngular:</strong></p> <ul> <li data-num="72"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> CodeMirror с темой Monokai — тёмная тема с полной подсветкой синтаксиса HTML/CSS/JS</li> <li data-num="73"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Горячая клавиша Ctrl+Shift+H — открытие HTML-редактора из л
1юбого места (работает даже без фокуса в редакторе)</li> <li data-num="74"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Автоформатирование при открытии — HTML сразу форматируется для читаемости и корректной подсветки</li> <li data-num="75"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Умное форматирование — каждый блок на отдельной строке, но вложенный контент компактно:<ul> <li data-num="76"><code>&lt;blockquote&gt;&lt;p&gt;Цитата&lt;/p&gt;&lt;/blockquote&gt;</code> — на одной строке</li> <li data-num="77"><code>&lt;h2&gt;Заголовок&lt;br&gt;&lt;/h2&gt;</code> — без лишних переносов</li> </ul> </li> <li data-num="78"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Диалог подтверждения &quot;Сохранить изменения?&quot; — при закрытии с несохранёнными правками (кнопки Сбросить/Сохранить)</li> <li data-num="79"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ctrl+S / Cmd+S — сохранение прямо из CodeMirror</li> </ul> <p><strong>UX-улучшения:</strong></p> <ul> <li data-num="80"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Компактный размер окна (800px × 90vh) — не перекрывает весь экран</li> <li data-num="81"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Кнопки &quot;Форматировать&quot; и &quot;Проверить&quot; с центрированным статусом</li> <li data-num="82"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Escape закрывает редактор (с подтверждением при изменениях)</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.3.0</span><span class="changelog-meta__date">10-02-2026</span></span><h2 id="v930-10-02-2026---embeddings--suggestion-mode-полная-поддержка-стилей-медиа-и-блочного-форматирования">Embeddings &amp; Suggestion Mode: полная поддержка стилей, медиа и блочного форматирования</h2></summary> <div class="changelog-section__body"> <p><strong>Режим комментирования / правок (Suggestion Mode) — полный функционал:</strong></p> <ul> <li data-num="46"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Node-level suggestions — отслеживание добавления/удаления изображений, видео, эмбедов и сносок</li> <li data-num="47"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Block format suggestions — отслеживание изменений заголовков (H2/H3), выравнивания, списков, подвёрсток и цитат</li> <li data-num="48"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Paragraph break tracking — отслеживание создания новых абзацев через Enter (zero-width space маркер)</li> <li data-num="49"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Accept All / Reject All в batch mode — единая транзакция для всех правок, корректная работа с позициями</li> <li data-num="50"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Paste handling в suggestion mode — вставка с выделением корректно помечает удалённое и добавленное</li> <li data-num="51"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Debug logging — <code>window.DISCOURS_DEBUG = true</code> включает детальные логи для отладки suggestion commands</li> <li data-num="52"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Визуальные индикаторы node suggestions — outline и badge &quot;Добавление&quot;/&quot;Удаление&quot; на изображениях/эмбедах</li> <li data-num="53"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Sidebar UI для format/node suggestions — новые типы правок с цветовой схемой (жёлтый/cyan/pink)</li> <li data-num="54"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Piktochart embed support — автоматическое определение и корректный рендеринг высоких инфографик</li> <li data-num="55"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> postMessage resize listener — динамическое изменение высоты iframe для embed-провайдеров</li> <li data-num="56"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Mod+A явный handler — гарантированная работа Cmd+A на Mac независимо от плагинов</li> </ul> <p><strong>Регистрация и UX:</strong></p> <ul> <li data-num="57"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Улучшенный flow регистрации — прямой вход через токен из ответа (без race condition)</li> <li data-num="58"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Редирект нового пользователя на страницу профиля для настройки</li> <li data-num="59"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Email verification toast — напоминание о подтверждении email (15 сек, без раздражения)</li> </ul> <p><strong>Исправления и улучшения:</strong></p> <ul> <li data-num="60"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> ProseMirror infinite loop — crawlableLinks больше не конфликтует с MutationObserver редактора</li> <li data-num="61"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Embed dimension repair — автоматическое восстановление потерянных размеров iframe из Y.js док
1умента</li> <li data-num="62"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Keyboard shortcuts в suggestion mode — Mod+B/I/U/K корректно работают через smartToggleFormat</li> <li data-num="63"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Typography shortcut заблокирован в suggestion mode — предотвращает потерю tracking данных</li> <li data-num="64"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> ZWS cleanup в stripTiptapMarksFromHtml — консервативное удаление маркеров без повреждения контента</li> </ul> <p><strong>Рефакторинг и стабильность:</strong></p> <ul> <li data-num="65"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> revertBlockFormat разбит на helper-функции для лучшей отладки</li> <li data-num="66"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> repairBrokenEmbedDimensions вынесен в отдельную функцию для тестируемости</li> <li data-num="67"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Memory leak prevention — явное обнуление DOM-ссылок в destroy() для DiscoursImage и SocialEmbed</li> <li data-num="68"><span class="changelog-tag changelog-tag--PERF" data-tag-alias="PERF">PERF</span> Single traversal в acceptAll/rejectAll — O(n) вместо O(n²) для документов с большим количеством правок</li> </ul> <p><strong>Стили:</strong></p> <ul> <li data-num="69"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Стили для node-level suggestions (outline, opacity, badges)</li> <li data-num="70"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Стили для format suggestions в sidebar (жёлтая цветовая схема)</li> <li data-num="71"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Gradient hint для scrollable tall embeds (Piktochart)</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.2.0</span><span class="changelog-meta__date">10-02-2026</span></span><h2 id="v920-10-02-2026---коллаборативный-редактор-полный-комплект-стилей-разделение-кода--новые-настройки-профиля">Коллаборативный редактор: полный комплект стилей, разделение кода + новые настройки профиля</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="37"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Страница настроек (Settings) — стили для профиля с боковой навигацией и табами; сетка выровнена с шапкой (sidebar = колонка логотипа, контент = колонка навигации)</li> <li data-num="38"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Единый список разрешённых доменов для эмбедов (allowed-embed-domains.js) — используется html-cleaner при вставке/загрузке и social-embed при вставке; добавление домена в одном месте</li> <li data-num="39"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Общие стили ссылок «подчёркивание + инверсия при ховере» (link-invert-hover-styles.js) — единый вид в редакторе сносок, подписей и caption-редакторе (TipTap и TextAngular)</li> <li data-num="40"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Единообразное отображение ссылок в модалках сносок и подписей к медиа — один источник стилей, без расхождений между редакторами</li> <li data-num="41"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Улучшения стилей редактора: отступы H2/H3, подверстки, абзацы, подписи к изображениям (partials: footnote-editor, tiptap-dialogs, discours-form, open-post, column, tooltip)</li> <li data-num="42"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Разделение кода коллаборативного редактора (collab-init, collab-autosave, comments, preview, publish, sidebar, toolbar, versioning) — проще поддержка и тестирование</li> <li data-num="43"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Расширения TipTap: обновления discours-image, footnote-tooltip, heading-with-id, html-cleaner, link-bubble-menu, link-inline-editor, rich-caption-editor, rich-footnote-editor, social-embed, toc-block, toc-generator, video-insert-dialog, discours-keyboard, keyboard-shortcuts-help</li> <li data-num="44"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> tiptapEditorDirective, editorHeader, captionEditorService, footnoteService, versioningService — согласование с новой структурой и константами</li> <li data-num="45">
1[CONFIG] Роуты, шаблоны страниц редактирования и автора, header — правки под коллаборативный режим и TipTap</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.1.0</span><span class="changelog-meta__date">09-02-2026</span></span><h2 id="v910-09-02-2026---оглавление-справка-по-горячим-клавишам-новые-шорткаты">Оглавление, справка по горячим клавишам, новые шорткаты</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="27"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Панель «Оглавление» (TOC) — навигация по заголовкам H2/H3 слева от редактора с подсветкой текущего раздела и прогресс-баром чтения; помогает авторам ориентироваться в длинных текстах и быстро переходить между разделами</li> <li data-num="28"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кнопка «?» со справкой по горячим клавишам (попап) — все шорткаты сгруппированы по категориям; снижает порог входа для новых авторов</li> <li data-num="29"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Жёлтая точка-индикатор на кнопке «?» при первом визите — пропадает после открытия справки</li> <li data-num="30"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ctrl/Cmd + / — показать/скрыть справку по горячим клавишам</li> <li data-num="31"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ctrl/Cmd + Alt + / — открыть/закрыть панель комментариев и правок</li> <li data-num="32"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ctrl/Cmd + Alt + P — переключение в режим предпросмотра</li> <li data-num="33"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ctrl/Cmd + Alt + E — возврат в режим редактирования</li> <li data-num="34"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Legacy-кнопки «Поправить типографику» и «Почистить код» скрыты в TipTap-режиме — интерфейс стал чище</li> <li data-num="35"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> TOC-панель скрывается на мобильных экранах (&lt; 768px)</li> <li data-num="36"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Новые custom events (toggleSidebar, togglePreview, setEditingMode, toggleShortcutsHelp) в tiptapEditorDirective для связи TipTap-шорткатов с Angular-контроллерами</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.0.1</span><span class="changelog-meta__date">08-02-2026</span></span><h2 id="v901-08-02-2026---staging-среда-и-кнопка-сохранения">Staging-среда и кнопка сохранения</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="15"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Кнопка «Сохранить» в editorHeader — сохраняет черновик и создаёт версию в истории</li> <li data-num="16"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Ctrl+S / Cmd+S — сохранение черновика и создание версии по горячей клавише</li> <li data-num="17"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Визуальная обратная связь: анимация галочки на кнопке после успешного сохранения</li> <li data-num="18"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Индикатор «Сохранено N мин назад» рядом с кнопкой сохранения</li> <li data-num="19"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Cloudflare Tunnel staging: try.discours.io проксирует localhost через туннель</li> <li data-num="20"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Защита от двойного клика при сохранении версии</li> <li data-num="21"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Корректный вывод ошибок сохранения (err.message вместо [object Object])</li> <li data-num="22"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> transition: all → конкретные свойства (производительность)</li> <li data-num="23"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Добавлены aria-label, aria-busy, focus-visible для доступности</li> <li data-num="24"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> isLocal → usesProxy — семантически корректное имя переменной</li> <li data-num="25">[CONFIG] API_SERVER в development/development_local переведён на относительный /api/v2</li> <li data-num="26">[CONFIG] webpack allowedHosts: добавлен try.discours.io</li> </ul> </div> </details> <details class="changelog-section"> <summary class="changelog-summary"><span class="changelog-meta"><span class="changelog-meta__version">v9.0.0</span><span class="changelog-meta__date">07-02-2026</span></span><h2 id="v900-07-02-2026---новый-редактор-с-коллаборацией">Новый редактор с коллаборацией</h2></summary> <div class="changelog-section__body"> <ul> <li data-num="1"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> TipTap Editor - миграция с TextAngular на современный редактор TipTap</li> <li data-num="2"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Пресеты редактора: full, simplified, album, comments для разных контекстов</li> <li data-num="3"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Система версионирования документов (versioningService)</li> <li data-num="4"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Feature flags сервис для управления функциями</li> <li data-num="5"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Social embed расширение с поддержкой YouTube, Vimeo, Twitter, Telegram, VK и др.</li> <li data-num="6"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Video insert dialog для вставки видео по URL</li> <li data-num="7"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> tiptapSimpleEditorDirective для комментариев и личных сообщений</li> <li data-num="8"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Expo preview контроллер для предпросмотра экспо-материалов</li> <li data-num="9"><span class="changelog-tag changelog-tag--NEW" data-tag-alias="NEW">NEW</span> Подмножество FontAwesome иконок (fa-brands, fa-solid)</li> <li data-num="10"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Улучшенный тулбар редактора с группировкой кнопок</li> <li data-num="11"><span class="changelog-tag changelog-tag--UX" data-tag-alias="UX">UX</span> Стили для диалогов TipTap, истории версий</li> <li data-num="12"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Улучшения bubble menu для ссылок и комментариев</li> <li data-num="13"><span class="changelog-tag changelog-tag--FIX" data-tag-alias="FIX">FIX</span> Исправления клавиатурных сокращений в редакторе</li> <li data-num="14"><span class="changelog-tag changelog-tag--REFACTOR" data-tag-alias="REFACTOR">REFACTOR</span> Редактор литературы переведён на TipTap</li> </ul> </div> </details> </div> <div class="tw:gutter-half tw:w-full tw:md:w-3/12 tw:md:mt-content-top" ng-non-bindable> <nav class="changelog-toc"> <div class="changelog-toc__title">Версии</div> <ul class="changelog-toc__list"> <li><a href="#v9177-28-09-2026--политика-конфиденциальности-своя-страница-и-понятный-раздел-о-входе-через-другие-сервисы"><span class="changelog-toc__version">v9.177</span><span class="changelog-toc__date">28-09-2026</span><span class="changelog-toc__topic" title="Политика конфиденциальности: своя страница и понятный раздел о входе через другие сервисы">Политика конфиденциальности: своя страница и понятный раздел о входе через другие сервисы</span></a></li> <li><a href="#v9176-28-09-2026--подпись-автора-без-имени-адрес-его-страницы"><span class="changelog-toc__version">v9.176</span><span class="changelog-toc__date">28-09-2026</span><span class="changelog-toc__topic" title="Подпись автора без имени: адрес его страницы">Подпись автора без имени: адрес его страницы</span></a></li> <li><a href="#v9175-27-09-2026--правки-рецензента-остаются-в-редакторе-после-публикации"><span class="changelog-toc__version">v9.175</span><span class="changelog-toc__date">27-09-2026</span><span class="changelog-toc__topic" title="Правки рецензента остаются в редакторе после публикации">Правки рецензента остаются в редакторе после публикации<
1/span></a></li> <li><a href="#v9174-26-09-2026--правки-рецензента-новый-абзац-и-история-версий"><span class="changelog-toc__version">v9.174</span><span class="changelog-toc__date">26-09-2026</span><span class="changelog-toc__topic" title="Правки рецензента: новый абзац и история версий">Правки рецензента: новый абзац и история версий</span></a></li> <li><a href="#v9173-14-09-2026--страница-подтверждения-почты-перестала-говорить-одно-и-то-же-на-все-случаи"><span class="changelog-toc__version">v9.173</span><span class="changelog-toc__date">14-09-2026</span><span class="changelog-toc__topic" title="Страница подтверждения почты перестала говорить одно и то же на все случаи">Страница подтверждения почты перестала говорить одно и то же на все случаи</span></a></li> <li><a href="#v9172-11-09-2026--иконки-пропавшие-из-набора-вернулись-на-кнопки"><span class="changelog-toc__version">v9.172</span><span class="changelog-toc__date">11-09-2026</span><span class="changelog-toc__topic" title="Иконки, пропавшие из набора, вернулись на кнопки">Иконки, пропавшие из набора, вернулись на кнопки</span></a></li> <li><a href="#v9171-06-09-2026--ответы-сайта-несут-политику-использования-а-llmstxt-ведёт-агентов-к-тексту"><span class="changelog-toc__version">v9.171</span><span class="changelog-toc__date">06-09-2026</span><span class="changelog-toc__topic" title="Ответы сайта несут политику использования, а llms.txt ведёт агентов к тексту">Ответы сайта несут политику использования, а llms.txt ведёт агентов к тексту</span></a></li> <li><a href="#v9170-05-09-2026--веб-версия-письма-открывается-по-любой-из-своих-ссылок"><span class="changelog-toc__version">v9.170</span><span class="changelog-toc__date">05-09-2026</span><span class="changelog-toc__topic" title="Веб-версия письма открывается по любой из своих ссылок">Веб-версия письма открывается по любой из своих ссылок</span></a></li> <li><a href="#v9169-04-09-2026--материал-из-уведомления-открывается-с-первого-раза"><span class="changelog-toc__version">v9.169</span><span class="changelog-toc__date">04-09-2026</span><span class="changelog-toc__topic" title="Материал из уведомления открывается с первого раза">Материал из уведомления открывается с первого раза</span></a></li> <li><a href="#v9168-03-09-2026--предложение-войти-больше-не-исчезает-само"><span class="changelog-toc__version">v9.168</span><span class="changelog-toc__date">03-09-2026</span><span class="changelog-toc__topic" title="Предложение войти больше не исчезает само">Предложение войти больше не исчезает само</span></a></li> <li><a href="#v9167-03-09-2026--название-в-уведомлении-открывает-ровно-то-же-что-и-вся-строка"><span class="changelog-toc__version">v9.167</span><span class="changelog-toc__date">03-09-2026</span><span class="changelog-toc__topic" title="Название в уведомлении открывает ровно то же, что и вся строка">Название в уведомлении открывает ровно то же, что и вся строка</span></a></li> <li><a href="#v9166-03-09-2026--название-материала-в-уведомлениях-ведёт-куда-надо"><span class="changelog-toc__version">v9.166</span><span class="changelog-toc__date">03-09-2026</span><span class="changelog-toc__topic" title="Название материала в уведомлениях ведёт куда надо">Название материала в уведомлениях ведёт куда надо</span></a></li> <li><a href="#v9165-03-09-2026--редактор-перестал-путать-истёкшую-сессию-с-закрытым-доступом"><span class="changelog-toc__version">v9.165</span><span class="changelog-toc__date">03-09-2026</span><span class="changelog-toc__topic" title="Редактор перестал путать истёкшую сессию с закрытым доступом">Редактор перестал путать истёкшую сессию с закрытым доступом</span></a></li> <li><a href="#v9164-27-08-2026--комментарии-и-лента-материалов-больше-не-исчезают"><span class="changelog-toc__version">v9.164</span><span class="changelog-toc__date">27-08-2026</span><span class="changelog-toc__topic" title="Комментарии и лента материалов больше не исчезают">Комментарии и лента материалов больше не исчезают</span></a></li> <li><a href="#v9163-26-08-2026--страница-автора-и-видео-показываем-ровно-то-что-есть"><span class="changelog-toc__version">v9.163</span><span class="changelog-toc__date">26-08-2026</span><span class="changelog-toc__topic" title="Страница автора и видео: показываем ровно то, что есть">Страница автора и видео: показываем ровно то, что есть</span></a></li> <li><a href="#v9162-25-08-2026--обсуждения-правок-в-редакторе-не-запираются-намертво"><span class="changelog-toc__version">v9.162</span><span class="changelog-toc__date">25-08-2026</span><span class="changelog-toc__topic" title="Обсуждения правок в редакторе не запираются намертво">Обсуждения правок в редакторе не запираются намертво</span></a></li> <li><a href="#v9161-22-08-2026--музыка-часовые-записи-форматы-диктофона-и-понятные-отказы"><span class="changelog-toc__version">v9.161</span><span class="changelog-toc__date">22-08-2026</span><span class="changelog-toc__topic" title="Музыка: часовые записи, форматы диктофона и понятные отказы">Музыка: часовые записи, форматы диктофона и понятные отказы</span></a></li> <li><a href="#v9160-20-08-2026--ссылка-из-письма-поддержки-открывает-сам-разговор"><span class="changelog-toc__version">v9.160</span><span class="changelog-toc__date">20-08-2026</span><span class="changelog-toc__topic" title="Ссылка из письма поддержки открывает сам разговор">Ссылка из письма поддержки открывает сам разговор</span></a></li> <li><a href="#v9159-19-08-2026--портал-доступа-черновики-и-скорость-повторных-заходов"><span class="changelog-toc__version">v9.159</span><span class="changelog-toc__date">19-08-2026</span><span class="changelog-toc__topic" title="Портал доступа, черновики и скорость повторных заходов">Портал доступа, черновики и скорость повторных заходов</span></a></li> <li><a href="#v91581-18-08-2026--черновики-музыка-и-понятные-отказы-редактора"><span class="changelog-toc__version">v9.158.1</span><span class="changelog-toc__date">18-08-2026</span><span class="changelog-toc__topic" title="Черновики, музыка и понятные отказы редактора">Черновики, музыка и понятные отказы редактора</span></a></li> <li><a href="#v9152-18-08-2026--журнал-подписан-в-поиске-своим-именем"><span class="changelog-toc__version">v9.152</span><span class="changelog-toc__date">18-08-2026</span><span class="changelog-toc__topic" title="Журнал подписан в поиске своим именем">Журнал подписан в поиске своим именем</span></a></li> <li>
1<a href="#v9158-18-08-2026--измерения-скорости-стали-пригодны-для-работы"><span class="changelog-toc__version">v9.158</span><span class="changelog-toc__date">18-08-2026</span><span class="changelog-toc__topic" title="Измерения скорости стали пригодны для работы">Измерения скорости стали пригодны для работы</span></a></li> <li><a href="#v9157-18-08-2026--журнал-подписан-в-поиске-своим-именем"><span class="changelog-toc__version">v9.157</span><span class="changelog-toc__date">18-08-2026</span><span class="changelog-toc__topic" title="Журнал подписан в поиске своим именем">Журнал подписан в поиске своим именем</span></a></li> <li><a href="#v9156-15-08-2026--страницы-журнала-снова-открываются-из-кэша"><span class="changelog-toc__version">v9.156</span><span class="changelog-toc__date">15-08-2026</span><span class="changelog-toc__topic" title="Страницы журнала снова открываются из кэша">Страницы журнала снова открываются из кэша</span></a></li> <li><a href="#v9155-13-08-2026--материал-не-подменяется-черновиком-карточки-без-обложки-ожили"><span class="changelog-toc__version">v9.155</span><span class="changelog-toc__date">13-08-2026</span><span class="changelog-toc__topic" title="Материал не подменяется черновиком, карточки без обложки ожили">Материал не подменяется черновиком, карточки без обложки ожили</span></a></li> <li><a href="#v9154-09-08-2026--ленты-и-обложки-перестали-тянуть-лишние-мегабайты"><span class="changelog-toc__version">v9.154</span><span class="changelog-toc__date">09-08-2026</span><span class="changelog-toc__topic" title="Ленты и обложки перестали тянуть лишние мегабайты">Ленты и обложки перестали тянуть лишние мегабайты</span></a></li> <li><a href="#v9153-07-08-2026--анимированные-обложки-открываются-пределы-загрузки-понятны-заранее"><span class="changelog-toc__version">v9.153</span><span class="changelog-toc__date">07-08-2026</span><span class="changelog-toc__topic" title="Анимированные обложки открываются, пределы загрузки понятны заранее">Анимированные обложки открываются, пределы загрузки понятны заранее</span></a></li> <li><a href="#v9152-05-08-2026--настройка-«уменьшить-движение»-наконец-работает"><span class="changelog-toc__version">v9.152</span><span class="changelog-toc__date">05-08-2026</span><span class="changelog-toc__topic" title="Настройка «уменьшить движение» наконец работает">Настройка «уменьшить движение» наконец работает</span></a></li> <li><a href="#v9151-26-07-2026--английская-версия-описания-для-поиска-типографика-и-починенные-склонения"><span class="changelog-toc__version">v9.151</span><span class="changelog-toc__date">26-07-2026</span><span class="changelog-toc__topic" title="Английская версия: описания для поиска, типографика и починенные склонения">Английская версия: описания для поиска, типографика и починенные склонения</span></a></li> <li><a href="#v9150-14-07-2026--скорость-журнал-открывается-быстрее-и-расходует-меньше-трафика"><span class="changelog-toc__version">v9.150</span><span class="changelog-toc__date">14-07-2026</span><span class="changelog-toc__topic" title="Скорость: журнал открывается быстрее и расходует меньше трафика">Скорость: журнал открывается быстрее и расходует меньше трафика</span></a></li> <li><a href="#v9149-11-07-2026--совместный-редактор-у-правок-появились-авторы-фильтры-стали-компактнее"><span class="changelog-toc__version">v9.149</span><span class="changelog-toc__date">11-07-2026</span><span class="changelog-toc__topic" title="Совместный редактор: у правок появились авторы, фильтры стали компактнее">Совместный редактор: у правок появились авторы, фильтры стали компактнее</span></a></li> <li><a href="#v9148-07-07-2026--чёткие-картинки-на-любых-экранах-надёжная-доставка-медиа-и-анонимная-поддержка"><span class="changelog-toc__version">v9.148</span><span class="changelog-toc__date">07-07-2026</span><span class="changelog-toc__topic" title="Чёткие картинки на любых экранах, надёжная доставка медиа и анонимная поддержка">Чёткие картинки на любых экранах, надёжная доставка медиа и анонимная поддержка</span></a></li> <li><a href="#v9147-26-06-2026--карты-смыслов-совместное-редактирование-в-реальном-времени-и-полный-английский-интерфейс"><span class="changelog-toc__version">v9.147</span><span class="changelog-toc__date">26-06-2026</span><span class="changelog-toc__topic" title="Карты смыслов: совместное редактирование в реальном времени. И полный английский интерфейс">Карты смыслов: совместное редактирование в реальном времени. И полный английский интерфейс</span></a></li> <li><a href="#v9146-04-06-2026--обновлённая-поддержка-ин
1струменты-статей-в-экспо-редакторах-и-дружелюбные-экраны-сбоев"><span class="changelog-toc__version">v9.146</span><span class="changelog-toc__date">04-06-2026</span><span class="changelog-toc__topic" title="Обновлённая поддержка, инструменты статей в экспо-редакторах и дружелюбные экраны сбоев">Обновлённая поддержка, инструменты статей в экспо-редакторах и дружелюбные экраны сбоев</span></a></li> <li><a href="#v9145-17-05-2026--дискурс-на-английском-полностью-переведены-все-тексты-интерфейса"><span class="changelog-toc__version">v9.145</span><span class="changelog-toc__date">17-05-2026</span><span class="changelog-toc__topic" title="Дискурс на английском: полностью переведены все тексты интерфейса">Дискурс на английском: полностью переведены все тексты интерфейса</span></a></li> <li><a href="#v9144-16-05-2026--обложки-дышат-мобильный-интерфейс-плотнее-карта-сайта-до-края-журнала"><span class="changelog-toc__version">v9.144</span><span class="changelog-toc__date">16-05-2026</span><span class="changelog-toc__topic" title="Обложки дышат, мобильный интерфейс плотнее, карта сайта до края журнала">Обложки дышат, мобильный интерфейс плотнее, карта сайта до края журнала</span></a></li> <li><a href="#v9143-02-05-2026--манифест-страница-голос-дискурса-против-цензуры"><span class="changelog-toc__version">v9.143</span><span class="changelog-toc__date">02-05-2026</span><span class="changelog-toc__topic" title="Манифест-страница: голос Дискурса против цензуры">Манифест-страница: голос Дискурса против цензуры</span></a></li> <li><a href="#v9142-29-04-2026--ссылки-которые-не-«протухают»-новые-видео-и-большой-seo-апдейт"><span class="changelog-toc__version">v9.142</span><span class="changelog-toc__date">29-04-2026</span><span class="changelog-toc__topic" title="Ссылки, которые не «протухают», новые видео и большой SEO-апдейт">Ссылки, которые не «протухают», новые видео и большой SEO-апдейт</span></a></li> <li><a href="#v9141-27-04-2026--первая-публикация-всё-происходит-само"><span class="changelog-toc__version">v9.141</span><span class="changelog-toc__date">27-04-2026</span><span class="changelog-toc__topic" title="Первая публикация: всё происходит само">Первая публикация: всё происходит само</span></a></li> <li><a href="#v9140-25-04-2026--невидимое-усиление-фундамент-в-поиске-и-устойчивость-к-сбоям"><span class="changelog-toc__version">v9.140</span><span class="changelog-toc__date">25-04-2026</span><span class="changelog-toc__topic" title="Невидимое усиление: фундамент в поиске и устойчивость к сбоям">Невидимое усиление: фундамент в поиске и устойчивость к сбоям</span></a></li> <li><a href="#v9139-23-04-2026--цензурный-щит-точнее-только-государственные-ведомства"><span class="changelog-toc__version">v9.139</span><span class="changelog-toc__date">23-04-2026</span><span class="changelog-toc__topic" title="Цензурный щит точнее: только государственные ведомства">Цензурный щит точнее: только государственные ведомства</span></a></li> <li><a href="#v9138-22-04-2026--связка-с-telegram-в-один-тап-и-тёплый-редактор"><span class="changelog-toc__version">v9.138</span><span class="changelog-toc__date">22-04-2026</span><span class="changelog-toc__topic" title="Связка с Telegram в один тап и тёплый редактор">Связка с Telegram в один тап и тёплый редактор</span></a></li> <li><a href="#v9137-19-04-2026--донаты-без-загадок-код-без-eol"><span class="changelog-toc__version">v9.137</span><span class="changelog-toc__date">19-04-2026</span><span class="changelog-toc__topic" title="Донаты без загадок, код без EOL">Донаты без загадок, код без EOL</span></a></li> <li><a href="#v9136-17-04-2026--слайдшоу-в-редакторе-подписи-не-теряются-и-одна--на-всех"><span class="changelog-toc__version">v9.136</span><span class="changelog-toc__date">17-04-2026</span><span class="changelog-toc__topic" title="Слайдшоу в редакторе: подписи не теряются и одна — на всех">Слайдшоу в редакторе: подписи не теряются и одна — на всех</span></a></li> <li><a href="#v9135-17-04-2026--почта-дискурса-на-броне-автоочистка-шифрование-защита-от-подделок"><span class="changelog-toc__version">v9.135</span><span class="changelog-toc__date">17-04-2026</span><span class="changelog-toc__topic" title="Почта Дискурса на броне: автоочистка, шифрование, защита от подделок">Почта Дискурса на броне: автоочистка, шифрование, защита от подделок</span></a></li> <li><a href="#v9134-16-04-2026--открытый-код-для-соучастия-гайд-шаблоны-манифест-разработки"><span class="changelog-toc__version">
1v9.134</span><span class="changelog-toc__date">16-04-2026</span><span class="changelog-toc__topic" title="Открытый код для соучастия: гайд, шаблоны, манифест разработки">Открытый код для соучастия: гайд, шаблоны, манифест разработки</span></a></li> <li><a href="#v9133-16-04-2026--переезд-на-github-быстрее-надёжнее-удобнее-для-соучастия"><span class="changelog-toc__version">v9.133</span><span class="changelog-toc__date">16-04-2026</span><span class="changelog-toc__topic" title="Переезд на GitHub: быстрее, надёжнее, удобнее для соучастия">Переезд на GitHub: быстрее, надёжнее, удобнее для соучастия</span></a></li> <li><a href="#v9132-15-04-2026--цензурный-щит-сайт-невидим-для-госцензоров"><span class="changelog-toc__version">v9.132</span><span class="changelog-toc__date">15-04-2026</span><span class="changelog-toc__topic" title="Цензурный щит: сайт невидим для госцензоров">Цензурный щит: сайт невидим для госцензоров</span></a></li> <li><a href="#v9131-14-04-2026--броня-для-платформы-защита-от-атак-ботов-и-блокировок"><span class="changelog-toc__version">v9.131</span><span class="changelog-toc__date">14-04-2026</span><span class="changelog-toc__topic" title="Броня для платформы: защита от атак, ботов и блокировок">Броня для платформы: защита от атак, ботов и блокировок</span></a></li> <li><a href="#v9130-14-04-2026--новая-«умная-дверь»-для-обхода-блокировок-и-обновление-старого-зеркала"><span class="changelog-toc__version">v9.130</span><span class="changelog-toc__date">14-04-2026</span><span class="changelog-toc__topic" title="Новая «умная дверь» для обхода блокировок и обновление старого зеркала">Новая «умная дверь» для обхода блокировок и обновление старого зеркала</span></a></li> <li><a href="#v9129-14-04-2026--английский-интерфейс-заглушка-с-характером-редактор-без-горизонтального-скролла"><span class="changelog-toc__version">v9.129</span><span class="changelog-toc__date">14-04-2026</span><span class="changelog-toc__topic" title="Английский интерфейс, заглушка с характером, редактор без горизонтального скролла">Английский интерфейс, заглушка с характером, редактор без горизонтального скролла</span></a></li> <li><a href="#v9128-05-04-2026--зеркала-работают-сообщения-удобнее-теги-с-rss"><span class="changelog-toc__version">v9.128</span><span class="changelog-toc__date">05-04-2026</span><span class="changelog-toc__topic" title="Зеркала работают, сообщения удобнее, теги с RSS">Зеркала работают, сообщения удобнее, теги с RSS</span></a></li> <li><a href="#v9127-05-04-2026--уведомления-чище-компактнее-живее"><span class="changelog-toc__version">v9.127</span><span class="changelog-toc__date">05-04-2026</span><span class="changelog-toc__topic" title="Уведомления: чище, компактнее, живее">Уведомления: чище, компактнее, живее</span></a></li> <li><a href="#v9126-04-04-2026--сообщения-удобное-создание-групповых-чатов"><span class="changelog-toc__version">v9.126</span><span class="changelog-toc__date">04-04-2026</span><span class="changelog-toc__topic" title="Сообщения: удобное создание групповых чатов">Сообщения: удобное создание групповых чатов</span></a></li> <li><a href="#v91250-03-04-2026--собственные-рассылки-и-почтовая-инфраструктура"><span class="changelog-toc__version">v9.125.0</span><span class="changelog-toc__date">03-04-2026</span><span class="changelog-toc__topic" title="Собственные рассылки и почтовая инфраструктура">Собственные рассылки и почтовая инфраструктура</span></a></li> <li><a href="#v91240-01-04-2026--discours-connect-импорт-из-google-docs-и-множество-других-улучшений"><span class="changelog-toc__version">v9.124.0</span><span class="changelog-toc__date">01-04-2026</span><span class="changelog-toc__topic" title="Discours Connect, импорт из Google Docs и множество других улучшений">Discours Connect, импорт из Google Docs и множество других улучшений</span></a></li> <li><a href="#v91230-25-03-2026--спринт-по-фиксам-ускорение-загрузки-изображений-ясные-ошибки-и-улучшение-ux"><span class="changelog-toc__version">v9.123.0</span><span class="changelog-toc__date">25-03-2026</span><span class="changelog-toc__topic" title="Спринт по фиксам: ускорение загрузки изображений, ясные ошибки и улучшение UX">Спринт по фиксам: ускорение загрузки изображений, ясные ошибки и улучшение UX</span></a></li> <li><a href="#v91220-24-03-2026--красивые-обложки-у-видео-и-запуск-плейров-с-первого-клика"><span class="changelog-toc__version">v9.122.0</span><span class="changelog-toc__date">24-03-2026</span><span class="changelog-toc__topic" title="Красивые обложки у видео и запуск плейров с первого клика">Красивые обложки у видео и запуск плейров с первого клика</span></a></li> <li>
1<a href="#v91210-24-03-2026--lighthouse-аудит-быстрее-доступнее-надёжнее"><span class="changelog-toc__version">v9.121.0</span><span class="changelog-toc__date">24-03-2026</span><span class="changelog-toc__topic" title="Lighthouse аудит: быстрее, доступнее, надёжнее">Lighthouse аудит: быстрее, доступнее, надёжнее</span></a></li> <li><a href="#v91200-24-03-2026--итоги-большого-перехода"><span class="changelog-toc__version">v9.120.0</span><span class="changelog-toc__date">24-03-2026</span><span class="changelog-toc__topic" title="Итоги большого перехода">Итоги большого перехода</span></a></li> <li><a href="#v91190-24-03-2026--подписки-вразнобой-донаты-с-клавиатуры-и-тишина-вместо-мерцания"><span class="changelog-toc__version">v9.119.0</span><span class="changelog-toc__date">24-03-2026</span><span class="changelog-toc__topic" title="Подписки вразнобой, донаты с клавиатуры и тишина вместо мерцания">Подписки вразнобой, донаты с клавиатуры и тишина вместо мерцания</span></a></li> <li><a href="#v91180-23-03-2026--баннеры-вернулись-sticky-sidebar-жив-анимация-при-скролле-и-чистый-vanilla-js"><span class="changelog-toc__version">v9.118.0</span><span class="changelog-toc__date">23-03-2026</span><span class="changelog-toc__topic" title="Баннеры вернулись: sticky sidebar жив, анимация при скролле и чистый vanilla JS">Баннеры вернулись: sticky sidebar жив, анимация при скролле и чистый vanilla JS</span></a></li> <li><a href="#v91170-23-03-2026--мгновенные-переходы-матовое-стекло-и-последний-jquery-платформа-дышит-свободнее"><span class="changelog-toc__version">v9.117.0</span><span class="changelog-toc__date">23-03-2026</span><span class="changelog-toc__topic" title="Мгновенные переходы, матовое стекло и последний jQuery: платформа дышит свободнее">Мгновенные переходы, матовое стекло и последний jQuery: платформа дышит свободнее</span></a></li> <li><a href="#v91160-23-03-2026--быстрее-доступнее-легче-2000-строк-svg-→-18-иконок-jquery-→-браузер-каждая-кнопка--с-клавиатуры"><span class="changelog-toc__version">v9.116.0</span><span class="changelog-toc__date">23-03-2026</span><span class="changelog-toc__topic" title="Быстрее, доступнее, легче: 2000 строк SVG → 18 иконок, jQuery → браузер, каждая кнопка — с клавиатуры">Быстрее, доступнее, легче: 2000 строк SVG → 18 иконок, jQuery → браузер, каждая кнопка — с клавиатуры</span></a></li> <li><a href="#v91150-23-03-2026--единый-язык-дизайна-токены-вместо-магических-чисел-клавиатура-вместо-мыши"><span class="changelog-toc__version">v9.115.0</span><span class="changelog-toc__date">23-03-2026</span><span class="changelog-toc__topic" title="Единый язык дизайна: токены вместо магических чисел, клавиатура вместо мыши">Единый язык дизайна: токены вместо магических чисел, клавиатура вместо мыши</span></a></li> <li><a href="#v91140-22-03-2026--от-каждого-пикселя--польза-от-каждого-байта--скорость"><span class="changelog-toc__version">v9.114.0</span><span class="changelog-toc__date">22-03-2026</span><span class="changelog-toc__topic" title="От каждого пикселя — польза, от каждого байта — скорость">От каждого пикселя — польза, от каждого байта — скорость</span></a></li> <li><a href="#v91130-22-03-2026--вход-без-нервов-валидация-по-делу-модалка-без-прокрутки-и-сквозной-аудит-доступности"><span class="changelog-toc__version">v9.113.0</span><span class="changelog-toc__date">22-03-2026</span><span class="changelog-toc__topic" title="Вход без нервов: валидация по делу, модалка без прокрутки и сквозной аудит доступности">Вход без нервов: валидация по делу, модалка без прокрутки и сквозной аудит доступности</span></a></li> <li><a href="#v91120-22-03-2026--bootstrap-прощай-собственная-дизайн-система-вместо-фреймворка-2015-года"><span class="changelog-toc__version">v9.112.0</span><span class="changelog-toc__date">22-03-2026</span><span class="changelog-toc__topic" title="Bootstrap, прощай: собственная дизайн-система вместо фреймворка 2015 года">Bootstrap, прощай: собственная дизайн-система вместо фреймворка 2015 года</span></a></li> <li><a href="#v91110-21-03-2026--связь-не-рвётся-умный-reconnect-индикатор-соединения-и-аудит-доступности"><span class="changelog-toc__version">v9.111.0</span><span class="changelog-toc__date">21-03-2026</span><span class="changelog-toc__topic" title="Связь не рвётся: умный reconnect, индикатор соединения и аудит доступности">Связь не рвётся: умный reconnect, индикатор соединения и аудит доступности</span></a></li> <li><a href="#v91100-20-03-2026--звуки-уведомлений-бейджи-и-черновики-которые-никогда-не-потеряют-ваши-тексты"><span class="changelog-toc__version">v9.110.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Звуки уведомлений, бейджи и черновики, которые никогда не потеряют ваши тексты">Звуки уведомлений, бейджи и черновики, которые никогда не потеряют ваши тексты</span></a></li> <li><a href="#v91090-20-03-2026--страж-вёрстки-playwright-следит-чтобы-ничего-не-поехало"><span class="changelog-toc__version">v9.109.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Страж вёрстки: Playwright следит, чтобы ничего не поехало">Страж вёрстки: Playwright следит, чтобы ничего не поехало</span></a></li> <li><a href="#v91080-20-03-2026--картинки-вернулись-экстренный-ремонт-thumbor"><span class="changelog-toc__version">v9.108.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Картинки вернулись: экстренный ремонт Thumbor">Картинки вернулись: экстренный ремонт Thumbor</span></a></li> <li><a href="#v91071-20-03-2026--живые-уведомления-ddp-подписки-мгновенный-отклик-и-real-time-уведомления"><span class="changelog-toc__version">v9.107.1</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Живые уведомления: DDP-подписки, мгновенный отклик и real-time уведомления">Живые уведомления: DDP-подписки, мгновенный отклик и real-time уведомления</span></a></li> <li><a href="#v91070-20-03-2026--websocket-без-компромиссов-настоящий-real-time-вместо-тихого-fallbackа"><span class="changelog-toc__version">v9.107.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="WebSocket без компромиссов: настоящий real-time вместо тихого fallback&#39;а">WebSocket без компромиссов: настоящий real-time вместо тихого fallback&#39;а</span></a></li> <li><a href="#v91060-20-03-2026--ремарки-с-обратной-связью-ответы-авторов-статусы-и-умная-фильтрация-уведомлений"><span class="changelog-toc__version">v9.106.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Ремарки с обратной связью: ответы авторов, статусы и умная фильтрация уведомлений">Ремарки с обратной связью: ответы авторов, статусы и умная фильтрация уведомлений</span></a></li> <li><a href="#v91050-20-03-2026--черновиками-можно-делиться-с-первых-слов--с-открытия-редактора"><span class="changelog-toc__version">v9.105.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Черновиками можно делиться с первых слов — с открытия редактора">Черновиками можно делиться с первых слов — с открытия редактора</span></a></li> <li><a href="#v91040-20-03-2026--прочитано-или-нет-управление-статусом-уведомлений"><span class="changelog-toc__version">v9.104.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Прочитано. Или нет: управление статусом уведомлений">Прочитано. Или нет: управление статусом уведомлений</span></a></li> <li><a href="#v91030-20-03-2026--формы-входа-надёжные-уведомления-и-мгновенные-черновики"><span class="changelog-toc__version">v9.103.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Формы входа, надёжные уведомления и мгновенные черновики">Формы входа, надёжные уведомления и мгновенные черновики</span></a></li> <li><a href="#v91020-20-03-2026--рентген-для-платформы-мониторинг-workerа-подкасты-в-поиске-лёгкий-bootstrap-и-автодеплой"><span class="changelog-toc__version">v9.102.0</span><span class="changelog-toc__date">20-03-2026</span><span class="changelog-toc__topic" title="Рентген для платформы: мониторинг Worker&#39;а, подкасты в поиске, лёгкий Bootstrap и автодеплой">Рентген для платформы: мониторинг Worker&#39;а, подкасты в поиске, лёгкий Bootstrap и автодеплой</span></a></li> <li><a href="#v91010-19-03-2026--финальный-штрих-анимации-тактильность-и-шлифовка-перед-выходом"><span class="changelog-toc__version">v9.101.0</span><span class="changelog-toc__date">19-03-2026</span><span class="changelog-toc__topic" title="Финальный штрих: анимации, тактильность и шлифовка перед выходом">Финальный штрих: анимации, тактильность и шлифовка перед выходом</span></a></li> <li><a href="#v91000-19-03-2026--уведомления-о-правках-прочтение-сообщение-sitemaps-rss-ленты-и-единые-дизайн-токены"><span class="changelog-toc__version">v9.100.0</span><span class="changelog-toc__date">19-03-2026</span><span class="changelog-toc__topic" title="Уведомления о правках, прочтение сообщение, sitemaps, rss-ленты и единые дизайн-токены">Уведомления о правках, прочтение сообщение, sitemaps, rss-ленты и единые дизайн-токены</span></a></li> <li><a href="#v9990-19-03-2026--поисковая-оптимизация-фундаментальная-ревизия-seo-системы"><span class="changelog-toc__version">v9.99.0</span><span class="changelog-toc__date">19-03-2026</span><span class="changelog-toc__topic" title="Поисковая оптимизация: фундаментальная ревизия SEO-системы">Поисковая оптимизация: фундаментальная ревизия SEO-системы</span></a></li> <li><a href="#v9980-18-03-2026--уведомления-живые-аватарки-время-и-хронология"><span class="changelog-toc__version">v9.98.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Уведомления: живые аватарки, время и хронология">Уведомления: живые аватарки, время и хронология</span></a></li> <li><a href="#v9970-18-03-2026--страница-темы-фильтры-аватарки-и-кнопка-«написать»"><span class="changelog-toc__version">v9.97.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Страница темы: фильтры, аватарки и кнопка «Написать»">Страница темы: фильтры, аватарки и кнопка «Написать»</span></a></li> <li><a href="#v9960-18-03-2026--темы-фильтры-плавно-обрезка-без-утечек-чистый-прод"><span class="changelog-toc__version">v9.96.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Темы: фильтры плавно, обрезка без утечек, чистый прод">Темы: фильтры плавно, обрезка без утечек, чистый прод</span></a></li> <li>
1<a href="#v9950-18-03-2026--редактор-молния-умная-форма-крепче-защита"><span class="changelog-toc__version">v9.95.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Редактор-молния, умная форма, крепче защита">Редактор-молния, умная форма, крепче защита</span></a></li> <li><a href="#v9940-18-03-2026--надёжность-безопасность-доступность"><span class="changelog-toc__version">v9.94.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Надёжность, безопасность, доступность">Надёжность, безопасность, доступность</span></a></li> <li><a href="#v9930-18-03-2026--хлебные-крошки-в-поиске-и-полировка-редактора"><span class="changelog-toc__version">v9.93.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Хлебные крошки в поиске и полировка редактора">Хлебные крошки в поиске и полировка редактора</span></a></li> <li><a href="#v9920-18-03-2026--эмбеды-конец-серого-прямоугольника-lite-youtube-и-полировка"><span class="changelog-toc__version">v9.92.0</span><span class="changelog-toc__date">18-03-2026</span><span class="changelog-toc__topic" title="Эмбеды: конец серого прямоугольника, lite-YouTube и полировка">Эмбеды: конец серого прямоугольника, lite-YouTube и полировка</span></a></li> <li><a href="#v9910-17-03-2026--редактор-квизы-новые-позиции-картинок-стрелки-в-поиске"><span class="changelog-toc__version">v9.91.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Редактор: квизы, новые позиции картинок, стрелки в поиске">Редактор: квизы, новые позиции картинок, стрелки в поиске</span></a></li> <li><a href="#v9900-17-03-2026--офлайн-без-паники-красивая-заглушка-умный-реконнект-тихие-обновления"><span class="changelog-toc__version">v9.90.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Офлайн без паники: красивая заглушка, умный реконнект, тихие обновления">Офлайн без паники: красивая заглушка, умный реконнект, тихие обновления</span></a></li> <li><a href="#v9890-17-03-2026--tailwind-css-фундамент-дизайн-системы"><span class="changelog-toc__version">v9.89.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Tailwind CSS: фундамент дизайн-системы">Tailwind CSS: фундамент дизайн-системы</span></a></li> <li><a href="#v9880-17-03-2026--seo-гигиена-чище-индекс-быстрее-поиск-меньше-мусора"><span class="changelog-toc__version">v9.88.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="SEO-гигиена: чище индекс, быстрее поиск, меньше мусора">SEO-гигиена: чище индекс, быстрее поиск, меньше мусора</span></a></li> <li><a href="#v9870-17-03-2026--режим-правок-обновление-«было-→-стало»"><span class="changelog-toc__version">v9.87.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Режим правок: обновление «было → стало»">Режим правок: обновление «было → стало»</span></a></li> <li><a href="#v9860-17-03-2026--генеральная-уборка-3-379-статей-приведены-в-порядок"><span class="changelog-toc__version">v9.86.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Генеральная уборка: 3 379 статей приведены в порядок">Генеральная уборка: 3 379 статей приведены в порядок</span></a></li> <li><a href="#v9850-17-03-2026--офлайн-устойчивость-и-ускорение-загрузки"><span class="changelog-toc__version">v9.85.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Офлайн-устойчивость и ускорение загрузки">Офлайн-устойчивость и ускорение загрузки</span></a></li> <li><a href="#v9840-17-03-2026--кадрирование-изображений-прямо-в-редакторе"><span class="changelog-toc__version">v9.84.0</span><span class="changelog-toc__date">17-03-2026</span><span class="changelog-toc__topic" title="Кадрирование изображений прямо в редакторе">Кадрирование изображений прямо в редакторе</span></a></li> <li><a href="#v9830-16-03-2026--pdf-книги-внутри-статей"><span class="changelog-toc__version">v9.83.0</span><span class="changelog-toc__date">16-03-2026</span><span class="changelog-toc__topic" title="PDF-книги внутри статей">PDF-книги внутри статей</span></a></li> <li><a href="#v9820-16-03-2026--мартовский-марш-320-файлов-рефакторинга"><span class="changelog-toc__version">v9.82.0</span><span class="changelog-toc__date">16-03-2026</span><span class="changelog-toc__topic" title="Мартовский марш: 320 файлов рефакторинга">Мартовский марш: 320 файлов рефакторинга</span></a></li> <li><a href="#v9810-16-03-2026--scribd-bandcamp-и-тишина-в-консоли"><span class="changelog-toc__version">v9.81.0</span><span class="changelog-toc__date">16-03-2026</span><span class="changelog-toc__topic" title="Scribd, Bandcamp и тишина в консоли">Scribd, Bandcamp и тишина в консоли</span></a></li> <li><a href="#v9800-16-03-2026--под-капотом-платформа-учится-жить-без-рамок"><span class="changelog-toc__version">v9.80.0</span><span class="changelog-toc__date">16-03-2026</span><span class="changelog-toc__topic" title="Под капотом: платформа учится жить без рамок">Под капотом: платформа учится жить без рамок</span></a></li> <li><a href="#v9790-16-03-2026--форматирование-в-ремарках"><span class="changelog-toc__version">v9.79.0</span><span class="changelog-toc__date">16-03-2026</span><span class="changelog-toc__topic" title="Форматирование в ремарках">Форматирование в ремарках</span></a></li> <li><a href="#v9780-15-03-2026--генеральная-уборка-старые-статьи-обретают-новую-разметку"><span class="changelog-toc__version">v9.78.0</span><span class="changelog-toc__date">15-03-2026</span><span class="changelog-toc__topic" title="Генеральная уборка: старые статьи обретают новую разметку">Генеральная уборка: старые статьи обретают новую разметку</span></a></li> <li><a href="#v9770-15-03-2026--без-вспышек-статьи-открываются-как-книга"><span class="changelog-toc__version">v9.77.0</span><span class="changelog-toc__date">15-03-2026</span><span class="changelog-toc__topic" title="Без вспышек: статьи открываются как книга">Без вспышек: статьи открываются как книга</span></a></li> <li><a href="#v9760-15-03-2026--embed-them-all-вставляйте-что-угодно-откуда-угодно"><span class="changelog-toc__version">v9.76.0</span><span class="changelog-toc__date">15-03-2026</span><span class="changelog-toc__topic" title="Embed Them All: вставляйте что угодно, откуда угодно">Embed Them All: вставляйте что угодно, откуда угодно</span></a></li> <li><a href="#v9750-15-03-2026--релизный-hardening-карточки-авторизация-и-мелочи-которые-решают"><span class="changelog-toc__version">v9.75.0</span><span class="changelog-toc__date">15-03-2026</span><span class="changelog-toc__topic" title="Релизный hardening: карточки, авторизация и мелочи, которые решают">Релизный hardening: карточки, авторизация и мелочи, которые решают</span></a></li> <li><a href="#v9740-15-03-2026--код-который-не-мешает-читать"><span class="changelog-toc__version">v9.74.0</span><span class="changelog-toc__date">15-03-2026</span><span class="changelog-toc__topic" title="Код, который не мешает читать">Код, который не мешает читать</span></a></li> <li><a href="#v9730-14-03-2026--быстрее-надёжнее-доступнее"><span class="changelog-toc__version">v9.73.0</span><span class="changelog-toc__date">14-03-2026</span>
1<span class="changelog-toc__topic" title="Быстрее, надёжнее, доступнее">Быстрее, надёжнее, доступнее</span></a></li> <li><a href="#v9720-14-03-2026--призраки-уведомлений-изгнаны-и-обезврежены"><span class="changelog-toc__version">v9.72.0</span><span class="changelog-toc__date">14-03-2026</span><span class="changelog-toc__topic" title="Призраки уведомлений: изгнаны и обезврежены">Призраки уведомлений: изгнаны и обезврежены</span></a></li> <li><a href="#v9710-14-03-2026--под-капотом-платформа-учится-жить-без-фреймворка"><span class="changelog-toc__version">v9.71.0</span><span class="changelog-toc__date">14-03-2026</span><span class="changelog-toc__topic" title="Под капотом: платформа учится жить без фреймворка">Под капотом: платформа учится жить без фреймворка</span></a></li> <li><a href="#v9700-14-03-2026--уведомления-ничего-не-теряется-всё-находится"><span class="changelog-toc__version">v9.70.0</span><span class="changelog-toc__date">14-03-2026</span><span class="changelog-toc__topic" title="Уведомления: ничего не теряется, всё находится">Уведомления: ничего не теряется, всё находится</span></a></li> <li><a href="#v9690-13-03-2026--тише-едешь-дальше-будешь-охота-на-призраков-sentry"><span class="changelog-toc__version">v9.69.0</span><span class="changelog-toc__date">13-03-2026</span><span class="changelog-toc__topic" title="Тише едешь, дальше будешь: охота на призраков Sentry">Тише едешь, дальше будешь: охота на призраков Sentry</span></a></li> <li><a href="#v9680-13-03-2026--импортэкспорт-пишите-где-удобно-публикуйте-на-дискурсе"><span class="changelog-toc__version">v9.68.0</span><span class="changelog-toc__date">13-03-2026</span><span class="changelog-toc__topic" title="Импорт/экспорт: пишите где удобно, публикуйте на Дискурсе">Импорт/экспорт: пишите где удобно, публикуйте на Дискурсе</span></a></li> <li><a href="#v9670-13-03-2026--поиск-авторов-находит-всех-а-не-только-первых-пятьдесят"><span class="changelog-toc__version">v9.67.0</span><span class="changelog-toc__date">13-03-2026</span><span class="changelog-toc__topic" title="Поиск авторов: находит всех, а не только первых пятьдесят">Поиск авторов: находит всех, а не только первых пятьдесят</span></a></li> <li><a href="#v9660-13-03-2026--эмодзи-в-сообщениях-одна-кнопка--64-эмоции"><span class="changelog-toc__version">v9.66.0</span><span class="changelog-toc__date">13-03-2026</span><span class="changelog-toc__topic" title="Эмодзи в сообщениях: одна кнопка — 64 эмоции">Эмодзи в сообщениях: одна кнопка — 64 эмоции</span></a></li> <li><a href="#v9650-13-03-2026--ctrlh-найти-и-заменить-прямо-в-редакторе"><span class="changelog-toc__version">v9.65.0</span><span class="changelog-toc__date">13-03-2026</span><span class="changelog-toc__topic" title="Ctrl+H: найти и заменить прямо в редакторе">Ctrl+H: найти и заменить прямо в редакторе</span></a></li> <li><a href="#v9640-12-03-2026--один-retry-на-всех-ошибки-больше-не-теряются-в-переводе"><span class="changelog-toc__version">v9.64.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="Один retry на всех: ошибки больше не теряются в переводе">Один retry на всех: ошибки больше не теряются в переводе</span></a></li> <li><a href="#v9630-12-03-2026--минус-90-кб-ui-bootstrap-на-покой"><span class="changelog-toc__version">v9.63.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="Минус 90 КБ: ui-bootstrap на покой">Минус 90 КБ: ui-bootstrap на покой</span></a></li> <li><a href="#v9620-12-03-2026--ctrls-для-черновиков-поиск-в-«читайте-также»-и-доведённые-до-блеска-групповые-чаты"><span class="changelog-toc__version">v9.62.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="Ctrl+S для черновиков, поиск в «Читайте также» и доведённые до блеска групповые чаты">Ctrl+S для черновиков, поиск в «Читайте также» и доведённые до блеска групповые чаты</span></a></li> <li><a href="#v9610-12-03-2026--csp-мониторинг-глубокий-focus-management-и-защита-от-гонок-в-сообщениях"><span class="changelog-toc__version">v9.61.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="CSP-мониторинг, глубокий focus management и защита от гонок в сообщениях">CSP-мониторинг, глубокий focus management и защита от гонок в сообщениях</span></a></li> <li><a href="#v9600-12-03-2026--автосохранение-в-искусстве-умные-ошибки-и-ловушки-фокуса"><span class="changelog-toc__version">v9.60.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="Автосохранение в Искусстве, умные ошибки и ловушки фокуса">Автосохранение в Искусстве, умные ошибки и ловушки фокуса</sp
1an></a></li> <li><a href="#v9590-12-03-2026--групповые-чаты-доведены-до-ума-аватары-настройки-надёжность"><span class="changelog-toc__version">v9.59.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="Групповые чаты доведены до ума: аватары, настройки, надёжность">Групповые чаты доведены до ума: аватары, настройки, надёжность</span></a></li> <li><a href="#v9580-12-03-2026--рассылки-нового-уровня-умная-доставка-точная-аналитика-защита-от-сбоев"><span class="changelog-toc__version">v9.58.0</span><span class="changelog-toc__date">12-03-2026</span><span class="changelog-toc__topic" title="Рассылки нового уровня: умная доставка, точная аналитика, защита от сбоев">Рассылки нового уровня: умная доставка, точная аналитика, защита от сбоев</span></a></li> <li><a href="#v9570-11-03-2026--база-сервиса-рассылки-готова-персонализация-управление-подписками-трекинг"><span class="changelog-toc__version">v9.57.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="База сервиса рассылки готова: персонализация, управление подписками, трекинг">База сервиса рассылки готова: персонализация, управление подписками, трекинг</span></a></li> <li><a href="#v9560-11-03-2026--обновление-системы-подписок-мгновенный-отклик-точные-счетчики-и-другие-фиксы"><span class="changelog-toc__version">v9.56.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Обновление системы подписок: мгновенный отклик, точные счетчики и другие фиксы">Обновление системы подписок: мгновенный отклик, точные счетчики и другие фиксы</span></a></li> <li><a href="#v9550-11-03-2026--групповые-чаты-цитаты-управление-участниками"><span class="changelog-toc__version">v9.55.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Групповые чаты, цитаты, управление участниками">Групповые чаты, цитаты, управление участниками</span></a></li> <li><a href="#v9540-11-03-2026--прощай-bower-всё-на-npm-модалка-вернулась-настройки-под-защитой"><span class="changelog-toc__version">v9.54.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Прощай, Bower: всё на npm, модалка вернулась, настройки под защитой">Прощай, Bower: всё на npm, модалка вернулась, настройки под защитой</span></a></li> <li><a href="#v9530-11-03-2026--картинки-всегда-через-https-storybook-на-диете-деплой-одной-командой"><span class="changelog-toc__version">v9.53.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Картинки всегда через HTTPS, Storybook на диете, деплой одной командой">Картинки всегда через HTTPS, Storybook на диете, деплой одной командой</span></a></li> <li><a href="#v9520-11-03-2026--окно-входа-летает-storybook-расцвёл-админка-не-теряет-правки"><span class="changelog-toc__version">v9.52.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Окно входа летает, Storybook расцвёл, админка не теряет правки">Окно входа летает, Storybook расцвёл, админка не теряет правки</span></a></li> <li><a href="#v9510-11-03-2026--мгновенные-переходы-и-комментарии-без-гонок"><span class="changelog-toc__version">v9.51.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Мгновенные переходы и комментарии без гонок">Мгновенные переходы и комментарии без гонок</span></a></li> <li><a href="#v9500-11-03-2026--прощай-restangular-платформа-сбросила-балласт"><span class="changelog-toc__version">v9.50.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Прощай, Restangular: платформа сбросила баÐ
1»Ð»Ð°ÑÑ‚">Прощай, Restangular: платформа сбросила балласт</span></a></li> <li><a href="#v9490-11-03-2026--финальная-полировка-перед-большим-релизом"><span class="changelog-toc__version">v9.49.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Финальная полировка перед большим релизом">Финальная полировка перед большим релизом</span></a></li> <li><a href="#v9480-11-03-2026--сторож-на-вахте-мониторинг-линтинг-порядок"><span class="changelog-toc__version">v9.48.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Сторож на вахте: мониторинг, линтинг, порядок">Сторож на вахте: мониторинг, линтинг, порядок</span></a></li> <li><a href="#v9470-11-03-2026--приглашения-с-защитой-и-сроком-годности"><span class="changelog-toc__version">v9.47.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Приглашения с защитой и сроком годности">Приглашения с защитой и сроком годности</span></a></li> <li><a href="#v9460-11-03-2026--живые-комментарии-и-лёгкий-редактор"><span class="changelog-toc__version">v9.46.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Живые комментарии и лёгкий редактор">Живые комментарии и лёгкий редактор</span></a></li> <li><a href="#v9450-11-03-2026--легче-на-старте-умнее-на-краю"><span class="changelog-toc__version">v9.45.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Легче на старте, умнее на краю">Легче на старте, умнее на краю</span></a></li> <li><a href="#v9440-11-03-2026--прощай-restangular-фундамент-стал-легче-и-быстрее"><span class="changelog-toc__version">v9.44.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Прощай, Restangular: фундамент стал легче и быстрее">Прощай, Restangular: фундамент стал легче и быстрее</span></a></li> <li><a href="#v9430-11-03-2026--storybook-песочница-и-порядок"><span class="changelog-toc__version">v9.43.0</span><span class="changelog-toc__date">11-03-2026</span><span class="changelog-toc__topic" title="Storybook: песочница и порядок">Storybook: песочница и порядок</span></a></li> <li><a href="#v9420-10-03-2026--быстрее-удобнее-отзывчивее"><span class="changelog-toc__version">v9.42.0</span><span class="changelog-toc__date">10-03-2026</span><span class="changelog-toc__topic" title="Быстрее, удобнее, отзывчивее">Быстрее, удобнее, отзывчивее</span></a></li> <li><a href="#v9410-10-03-2026--комплексный-аудит-безопасности"><span class="changelog-toc__version">v9.41.0</span><span class="changelog-toc__date">10-03-2026</span><span class="changelog-toc__topic" title="Комплексный аудит безопасности">Комплексный аудит безопасности</span></a></li> <li><a href="#v9400-09-03-2026--распознание-ссылок-тихий-режим-сравнение-версий-storybook-и-система-рассылок"><span class="changelog-toc__version">v9.40.0</span><span class="changelog-toc__date">09-03-2026</span><span class="changelog-toc__topic" title="Распознание ссылок, тихий режим, сравнение версий, storybook и система рассылок">Распознание ссылок, тихий режим, сравнение версий, storybook и система рассылок</span></a></li> <li><a href="#v9390-08-03-2026--ubuntu-18-→-20-→-22-→-24-надёжнее-и-быстрее"><span class="changelog-toc__version">v9.39.0</span><span class="changelog-toc__date">08-03-2026</span><span class="changelog-toc__topic" title="Ubuntu 18 → 20 → 22 → 24: надёжнее и быстрее">Ubuntu 18 → 20 → 22 → 24: надёжнее и быстрее</span></a></li> <li><a href="#v9380-08-03-2026--оптимизация-загрузки-картинок-авторизации-и-загрузки-главной"><span class="changelog-toc__version">v9.38.0</span><span class="changelog-toc__date">08-03-2026</span><span class="changelog-toc__topic" title="Оптимизация загрузки картинок, авторизации и загрузки главной">Оптимизация загрузки картинок, авторизации и загрузки главной</span></a></li> <li><a href="#v9370-07-03-2026--meteor-34-реформа-технического-фундамента-бэкенда"><span class="changelog-toc__version">v9.37.0</span><span class="changelog-toc__date">07-03-2026</span><span class="changelog-toc__topic" title="Meteor 3.4: реформа теÑ
1…нического фундамента бэкенда">Meteor 3.4: реформа технического фундамента бэкенда</span></a></li> <li><a href="#v9360-06-03-2026--дашборд-редакции-тихие-уведомления-и-точный-редактор"><span class="changelog-toc__version">v9.36.0</span><span class="changelog-toc__date">06-03-2026</span><span class="changelog-toc__topic" title="Дашборд редакции, тихие уведомления и точный редактор">Дашборд редакции, тихие уведомления и точный редактор</span></a></li> <li><a href="#v9350-06-03-2026--дискурс-теперь-в-tor"><span class="changelog-toc__version">v9.35.0</span><span class="changelog-toc__date">06-03-2026</span><span class="changelog-toc__topic" title="Дискурс теперь в Tor">Дискурс теперь в Tor</span></a></li> <li><a href="#v9340-06-03-2026--«читайте-также»-одной-кнопкой-шапка-без-глитчей"><span class="changelog-toc__version">v9.34.0</span><span class="changelog-toc__date">06-03-2026</span><span class="changelog-toc__topic" title="«Читайте также» одной кнопкой, шапка без глитчей">«Читайте также» одной кнопкой, шапка без глитчей</span></a></li> <li><a href="#v9330-05-03-2026--живые-обсуждения-мгновенный-сброс-превью-и-надёжная-почта"><span class="changelog-toc__version">v9.33.0</span><span class="changelog-toc__date">05-03-2026</span><span class="changelog-toc__topic" title="Живые обсуждения, мгновенный сброс превью и надёжная почта">Живые обсуждения, мгновенный сброс превью и надёжная почта</span></a></li> <li><a href="#v9320-05-03-2026--уведомления-ведут-прямо-к-ответу-меню-на-всех-экранах"><span class="changelog-toc__version">v9.32.0</span><span class="changelog-toc__date">05-03-2026</span><span class="changelog-toc__topic" title="Уведомления ведут прямо к ответу, меню на всех экранах">Уведомления ведут прямо к ответу, меню на всех экранах</span></a></li> <li><a href="#v9310-04-03-2026--сервер-стал-надёжнее-и-безопаснее"><span class="changelog-toc__version">v9.31.0</span><span class="changelog-toc__date">04-03-2026</span><span class="changelog-toc__topic" title="Сервер стал надёжнее и безопаснее">Сервер стал надёжнее и безопаснее</span></a></li> <li><a href="#v9300-03-03-2026--платформа-обновлена-со-стека-2016-на-2024-быстрее-надежнее-современнее"><span class="changelog-toc__version">v9.30.0</span><span class="changelog-toc__date">03-03-2026</span><span class="changelog-toc__topic" title="Платформа обновлена со стека 2016 на 2024: быстрее, надежнее, современнее">Платформа обновлена со стека 2016 на 2024: быстрее, надежнее, современнее</span></a></li> <li><a href="#v9230-03-03-2026--правильные-превью-в-соцсетях-авторы-в-поиске-старые-ссылки-работают"><span class="changelog-toc__version">v9.23.0</span><span class="changelog-toc__date">03-03-2026</span><span class="changelog-toc__topic" title="Правильные превью в соцсетях, авторы в поиске, старые ссылки работают">Правильные превью в соцсетях, авторы в поиске, старые ссылки работают</span></a></li> <li><a href="#v9222-02-03-2026--повтор-сохранения-загрузка-через-зеркало-быстрее-везде"><span class="changelog-toc__version">v9.22.2</span><span class="changelog-toc__date">02-03-2026</span><span class="changelog-toc__topic" title="Повтор сохранения, загрузка через зеркало, быстрее везде">Повтор сохранения, загрузка через зеркало, быстрее везде</span></a></li> <li><a href="#v9221-02-03-2026--мгновенная-повторная-загрузка-скелетоны-и-мобильная-клавиатура"><span class="changelog-toc__version">v9.22.1</span><span class="changelog-toc__date">02-03-2026</span><span class="changelog-toc__topic" title="Мгновенная повторная загрузка, скелетоны и мобильная клавиатура">Мгновенная повторная загрузка, скелетоны и мобильная клавиатура</span></a></li> <li><a href="#v9220-01-03-2026--комментарии-на-полях-форматирование-в-обсуждениях-упоминания-с-именами-и-многое-другое"><span class="changelog-toc__version">v9.22.0</span><span class="changelog-toc__date">01-03-2026</span><span class="changelog-toc__topic" title="Комментарии на полях, форматирование в обсуждениях, @упоминания с именами и многое другое">Комментарии на полях, форматирование в обсуждениях, @упоминания с именами и многое другое</span></a></li> <li><a href="#v9210-27-02-2026--умные-кнопки-редактора-чистые-сообщения-и-мгновенные-исправления"><span class="changelog-toc__version">v9.21.0</span><span class="changelog-toc__date">27-02-2026</span><span class="changelog-toc__topic" title="Умные кнопки редактора, чистые сообщения и мгновенные исправления">Умные кнопки редактора, чистые сообщения и мгновенные исправления</span></a></li> <li><a href="#v9200-26-02-2026--мгновенная-загрузка-редактора-правильные-ссылки-и-проверка-грамматики"><span class="changelog-toc__version">v9.20.0</span><span class="changelog-toc__date">26-02-2026</span><span class="changelog-toc__topic" title="Мгновенная загрузка редактора, правильные ссылки и проверка грамматики">Мгновенная загрузка редактора, правильные ссылки и проверка грамматики</span></a></li> <li><a href="#v9190-25-02-2026--соавторство-оптимизированные-шрифты-и-hardened-security"><span class="changelog-toc__version">v9.19.0</span><span class="changelog-toc__date">25-02-2026</span><span class="changelog-toc__topic" title="Соавторство, оптимизированные шрифты и hardened security">Соавторство, оптимизированные шрифты и hardened security</span></a></li> <li><a href="#v9180-24-02-2026--баннеры-соучастия-в-лентах-и-languagetool-на-стероидах"><span class="changelog-toc__version">v9.18.0</span><span class="changelog-toc__date">24-02-2026</span><span class="changelog-toc__topic" title="Баннеры соучастия в лентах и LanguageTool на стероидах">Баннеры соучастия в лентах и LanguageTool на стероидах</span></a></li> <li><a href="#v9170-23-02-2026--бейджи-непрочитанного-в-ленте-литература-без-обёрток-и-хирургическая-стабильность"><span class="changelog-toc__version">v9.17.0</span><span class="changelog-toc__date">23-02-2026</span><span class="changelog-toc__topic" title="Бейджи непрочитанного в ленте, литература без обёрток и хирургическая стабильность">Бейджи непрочитанного в ленте, литература без обёрток и хирургическая стабильность</span></a></li> <li><a href="#v9160-23-02-2026--запросы-на-соавторство-умн
1ые-сноски-и-мощные-инструменты-редактора"><span class="changelog-toc__version">v9.16.0</span><span class="changelog-toc__date">23-02-2026</span><span class="changelog-toc__topic" title="Запросы на соавторство, умные сноски и мощные инструменты редактора">Запросы на соавторство, умные сноски и мощные инструменты редактора</span></a></li> <li><a href="#v9150-22-02-2026--мгновенная-актуализация-ролей-при-открытии-платформы"><span class="changelog-toc__version">v9.15.0</span><span class="changelog-toc__date">22-02-2026</span><span class="changelog-toc__topic" title="Мгновенная актуализация ролей при открытии платформы">Мгновенная актуализация ролей при открытии платформы</span></a></li> <li><a href="#v9140-22-02-2026--коллаборации-в-один-клик-и-seo-без-компромиссов"><span class="changelog-toc__version">v9.14.0</span><span class="changelog-toc__date">22-02-2026</span><span class="changelog-toc__topic" title="Коллаборации в один клик и SEO без компромиссов">Коллаборации в один клик и SEO без компромиссов</span></a></li> <li><a href="#v9130-21-02-2026--упоминания-сквозная-система-от-редактора-до-уведомлений"><span class="changelog-toc__version">v9.13.0</span><span class="changelog-toc__date">21-02-2026</span><span class="changelog-toc__topic" title="@упоминания: сквозная система от редактора до уведомлений">@упоминания: сквозная система от редактора до уведомлений</span></a></li> <li><a href="#v9121-20-02-2026--безопасность-производительность-и-стабильность-pre-release-polish"><span class="changelog-toc__version">v9.12.1</span><span class="changelog-toc__date">20-02-2026</span><span class="changelog-toc__topic" title="Безопасность, производительность и стабильность: pre-release polish">Безопасность, производительность и стабильность: pre-release polish</span></a></li> <li><a href="#v9120-19-02-2026--уведомления-коллаборации-ночная-тема-редактора"><span class="changelog-toc__version">v9.12.0</span><span class="changelog-toc__date">19-02-2026</span><span class="changelog-toc__topic" title="Уведомления, коллаборации, ночная тема редактора">Уведомления, коллаборации, ночная тема редактора</span></a></li> <li><a href="#v9113-19-02-2026--загрузка-обложек-дублирование-текста-конфликты-версия"><span class="changelog-toc__version">v9.11.3</span><span class="changelog-toc__date">19-02-2026</span><span class="changelog-toc__topic" title="Загрузка обложек, дублирование текста, конфликты версия">Загрузка обложек, дублирование текста, конфликты версия</span></a></li> <li><a href="#v9112-18-02-2026--changelog-полировка-ошибок-видео-и-email"><span class="changelog-toc__version">v9.11.2</span><span class="changelog-toc__date">18-02-2026</span><span class="changelog-toc__topic" title="Changelog, полировка ошибок, видео и email">Changelog, полировка ошибок, видео и email</span></a></li> <li><a href="#v9111-17-02-2026--надёжность-безопасность-и-доступность-post-review-polish"><span class="changelog-toc__version">v9.11.1</span><span class="changelog-toc__date">17-02-2026</span><span class="changelog-toc__topic" title="Надёжность, безопасность и доступность: post-review polish">Надёжность, безопасность и доступность: post-review polish</span></a></li> <li><a href="#v9110-16-02-2026--категории-обратной-связи-ремарки-в-редакторе-подписки-на-теги"><span class="changelog-toc__version">v9.11.0</span><span class="changelog-toc__date">16-02-2026</span><span class="changelog-toc__topic" title="Категории обратной связи, ремарки в редакторе, подписки на теги">Категории обратной связи, ремарки в редакторе, подписки на теги</span></a></li> <li><a href="#v9100-15-02-2026--публикация-без-нервов-валидация-подтверждение-и-умн
1ые-ошибки"><span class="changelog-toc__version">v9.10.0</span><span class="changelog-toc__date">15-02-2026</span><span class="changelog-toc__topic" title="Публикация без нервов: валидация, подтверждение и умные ошибки">Публикация без нервов: валидация, подтверждение и умные ошибки</span></a></li> <li><a href="#v990-14-02-2026--черновики-без-границ-совместное-редактирование-общий-доступ-по-ссылке-и-безопасность-нового-поколения"><span class="changelog-toc__version">v9.9.0</span><span class="changelog-toc__date">14-02-2026</span><span class="changelog-toc__topic" title="Черновики без границ: совместное редактирование, общий доступ по ссылке и безопасность нового поколения">Черновики без границ: совместное редактирование, общий доступ по ссылке и безопасность нового поколения</span></a></li> <li><a href="#v980-13-02-2026---уведомления-мечты-черновики-наяву-и-защита-без-компромиссов"><span class="changelog-toc__version">v9.8.0</span><span class="changelog-toc__date">13-02-2026</span><span class="changelog-toc__topic" title="Уведомления мечты, черновики наяву и защита без компромиссов">Уведомления мечты, черновики наяву и защита без компромиссов</span></a></li> <li><a href="#v970-12-02-2026---правки-живут-вечно-suggestion-persistence-переосмысленные-комментарии-и-печать"><span class="changelog-toc__version">v9.7.0</span><span class="changelog-toc__date">12-02-2026</span><span class="changelog-toc__topic" title="Правки живут вечно: suggestion-persistence, переосмысленные комментарии и печать">Правки живут вечно: suggestion-persistence, переосмысленные комментарии и печать</span></a></li> <li><a href="#v960-12-02-2026---галерея-спойлеры-oauth-черновики-и-обновлённый-вход"><span class="changelog-toc__version">v9.6.0</span><span class="changelog-toc__date">12-02-2026</span><span class="changelog-toc__topic" title="Галерея, спойлеры, OAuth, черновики и обновлённый вход">Галерея, спойлеры, OAuth, черновики и обновлённый вход</span></a></li> <li><a href="#v950-11-02-2026---молниеносная-загрузка-lazy-split-шаблонов-скелетоны-prefetch-и-плавные-переходы"><span class="changelog-toc__version">v9.5.0</span><span class="changelog-toc__date">11-02-2026</span><span class="changelog-toc__topic" title="Молниеносная загрузка: lazy-split шаблонов, скелетоны, prefetch и плавные переходы">Молниеносная загрузка: lazy-split шаблонов, скелетоны, prefetch и плавные переходы</span></a></li> <li><a href="#v940-10-02-2026---html-редактор-с-codemirror-тема-monokai-умное-форматирование-и-горячие-клавиши"><span class="changelog-toc__version">v9.4.0</span><span class="changelog-toc__date">10-02-2026</span><span class="changelog-toc__topic" title="HTML-редактор с CodeMirror: тема Monokai, умное форматирование и горячие клавиши">HTML-редактор с CodeMirror: тема Monokai, умное форматирование и горячие клавиши</span></a></li> <li><a href="#v930-10-02-2026---embeddings--suggestion-mode-полная-поддержка-стилей-медиа-и-блочного-форматирования"><span class="changelog-toc__version">v9.3.0</span><span class="changelog-toc__date">10-02-2026</span><span class="changelog-toc__topic" title="Embeddings &amp;amp; Suggestion Mode: полная поддержка стилей, медиа и блочного форматирования">Embeddings &amp;amp; Suggestion Mode: полная поддержка стилей, медиа и блочного форматирования</span></a></li> <li><a href="#v920-10-02-2026---коллаборативный-редактор-полный-комплект-стилей-разделение-кода--новые-настройки-профиля"><span class="changelog-toc__version">v9.2.0</span><span class="changelog-toc__date">10-02-2026</span><span class="changelog-toc__topic" title="Коллаборативный редактор: полный комплект стилей, разделение кода + новые настройки профиля">Коллаборативный редактор: полный комплект стилей, разделение кода + новые настройки профиля</span></a></li> <li><a href="#v910-09-02-2026---оглавление-справка-по-горячим-клавишам-новые-шорткаты"><span class="changelog-toc__version">v9.1.0</span><span class="changelog-toc__date">09-02-2026</span><span class="changelog-toc__topic" title="Оглавление, справка по горячим клавишам, новые шорткаты">Оглавление, справка по горячим клавишам, новые шорткаты</span></a></li> <li><a href="#v901-08-02-2026---staging-среда-и-кнопка-сохранения"><span class="changelog-toc__version">v9.0.1</span><span class="changelog-toc__date">08-02-2026</span><span class="changelog-toc__topic" title="Staging-среда и кнопка сохранения">Staging-среда и кнопка сохранения</span></a></li> <li><a href="#v900-07-02-2026---новый-редактор-с-коллаборацией"><span class="changelog-toc__version">v9.0.0</span><span class="changelog-toc__date">07-02-2026</span><span class="changelog-toc__topic" title="Новый редактор с коллаборацией">Новый редактор с коллаборацией</span></a></li> </ul> </nav> </div> </div> </div>'),e.put("/pages/about/community-rules.html",'<update-meta class="black-link" title="{{ \'Правила дискуссий в открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" name="description" content="{{ \'За что можно заслужить дырку в карме и как получить лучи благодарности за вклад в дискуссии в сообществах самиздата\' | t }}" ></update-meta> <update-meta class="black-link" name="keywords" content="{{ \'Discours.io, сообщества, ценности, правила редакции, многоголосие, созидание\' | t }}" ></update-meta> <update-meta class="black-link" property="og:title" content="{{ \'Правила дискуссий в открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" property="og:description" content="{{ \'За что можно заслужить дырку в карме и как получить лучи благодарности за вклад в дискуссии в сообществах самиздата\' | t }}" ></update-meta> <update-meta property="og:image" content="/images/discussion-rules.jpg"></update-meta>
1 <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="750"></update-meta> <div class="ds-container open-post about-post tw:mt-5"> <div class="tw:row-flex" id="basics"> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12"> <h1 class="h2 black-bg-heading black-bg-heading-margin"> <span class="wrapped">{{ \'Правила сообществ самиздата в соцсетях\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top"> <p><span>{{ \'Открытая редакция существует благодаря дружному сообществу авторов и читателей — вдумчивых и сознательных людей, приверженных ценностям гуманизма, демократии и прав человека. Мы очень ценим атмосферу осмысленного общения, которая здесь сложилась. Чтобы сохранить ее такой же уютной и творческой, мы составили правила общения в сообществе, руководствуясь которыми каждый мог бы соучаствовать в плодотворных дискуссиях, не задевая других. Ключевой принцип этих правил предельно прост — уважайте ближних, постарайтесь не нарушать законы Российской Федерации без крайней на то необходимости и помните, что в дискуссиях чутких и здравомыслящих людей рождается истина.\' | t }}</span></p> <h2 class="not-left tw:mb-6"> <span>{{ \'За что можно получить дырку в карме и выиграть бан в сообществе\' | t }}</span> </h2> <ol> <li> <p><span>{{ \'Оскорбления, личные нападки, травля и угрозы. В любом виде. Конкретного человека или социальной группы — не суть. Агрессия, переход на личности и токсичность едва ли способствуют плодотворному общению.\' | t }}</span></p> </li> <li> <p><span>{{ \'Шовинизм, расизм, сексизм, гомофобия, пропаганда ненависти, педофилии, суицида, распространение детской порнографии и другого человеконенавистнического контента.\' | t }}</span></p> </li> <li> <p><span>{{ \'Спам, реклама, фейкньюз, ссылки на пропагандистские СМИ, вбросы дезинформации, специально уводящий от темы флуд, провокации, разжигание конфликтов, намеренный срыв дискуссий.\' | t }}</span></p> </li> <li> <p><span>{{ \'Неаргументированная критика и комментарии вроде «отстой», «зачем я это увидел/а», «не читал, но осуждаю», «либераху порвало», «лол», «скатились», «первый нах» и тому подобные. Односложные реплики не подразумевают возможность обогащающего диалога, не продуктивны и никак не помогают авторам делать материалы лучше, а читателям — разобраться.\' | t }}</span></p> </li> </ol> <h2 class="not-left tw:mb-6"> <span>{{ \'За что можно получить лучи добра и благодарности в сообществе\' | t }}</span> </h2> <ol> <li> <p><span><span class="lead">{{ \'Вежливость и конструктивность.\' | t }}</span> {{ \'Мы выступаем за конструктивный диалог, аргументированные комментарии и доброжелательное отношение друг к другу. Задавайте содержательные вопросы, пишите развернутые комментарии, подкрепляйте их аргументами, чтобы диалог был полезен всем участникам, помогая глубже понять тему и разобраться в вопросе. И, пожалуйста, уважайте собеседника, даже если он вам лично не импонирует: только так получаются продуктивные дискуссии.\' | t }}</span></p> </li> <li> <p><span><span class="lead">{{ \'Обмен знаниями и историями.\' | t }}</span> {{ \'Осмысленные высказывания по теме поста, оригинальные рассуждения, рассказы о личном опыте и проектах, обмен профессиональной экспертизой, наблюдения и реальные истории из жизни — чем больше мы делимся друг с другом знаниями, тем интереснее и плодотворнее становится наше общение. Помните, что каждый вдумчивый ответ повышает качество дискуссий в сообществе и делает чтение самиздата ещё интереснее.\' | t }}</span></p> </li> <li> <p><span><span class="lead">{{ \'Чувство юмора и добродушие.\' | t }}</span> {{ \'Остроумие и дружелюбие не только направляют дискуссии в продуктивное русло, но и улучшают настроение.\' | t }} </span><span>{{ \'Не вредите негативом, которого в интернете и без нас хватает, и не травите на корню классные инициативы — всё великое начинается с малого. Мы за поддерживающую и вдохновляющую атмосферу в сообществе. Надеемся, вы тоже.\' | t }}</span></p> </li> <li> <p><span><span class="lead">{{ \'Благодарность и поддержка.\' | t }}</span> {{ \'Если публикация вам зашла, не стесняйтесь ставить лайки, делиться понравившимися материалами, благодарить авторов, читателей, художников и редакторов в комментариях. Цените и поддерживайте классные проекты, сильные тексты, новое искусство, осмысленные комментарии и вклад других в самиздат — сотрудничество делает нас сильнее и усиливает звучание идей и смыслов, которые помогают лучше понимать мир.\' | t }}</span></p> </li> </ol> </div> </div> </div>'),e.put("/pages/about/constitution.html",'<update-meta title="{{ \'Конституция\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Конституция Дискурса: права и свободы человека и журналиста.\' | t }}" ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, конституция, правила\' | t }}"></update-meta> <update-meta property="og:title" content="{{ \'Конституция\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Конституция Дискурса: права и свободы человека и журналиста.\' | t }}" ></update-meta> <update-meta property="og:image" content="/images/constitution.jpg"></update-meta>
1 <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="470"></update-meta> <div class="tw:overflow-hidden constitution-wrapper"> <div class="ds-container"> <div class="tw:row-flex"> <div class="constitution-sidebar tw:gutter-half tw:w-full"> <h1 class="h2 black-bg-heading constitution-header black-bg-heading-margin"> <span>{{ \'Конституция\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:md:w-10/12"> <div class="constitution"> <div class="col first"> <p><span class="number">1</span>{{ \'Мы объединились, чтобы вместе создавать общий интернет-журнал, содержание которого зависит от каждого.\' | t }}</p> </div> <div class="col bottom second"> <p><span class="number">2</span>{{ \'Все авторы равны, и никто не равнее других.\' | t }}</p> </div> <div class="col bottom third"> <p><span class="number">3</span>{{ \'Каждый имеет право на мнение, право высказаться и быть услышанным.\' | t }}</p> </div> <div class="col bottom continue-right fourth"> <p><span class="number">4</span>{{ \'Каждый, отстаивая свое мнение, обязан его аргументировать.\' | t }}</p> </div> </div> <div class="constitution"> <div class="col continue-left fifth"> <p><span class="number">5</span>{{ \'Каждый пишет о том, о чем хочет писать, и никто не обязан писать о том, о чём не хочет.\' | t }}</p> </div> <div class="col bottom sixth"> <p><span class="number">6</span>{{ \'Каждый может помочь другому сделать его материал лучше.\' | t }}</p> </div> <div class="col seventh"> <p><span class="number">7</span>{{ \'Каждый имеет право быть непохожим на других.\' | t }}</p> </div> <div class="col eighth"> <p><span class="number">8</span>{{ \'Каждый имеет право любить своих друзей, но любить истину обязан больше.\' | t }}</p> </div> </div> </div> </div> </div> </div>'),e.put("/pages/about/contacts.html",'<update-meta title="{{ \'Контакты\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Как связаться с редакцией открытого журнала\' | t }}" ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, контакты, как добраться?\' | t }}" ></update-meta> <update-meta property="og:title" content="{{ \'Контакты\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Как связаться с редакцией открытого журнала\' | t }}" ></update-meta> <div class="ds-container"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:md:w-6/12"> <p> {{ \'Вы можете навестить нас в Открытой редакции Дискурса:\' | t }} <em>{{ \'Москва, Потаповский переулок, 8/12, стр. 2\' | t }}</em> </p> <p ng-bind-html="(\'Статьи и искусство для публикации присылайте через форму или по адресу <a class=\\\'black-link\\\' href=\\\'mailto:[email protected]\\\'>[email protected]</a>\' | t) | trustedHtml" ></p> <div class="right tw:relative"> <div class="help-text left"> {{ \'Следите за обновлениями Дискурса в социальных сетях\' | t }} </div> <div class="social-buttons"> <a data-crawlable-off href="https://vk.com/discoursio" target="_blank" rel="noopener noreferrer" aria-label="VK" data-tooltip="{{ \'VK Дискурса\' | t }}" ><i class="fab fa-vk"></i ></a> <a data-crawlable-off href="https://facebook.com/discoursio" target="_blank" rel="noopener noreferrer" aria-label="Facebook" data-tooltip="{{ \'Наш Facebook\' | t }}" ><i class="fab fa-facebook-f"></i ></a> <a data-crawlable-off href="https://instagram.com/discours.io" target="_blank" rel="noopener noreferrer" aria-label="Instagram" data-tooltip="{{ \'Наш Instagram\' | t }}" ><i class="fab fa-instagram" style="font-weight: 900"></i ></a> <a data-crawlable-off href="https://youtube.com/@discoursio" target="_blank" rel="noopener noreferrer" aria-label="{{ \'Канал Discours на YouTube\' | t }}" data-tooltip="{{ \'YouTube Дискурса\' | t }}" ><i class="fab fa-youtube"></i ></a> <a data-crawlable-off href="https://t.me/discoursio" target="_blank" rel="noopener noreferrer" aria-label="Telegram" data-tooltip="{{ \'Наш Telegram\' | t }}" ><i class="fab fa-telegram-plane"></i ></a> <a href="/#subscribe" view-modal="#subscribe-modal" aria-label="{{ \'Подписаться по email\' | t }}" data-tooltip="{{ \'Рассылка Дискурса\' | t }}" ><i class="fas fa-envelope"></i ></a> </div> </div> </div> <div class="tw:gutter-half tw:w-full tw:md:w-6/12"> <yandex-map class="ymaps-container" center="[55.760246, 37.641351]" zoom="15"> <ymap-marker coordinates="[55.760246, 37.641351]"></ymap-marker> </yandex-map> </div> </div> </div> </div> </div>'),e.put("/pages/about/dogma.html",'<update-meta class="black-link" title="{{ \'Догма открытой редакции\' | t }}"></update-meta> <update-meta class="black-link" name="description" content="{{ \'Профессиональные и этические принципы открытой редакции Дискурса: свобода слова, факты, независимость и ответственность перед читателями\' | t }}" ></update-meta> <update-meta class="black-link" name="keywords" content="{{ \'Discours.io, догма открытой редакции, редакционные принципы, этический кодекс, журналистские стандарты\' | t }}" ></update-meta> <update-meta class="black-link" property="og:title" content="{{ \'Догма открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" property="og:description" content="{{ \'Профессиональные и этические принципы открытой редакции Дискурса\' | t }}" ></update-meta> <update-meta property="og:image" content="/images/dogma.jpg"></update-meta>
1 <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="821"></update-meta> <div class="ds-container open-post about-post tw:mt-5"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12"> <h1 class="h2 black-bg-heading black-bg-heading-margin"> <span class="wrapped">{{ \'Догма открытой редакции\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top"> <p> {{ \'Дискурс —\' | t }} <a href="/manifest" target="_self">{{ \'открытый журнал\' | t }}</a> {{ \'с горизонтальной редакцией. У нас нет хозяев, главреда и тайных акционеров, мы не следуем указаниям цензоров: судьбу каждого материала авторы сообщества решают прямым голосованием. Сообщество\' | t }} <a href="/authors" target="_self">{{ \'объединяет\' | t }}</a> {{ \'сотни людей самых разных взглядов и профессий — ученых и рабочих, художников и программистов, врачей и журналистов из десятков городов и стран. Мы часто бываем не согласны друг с другом, но в работе над журналом следуем общим профессиональным принципам. Эта догма — то, что каждый из нас обещает себе, и то, что мы обещаем каждому, кто нас читает.\' | t }} </p> <ol style="padding-inline-start: 15px"> <li> <p><span class="lead">{{ \'Служим общественному благу.\' | t }}</span> {{ \'Нас объединяют принципы свободы слова, человеческого достоинства и профессиональной независимости. Журналистика — общественное дело. Её задача — не обслуживать интересы государств, олигархов и корпораций, а исследовать и надежно документировать происходящее, называть вещи своими именами, защищать человеческое достоинство, помогать людям слышать друг друга и лучше понимать мир.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Работаем для читателей — и вместе с ними.\' | t }}</span> {{ \'Мы трудимся для людей, которые нас читают, и действуем только в их интересах. Мы уважаем читателей, ценим время и внимание друг друга, не держим других за наивных и не упрощаем сложное до удобных схем. Читатель открытого журнала — не потребитель готовых мнений, а равный участник разговора: каждый может дополнять материалы знаниями и опытом, проверять факты, вносить возражения и уточнения.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'На первое место ставим факты.\' | t }}</span> {{ \'Понимая ответственность перед аудиторией, мы выбираем надежные источники и компетентных экспертов, ищем независимые подтверждения свидетельств, собираем детали, контекст и живые голоса, чтобы каждый мог сделать свои выводы. Все утверждения, которые мы делаем, подтверждаются фактами, документами, мнениями экспертов и ссылками Ð
1½Ð° первоисточники — так, чтобы читатели могли проверить сами.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Не боимся запретных тем.\' | t }}</span> {{ \'У нас нет цензуры, идеологических рамок и тем, о которых «сейчас не время». Мы не прячем неудобные факты, рассказываем правду, даже когда она горька, и говорим о том, что важно, даже если об этом принято молчать. Свобода слова не отменяет проверки фактов, уважения к людям и ответственности за последствия.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Не гонимся за повесткой.\' | t }}</span> {{ \'Авторы самиздата не переписывают новости, не заманивают кликбейтом, не торгуют тревогой и сенсациями. Мы выбираем темы, которым в новостной гонке достается незаслуженно мало внимания. Нам интереснее смотреть глубже сиюминутных фактов: исследовать реальность, разбираться в деталях, анализировать причины и последствия, искать выходы и решения, показывать, что можно делать уже сейчас, и создавать материалы, которые не потеряют смысла завтра.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Даем возможность высказаться всем.\' | t }}</span> {{ \'Мы выслушиваем все заинтересованные стороны, но не присоединяемся ни к чьему лагерю. Каждый, кого касается публикация, вправе ответить и быть услышанным. К любым властям, партиям, людям, компаниям — и к собственным убеждениям — мы относимся с одинаковым трезвым скептицизмом.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Аргументы важнее статуса.\' | t }}</span> {{ \'Известность, должности и регалии Ð
1½Ðµ дают привилегий — дебютный текст мы оцениваем так же внимательно, как статью известного автора. Каждый имеет право предлагать, возражать и аргументировать свою позицию. Все решения в открытом журнале рождаются из обсуждений, в которых взвешиваются аргументы, а не статусы и симпатии.\' | t }}</p> </li> <li> <p><span class="lead">{{ \'Принцип «не навреди».\' | t }}</span> {{ \'Мы защищаем безопасность, бережем частную жизнь и достоинство авторов, героев, источников и собеседников. Человек, доверивший нам свою историю, — не сырьё для текста. Мы не искажаем слова, соблюдаем договоренности, храним анонимность и никогда не используем доверие во вред людям.\' | t }}</p> </li> <li> <p> <span class="lead">{{ \'Всегда исправляем ошибки.\' | t }}</span> {{ \'Никто не безгрешен, иногда и мы ошибаемся. Ответственность в том, что признавать и исправлять ошибки. Заметили ошибку — отправьте\' | t }} <a href="/guide#editing" target="_self">{{ \'ремарку\' | t }}</a> {{ \'автору или напишите на\' | t }} <a href="mailto:[email protected]" rel="noopener noreferrer" target="_blank">[email protected]</a>{{ \': мы благодарны каждому, кто помогает журналу быть точнее.\' | t }} </p> </li> <li> <p><span class="lead">{{ \'Делаем журнал вместе.\' | t }}</span> {{ \'Каждый материал журнала проходит через открытую редакцию — обсуждение, голосование и взаимную проверку. Каждый может помочь другому сделать текст точнее, яснее и сильнее, а автор может не принимать изменения, которые делают материал хуже. Финальная публикация — общая работа и общая ответственность.\' | t }}</p> </li> </ol> <div class="tw:mt-section"> <p>{{ \'Как и всё в открытой редакции, эта догма не высечена в камне: мы обсуждаем её и развиваем вместе. Пока эти принципы в силе, это наш обет, который мы держим, каждый день стараясь делать общий журнал лучше.\' | t }}</p> <p> {{ \'Догма — часть общего свода самиздата: в\' | t }} <a href="/manifest" target="_self">{{ \'манифесте\' | t }}</a> {{ \'— зачем существует Дискурс, в\' | t }} <a href="/constitution" target="_self">{{ \'конституции\' | t }}</a> {{ \'— права каждого автора, в\' | t }} <a href="/guide" target="_self">{{ \'гайде\' | t }}</a> {{ \'— как устроена открытая редакция, в\' | t }} <a href="/principles" target="_self">{{ \'принципах сообщества\' | t }}</a> {{ \'— как мы сотрудничаем друг с другом.\' | t }} </p> <p> {{ \'Если вы заметили отступ от этих принципов — напишите на\' | t }} <a href="mailto:[email protected]" rel="noopener noreferrer" target="_blank">[email protected]</a>{{ \'. Мы отвечаем на каждое письмо. А если вам есть что сказать —\' | t }} <a href="/#post" view-modal="#become-author-modal">{{ \'присоединяйтесь\' | t }}</a>{{ \': горизонтальная редакция открыта для каждого.\' | t }} </p> </div> </div> </div> </div>'),e.put("/pages/about/guide.html","<update-meta title=\"{{ 'Как устроен Дискурс' | t }}\"></update-meta> <update-meta name=\"description\" content=\"{{ 'Горизонтальная редакция: как работает открытый журнал' | t }}\" ></update-meta> <update-meta name=\"keywords\" content=\"{{ 'Discours.io, правила, terms of use' | t }}\"></update-meta> <update-meta property=\"og:title\" content=\"{{ 'Как устроен Дискурс' | t }}\"></update-meta> <update-meta property=\"og:description\" content=\"{{ 'Горизонтальная редакция: как работает открытый журнал' | t }}\" ></update-meta> <update-meta property=\"og:image\" content=\"/images/guide.jpg\"></update-meta>
1 <update-meta property=\"og:image:width\" content=\"1200\"></update-meta> <update-meta property=\"og:image:height\" content=\"941\"></update-meta> <div class=\"ds-container open-post tw:mt-5\"> <div class=\"tw:row-flex\"> <div class=\"tw:gutter-half tw:w-full tw:sm:w-7/12\"> <h1 class=\"h3 black-bg-heading black-bg-heading-margin\"> <span>{{ 'Как устроен Дискурс' | t }}</span> </h1> </div> <div class=\"tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 post-body tw:mt-section\"> <p ng-bind-html=\"('Дискурс — независимый журнал о культуре, науке, искусстве и обществе с&amp;nbsp;<a href=\\'/manifest\\'>открытой редакцией</a>. У&amp;nbsp;нас нет главного редактора, инвестора и&amp;nbsp;вообще никого, кто бы принимал единоличные решения. Вместо традиционных иерархий Дискурс основан на принципах прямой демократии: в нашем горизонтальном сообществе все редакционные вопросы решаются открытым&amp;nbsp;голосованием авторов&amp;nbsp;журнала. Вот&amp;nbsp;как это&amp;nbsp;работает.' | t) | trustedHtml\" ></p> <h3 class=\"tw:mt-6 tw:mb-4\"> <strong>{{ 'Как устроен сайт Дискурса' | t }}</strong> </h3> <p>{{ 'Дискурс состоит из четырех основных разделов:' | t }}</p> <ul> <li> <p> <a href=\"/articles/all\">{{ 'Статьи' | t }}</a>&#160;&#8212; {{ 'здесь публикуются исследования, обзоры, эссе, интервью, репортажи, аналитика и другие материалы о культуре, науке, искусстве и обществе.' | t }} </p> </li> <li> <p> <a href=\"/expo/image\">{{ 'Искусство' | t }}</a>&#160;&#8212; {{ 'здесь представлены художественные произведения: литература, живопись, музыка, фотографии, видео. Этот раздел помогает прозвучать новому искусству, которое создают российские художники, писатели, режиссёры и музыканты.' | t }} </p> </li> <li> <p> <a href=\"/events\">{{ 'События' | t }}</a>&#160;&#8212; <span ng-bind-html=\"('в этом разделе публикуются самые важные, по мнению редакции, культурные события России — выставки, лекции, концерты, кинопоказы, фестивали, художественные и политические акции. Напишите нам на <a href=\\'mailto:[email protected]\\'>почту</a>, если вы хотите разместить объявление. Мы делаем это на безвозмездной основе.' | t) | trustedHtml\" ></span> </p> </li> <li> <p> <a href=\"/feed?editorial\" ng-if=\"$ctrl.isStaff\">{{ 'Редакция' | t }}</a ><a ng-if=\"!$ctrl.isStaff\" href=\"/#post\" view-modal=\"#become-author-modal\" >{{ 'Редакция' | t }}</a >&#160;&#8212; {{ 'это внутренний раздел, где появляются новые материалы, которые присылают в редакцию. Здесь авторы обсуждают, редактируют и оценивают публикации, определяя таким образом содержание журнала.' | t }} </p> </li> </ul> <p ng-bind-html=\"('Материалы в&amp;nbsp;Дискурсе объединяются по&amp;nbsp;<b>темам</b> —&amp;nbsp;ключевым словам, которые располагаются в&amp;nbsp;конце материалов и&amp;nbsp;связывают материалы по&amp;nbsp;жанрам (например, <a href=\\'https://discours.io/tags/interview\\'>интервью</a>, <a href=\\'https://discours.io/tags/reportage\\'>репортажи</a>, <a href=\\'https://discours.io/tags/essay\\'>эссе</a>, <a href=\\'https://discours.io/tags/likbez\\'>ликбезы</a>), по&amp;nbsp;тематике (<a href=\\'https://discours.io/tags/cinema\\'>кино</a>, <a href=\\'https://discours.io/tags/philosophy\\'>философия</a>, <a href=\\'https://discours.io/tags/history\\'>история</a>, <a href=\\'https://discours.io/tags/absurdism\\'>абсурдизм</a>, <a href=\\'https://discours.io/tags/sex\\'>секс</a> и т.д.) или в&amp;nbsp;серии (как «<a href=\\'https://discours.io/tags/zakony-mira\\'>Законы мира</a>» или «<a href=\\'https://discours.io/tags/za-liniey-mannergeyma\\'>За&amp;nbsp;линией Маннергейма</a>»). Темы объединяют сотни публикаций, помогают ориентироваться в&amp;nbsp;журнале и&amp;nbsp;следить за&amp;nbsp;интересными материалами.' | t) | trustedHtml\" ></p> <section ng-if=\"$ctrl.isStaff\"> <p ng-bind-html=\"('Самая важная кнопка в Дискурсе — «<a href=\\'/#post\\' view-modal=\\'#become-author-modal\\'>предложить публикацию</a>». Нажав на неё, вы сможете поделиться своей работой. Кстати, вот несколько полезных <a href=\\'/articles/theory/how-to-write-a-good-article\\'>советов</a> о том, как сделать свой текст лучше.' | t) | trustedHtml\" ></p> </section> <section ng-if=\"$ctrl.isStaff\"> <p ng-bind-html=\"('У каждого автора есть <a href=\\'/profile\\'>профиль</a>. В нем отображаются ваши работы и информация, которую вы о себе укажете. Заполнить профиль (рассказать о себе, поставить портрет, указать ссылки на свои аккаунты в соцсетях и т.д.) можно в <a href=\\'/edit/profile\\'>настройках</a>.' | t) | trustedHtml\" ></p> <p> {{ 'В профиле каждого автора есть' | t }} <b>{{ 'реноме' | t }}</b> — {{ 'это показатель заслуг автора перед человечеством и его вклада в Дискурс. Реноме отражает отношение участников сообщества к деятельности автора и служит средством самоуправления. Если вы цените участие другого автора, считаете, что он делает журнал лучше, и хотите сказать ему «спасибо», поблагодарить его можно, повысив его реноме. Дл
1я этого нужно нажать на плюс в его профиле. И наоборот — если автор поступает плохо, его реноме рискует оказаться в глубоком минусе и решением сообщества он может быть исключен из редакции. Впрочем, пока таких случаев не было.' | t }} </p> </section> <section ng-if=\"!$ctrl.isStaff\"> <h3 class=\"tw:mt-section tw:mb-4\"> <strong>{{ 'Как стать автором журнала' | t }}</strong> </h3> <p ng-bind-html=\"('Дискурс объединяет журналистов, активистов, музыкантов, художников, фотографов, режиссеров, философов, ученых и других замечательных людей. Каждый может <a href=\\'/#post\\' view-modal=\\'#become-author-modal\\'>прислать</a> свой материал в журнал. Формат и тематика не имеют значения, единственное, что важно — <a href=\\'/articles/theory/how-to-write-a-good-article\\'>хороший</a> ли материал. Если сообщество поддержит вашу публикацию, она выйдет в журнале и станет доступна тысячам наших читателей.' | t) | trustedHtml\" ></p> </section> <h3 id=\"voting\" class=\"tw:mt-section tw:mb-4\"> <strong>{{ 'Как проходит голосование' | t }}</strong> </h3> <p> {{ 'Все присылаемые в Дискурс материалы попадают в' | t }} <b>{{ '\"Редакцию\"' | t }}</b>. {{ 'Это внутренний раздел сайта, где участники сообщества решают, что будет опубликовано в Дискурсе. Как только работа получает одобрение как минимум пятерых авторов открытой редакции, она немедленно публикуется в журнале. Если же материал набирает более 20% голосов «против», он не выходит и может быть отправлен на доработку. Жестких сроков рассмотрения материалов у нас нет, иногда это занимает час, иногда месяц, обычно — несколько дней.' | t }} </p> <section ng-if=\"!$ctrl.isStaff\"> <p> {{ 'Как только сообщество поддержит публикацию, вы получите приглашение в интернет-редакцию и сможете голосовать за новые материалы.' | t }} </p> </section> <h3 id=\"editing\" class=\"tw:mt-section tw:mb-4\"> <strong>{{ 'Как мы делаем тексты друг друга лучше' | t }}</strong> </h3> <p> {{ 'Дискурс — журнал с совместным редактированием. Совершенствовать тексты нам помогает' | t }} <b>{{ 'система ремарок' | t }}</b>. {{ 'Вы можете выделить часть текста в любой статье и оставить к ней замечание, вопрос или предложение — автор текста получит совет на почту и сможет его учесть. Так мы устраняем опечатки, неточности и советуем друг другу, как сделать тексты качественнее и интереснее.' | t }} </p> <p> {{ 'Среди участников сообщества есть профессиональные редакторы, которые помогают авторам делать тексты лучше. Если вашему материалу потребуется доработка, они помогут отредактировать текст, подобрать иллюстрации, придумать заголовок и красиво сверстать публикацию.' | t }} {{ 'Если вы хотите обсудить текст, прежде чем загрузить материал в интернет-редакцию — разместите его в google-документе, откройте доступ к редактированию по ссылке и напишите нам на' | t }} <a href=\"mailto:[email protected]\">[email protected]</a>. </p> <p> {{ 'Если у вас возникают трудности с тем, чтобы подобрать к своему материалу иллюстрации, тоже пишите на' | t }} <a href=\"mailto:[email protected]\">{{ 'почту' | t }}</a> — {{ 'наши коллеги-художники могут вам помочь.' | t }} <a name=\"together\" ng-if=\"!$ctrl.isStaff\"></a> </p> <section ng-if=\"$ctrl.isStaff\">
1 <h3 id=\"begood\" class=\"tw:mt-section tw:mb-4\"> <strong>{{ 'Как у нас принято себя вести' | t }}</strong> </h3> <p> {{ 'Читайте, оценивайте, комментируйте статьи других авторов, делитесь интересными публикациями в соцсетях, публикуйте свои статьи и художественные работы. Помните, что Дискурс — это ваш журнал, и именно вы, голосуя за публикации, решаете каким он будет. Если вы считаете, что работа достойна публикации в журнале, ставьте плюс, а если думаете, что лучше бы её не было, вручите минус. Пожалуйста, руководствуйтесь лишь качеством материала при его оценке. Не ставьте материалу плюс/минус только потому, что вам нравится/не нравится его автор. Уважайте собеседника, даже если он вам лично не импонирует: только так получаются конструктивные дискуссии. И последнее: постарайтесь не нарушать законы Российской Федерации без крайней на то необходимости.' | t }} <a name=\"together\" ng-if=\"$ctrl.isStaff\"></a> </p> </section> <h3 class=\"tw:mt-section tw:mb-4\"> <strong>{{ 'Что сообщество дает авторам' | t }}</strong> </h3> <ul> <li> <p> <strong>{{ 'Право определять, каким будет журнал' | t }}</strong>. {{ 'Дискурс — это общественная институция, созданная людьми и ради людей, функционирующая на условиях прямой демократии. Авторы публикуют статьи и художественные проекты, участвуют в обсуждениях, голосуют за работы коллег и таким образом вносят свой вклад в развитие проекта, определяя содержание и направление журнала.' | t }} </p> </li> <li> <p> <strong>{{ 'Возможность обратиться к широкой аудитории' | t }}</strong>. {{ 'Дискурс читают десятки тысяч людей, и с каждым днем их становится больше.' | t }} </p> </li> <li> <p> <strong>{{ 'Поддержка редакции' | t }}</strong>. {{ 'Дискурс предоставляет авторам аккредитацию на мероприятия, базу контактов, юридическую поддержку, ознакомление с книжными, кино- и музыкальными новинками до их выхода в свет. Если что-то из этого вам понадобится, пишите на почту' | t }} <a href=\"mailto:[email protected]\">[email protected]</a>&nbsp;— {{ 'поможем.' | t }} </p> <section ng-if=\"$ctrl.editorialLinks.booksList\"> <p> {{ 'Кстати, вот обновляющийся' | t }} <a ng-href=\"{{ $ctrl.editorialLinks.booksList }}\" rel=\"noopener noreferrer\" >{{ 'список книжных новинок' | t }}</a >, {{ 'которые можно взять для обзора, конспекта, рецензии или публикации понравившегося фрагмента.' | t }} </p> </section> </li> <li> <p> <strong>{{ 'Пресс-карты для корреспондентов' | t }}</strong>. {{ 'Три опубликованные статьи позволяют авторам Дискурса получить официальные удостоверения журналистов (пресс-карты) на следующий год. Пресс-карты удостоверяют, что вы журналист и можете пользоваться всеми теми правами, которые гарантирует Закон о СМИ. Кроме того, многие культурные институции (музеи, галереи и др.) предоставляют журналистам право свободного входа.' | t }} </p> </li> <li> <p> <strong>{{ 'Помощь сотен специалистов в разных областях' | t }}</strong>. {{ 'В основе Дискурса лежит идея совместного редактирования. Участники редакционного сообщества — несколько сотен журналистов, исследователей, художников, литераторов из разных стран — изучают материалы друг друга до публикации и помогают сделать их качественнее и интереснее. Так, в редакции нередко складываются творческие союзы: например, авторов текстов и художников, создающих для них иллюстрации.' | t }} </p> </li> <li> <p> <strong>{{ 'Пространство общения полное выдающихся людей' | t }}</strong>. {{ 'Дискурс — большое живое сообщество интеллектуалов, разбросанных по всему земному шару. Вступив в редакцию, вы сможете познакомиться со множеством интересных людей, которые определяют повестку завтрашнего дня, вдохновляют окружающих, создают новое и изучают старое, ищут знания и готовы ими делиться, чтобы менять мир в соответствии со своими идеалами.' | t }} </p> </li> </ul>
1 <h3 class=\"tw:mt-section tw:mb-4\"> <strong>{{ 'Как быть в курсе' | t }}</strong> </h3> <p ng-bind-html=\"('За свежими публикациями Дискурса можно следить не только на сайте, но и на страницах в <a href=\\'https://facebook.com/discoursio/\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>Фейсбуке</a>, <a href=\\'https://vk.com/discoursio\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>ВКонтакте</a> и <a href=\\'https://t.me/discoursio\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>Телеграме</a>. А ещё раз в месяц мы отправляем <a href=\\'/#subscribe\\' view-modal=\\'#subscribe-modal\\'>почтовую рассылку</a> с дайджестом лучших материалов.' | t) | trustedHtml\" ></p> <p ng-if=\"$ctrl.editorialLinks.telegramChat\"> {{ 'Оперативную связь с коллегами по горизонтальной редакции мы поддерживаем в' | t }} <a ng-href=\"{{ $ctrl.editorialLinks.telegramChat }}\" rel=\"noopener noreferrer\" >{{ 'Telegram-чате' | t }}</a >. {{ 'Там мы разрабатываем темы новых публикаций, обсуждаем материалы, которые присылают в редакцию, советуемся и думаем о том, как сделать журнал ещё лучше. Присоединяйтесь!' | t }} </p> <p ng-bind-html=\"('Если вы хотите сотрудничать, что-то обсудить или предложить — пожалуйста, пишите на <a href=\\'mailto:[email protected]\\'>[email protected]</a>. Мы обязательно ответим.' | t) | trustedHtml\" ></p> </div> </div> </div>"),e.put("/pages/about/help-us.html",'<update-meta title="{{ \'Поддержать Дискурс\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Внесите вклад в самиздат. Поддержите Дискурс — независимое некоммерческое издание, которое работает только для вас и существует на наши пожертвования. Вы — опора открытой редакции\' | t }}" ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, помощь, благотворительность\' | t }}" ></update-meta> <update-meta property="og:title" content="{{ \'Внесите вклад в самиздат\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Поддержите Дискурс — независимое некоммерческое издание с открытой редакцией, которое честно рассказывает правду, работает только для вас и существует на наши пожертвования\' | t }}" ></update-meta> <update-meta property="og:image" content="/images/donate/donate-discours-big.jpg"></update-meta> <div class="help-us-page-wrapper"> <div class="ds-container"> <div class="tw:row-flex"> <div class="help-us-content-col tw:gutter-half tw:w-full tw:md:w-6/12 tw:md:ml-offset-2"> <div class="help-us"> <div class="help-us__wrapper"> <div class="help-us__body"> <h1 class="help-us__title"> <b>{{ \'Внесите вклад в самиздат\' | t }}</b> </h1> <h2 class="help-us__subtitle"> {{ \'Поддержите независимую культуру и гражданскую журналистику\' | t }} </h2> <div class="help-us__content"> <div class="help-us__image"> <picture> <source srcset=" /images/donate/donate-by-danya.jpg, /images/donate/donate-by-danya.jpg 2x " media="(min-width: 992px)" /> <img srcset="/images/donate/donate-by-danya-mobile.jpg" alt="{{ \'Свобода слова\' | t }}" loading="lazy" /> </picture> </div> <div class="help-us__text"> {{ \'Дискурс — открытая медиаплатформа с горизонтальной редакцией, которая выступает трибуной для независимой журналистики и свободной мысли, ежедневно помогая л
1юдям рассказывать значимые истории так, чтобы они были услышаны. В эпоху беспрецедентного давления на независимые медиа и свободу слова мы противостоим наступающему невежеству и пропаганде, делая всё возможное, чтобы продолжать говорить правду и честно писать о важном.\' | t }} </div> <div class="help-us__quote"> <b> {{ \'Мы верим, что с помощью слова, идей и знаний можно изменить не только разговоры вокруг, но вместе с ними и мир\' | t }} </b> </div> <div class="help-us__text" ng-bind-html="(\'Создавая пространство, основанное на <a href=\\\'https://discours.io/manifest\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>принципах</a> коллективной журналистики, свободы слова и прямой демократии, Дискурс распространяет знания и критическое мышление, рассказывает о людях и идеях, меняющих мир, усиливает голоса тех, кто не представлен в традиционных медиа, и прямо говорит о том, о чем другие молчат.\' | t) | trustedHtml" ></div> <div class="help-us__text"> {{ \'Дискурс — некоммерческое издание и существует лишь благодаря пожертвованиям, опираясь исключительно на вашу поддержку. Мы работаем только для вас и с вашей помощью.\' | t }} </div> <div class="help-us__quote"> <b>{{ \'Помогите Дискурсу выжить — станьте меценатом самиздата.\' | t }}</b> </div> <div class="help-us__text"> {{ \'Вы поможете прозвучать значимым историям, которые не вышли бы без вашей помощи, внесёте неоценимый вклад в развитие независимой культуры и откроете себе и миллионам других людей доступ к неподцензурному искусству и свободной гражданской журналистике.\' | t }} </div> </div> <hr class="help-us__hr" /> <h1 class="help-us__title" id="purpose"> <b>{{ \'Ваша поддержка поможет самиздату\' | t }}</b> </h1> <div class="help-us__content"> <details class="help-us__spoiler help-us__spoiler--1"> <summary> {{ \'Оставаться оплотом гуманизма и гражданской журналистики\' | t }} </summary> <p> {{ \'Самиздат ежедневно помогает людям рассказывать потрясающие истории, делиться опытом, знаниями, разбираться в сложных научных феноменах и создавать мультимедийные лонгриды на важные для понимания мира темы. Благодаря вам журнал и дальше сможет публиковать тщательно проработанные статьи, поддерживать независимых авторов и усиливать альтернативные голоса.\' | t }} </p> </details> <details class="help-us__spoiler help-us__spoiler--2"> <summary>{{ \'Создавать новые уникальные публикации\' | t }}</summary> <p ng-bind-html="(\'Ваша помощь позволит публиковать научно-популярные <a href=\\\'https://discours.io/articles/theory\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>исследования</a>, переводы важных зарубежных статей, ликбезы по психологии, философии и антропологии, исторические <a href=\\\'https://discours.io/articles/culture\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>гиды</a> о феноменах культуры, репортажи, журналистские расследования, <a href=\\\'https://discours.io/tags/interview\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>интервью</a> с учеными, деятелями искусства и активистами, аналитические <a href=\\\'https://discours.io/articles/social\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>фичеры</a> об изменениях в обществе, передовой <a href=\\\'https://discours.io/articles/chapter
1s\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>нон-фикшен</a>, остросоциальную <a href=\\\'https://discours.io/expo/image\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>графику</a>, независимую <a href=\\\'https://discours.io/expo/music\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>музыку</a> и новаторскую современную <a href=\\\'https://discours.io/expo/literature\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>литературу</a>.\' | t) | trustedHtml" ></p> </details> <details class="help-us__spoiler help-us__spoiler--3"> <summary>{{ \'Запускать масштабные просветительские спецпроекты\' | t }}</summary> <p> {{ \'Благодаря вам Дискурс сможет создавать документальные фильмы-расследования о социальных проблемах, фотопроекты из российской глубинки и центров мировой эмиграции, подкасты с выдающимися учеными, музыкантами и артистами, печатные альманахи с избранными материалами самиздата и образовательные медиакурсы, развивающие ответственную журналистику в России.\' | t }} </p> </details> <details class="help-us__spoiler help-us__spoiler--4"> <summary>{{ \'Развивать свободную медиаплатформу\' | t }}</summary> <p> {{ \'С вашей поддержкой мы продолжим разрабатывать мультимедийные инструменты для сотрудничества, которые позволят авторам и читателям взаимодействовать напрямую и участвовать в создании коллективной журналистики — делиться знаниями и экспертизой, проводить совместную редактуру и публичный фактчек, участвовать в открытых дискуссиях, вместе создавать новые публикации и формировать живую повестку издания.\' | t }} </p> </details> <details class="help-us__spoiler help-us__spoiler--5"> <summary>{{ \'Противостоять незнанию, мракобесию и пропаганде\' | t }}</summary> <p> {{ \'Самиздат, несмотря ни на что, продолжит бороться за свободу слова, сохранять высокие стандарты профессиональных медиа и доступность информации для всех. У нас громадные планы — поддерживать независимых авторов и ученых, развивать платформу коллаборативной журналистики, рассказывать еще больше историй, которые помогают понимать мир. Но без вас ничего не получится.\' | t }} </p> </details> </div> <hr class="help-us__hr" /> <h1 class="help-us__title" id="specials"> <b>{{ \'Что мы делаем с вашей помощью\' | t }}</b> </h1> <h2 class="help-us__faq-subtitle"> {{ \'Спецпроекты самиздата, созданные благодаря поддержке читателей\' | t }} </h2> <div class="help-us__content"> <div class="help-us__best-articles"> <donate-best-article ng-repeat="article in $ctrl.bestArticles | limitTo:$ctrl.visibleProjectsCount track by (article._id || $index)" article-data="article" ></donate-best-article> </div> <button type="button" class="help-us__show-more-btn" ng-click="$ctrl.loadMoreProjects()" ng-if="$ctrl.visibleProjectsCount < $ctrl.bestArticles.length" > {{ \'Показать ещё\' | t }} </button> </div> </div> </div> </div> </div> </div> </div> <div class="help-us__faq"> <div class="ds-container"> <div class="tw:row-flex"> <div class="help-us-content-col tw:gutter-half tw:w-full tw:md:w-6/12 tw:md:ml-offset-2"> <h1 class="help-us__title" id="faq"> <b>{{ \'Частые вопросы\' | t }}</b> </h1> <h2 class="help-us__faq-subtitle">{{ \'Как и зачем поддерживать самиздат\' | t }}</h2> <div class="help-us__content"> <donation-faq></donation-faq> </div> </div> </div> </div> </div>
1 <div class="help-us__aside-donate" ng-if="!$ctrl.isMobile"> <div class="help-us__donate-wrapper"> <div class="help-us__donate-form" ng-class="{\'is-moving\': $root.stickyMenuStyle }"> <donate-form id="donate-form"></donate-form> </div> </div> </div> </div> <div class="ds-container"> <div class="tw:row-flex"> <div class="help-us-content-col tw:gutter-half tw:w-full tw:md:w-6/12 tw:md:ml-offset-2"> <div class="help-us"> <div class="help-us__wrapper"> <div class="help-us__body"> <div class="help-us__thanks"> <div class="help-us__flying-image help-us__flying-image--right"> <img src="/images/donate/planning.png" alt="{{ \'Планерка\' | t }}" loading="lazy" /> </div> <h2 class="help-us__title costyl-from-kostya"> <b >{{ \'Спасибо, что вы с нами. Без вас мы не справимся, вместе — сможем изменить мир.\' | t }}</b > </h2> </div> </div> </div> </div> </div> </div> </div> <div class="mobile-donate-control" ng-if="$ctrl.isTabletOrSmaller"> <button type="button" ng-click="$ctrl.openMobileDonate()">{{ \'Поддержать самиздат\' | t }}</button> </div> <div class="mobile-donate-block" ng-if="$ctrl.isMobileDonateOpen"> <button type="button" class="mobile-donate-close close tw:cursor-pointer" ng-click="$ctrl.closeMobileDonate()" aria-label="{{ \'Закрыть\' | t }}" > <i class="fas fa-times"></i> </button> <donate-form id="donate-form"></donate-form> </div>'),e.put("/pages/about/manifest.html","<update-meta title=\"{{ 'Манифест Дискурса' | t }}\"></update-meta> <update-meta name=\"description\" content=\"{{ 'Манифест самиздата: принципы и миссия открытого журнала с горизонтальной редакцией' | t }}\" ></update-meta> <update-meta name=\"keywords\" content=\"{{ 'Discours.io, журнал, о проекте, манифест, что это?' | t }}\" ></update-meta> <update-meta property=\"og:title\" content=\"{{ 'Манифест Дискурса' | t }}\"></update-meta> <update-meta property=\"og:description\" content=\"{{ 'Манифест самиздата: принципы и миссия открытого журнала с горизонтальной редакцией' | t }}\" ></update-meta> <update-meta property=\"og:image\" content=\"/images/participation.png\"></update-meta> <update-meta property=\"og:image:width\" content=\"1200\"></update-meta> <update-meta property=\"og:image:height\" content=\"630\"></update-meta> <div class=\"ds-container open-post tw:mt-5\"> <div class=\"tw:row-flex\"> <div class=\"tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12\"> <h1 class=\"h2 black-bg-heading black-bg-heading-margin\"> <span>{{ 'Манифест' | t }}</span> </h1> </div> <div class=\"tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top\"> <p> {{ 'Дискурс — независимый художественно-аналитический журнал с горизонтальной редакцией, основанный на принципах свободы слова, прямой демократии и совместного редактирования. Дискурс создается открытым медиасообществом ученых, журналистов, музыкантов, писателей, предпринимателей, философов, инженеров, художников и специалистов со всего мира, объединившихся, чтобы вместе делать общий журнал и объяснять с разных точек зрения мозаичную картину современности.' | t }} </p> <p> {{ 'Мы пишем о культуре, науке и обществе, рассказываем о новых идеях и современном искусстве, публикуем статьи, исследования, репортажи, интервью людей, чью прямую речь стоит услышать, и работы художников из разных стран — от фильмов и музыки до живописи и фотографии. Помогая друг другу делать публикации качественнее и общим голосованием выбирая лучшие материалы для журнала, мы создаем новую горизонтальную журналистику, чтобы честно рассказывать о важном и интересном.' | t }} </p> <p> {{ 'Редакция Дискурса открыта для всех: у нас нет цензуры, запретных тем и идеологических рамок.' | t }} <a href=\"/#post\" view-modal=\"#become-author-modal\">{{ 'Каждый может предложить материал' | t }}</a> {{ 'в журнал и присоединиться к горизонтальной редакции. Предоставляя трибуну для независимой журналистики и художественных проектов, мы помогаем людям рассказывать свои истории так, чтобы оÐ
1½Ð¸ были услышаны. Мы убеждены: чем больше голосов будет звучать на Дискурсе, тем громче в полифонии мнений будет слышна истина.' | t }} </p> </div> </div> <div id=\"participation\" class=\"tw:mb-10 tw:-mt-section\"></div> <div class=\"tw:row-flex\"> <div class=\"tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12\"> <h2 class=\"h2 black-bg-heading black-bg-heading-margin\"> <span class=\"wrapped\">{{ 'Как участвовать в самиздате' | t }}</span> </h2> </div> <div class=\"tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top\"> <p ng-bind-html=\"('Дискурс <a href=\\'guide\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>создается открытым сообществом</a> энтузиастов новой независимой журналистики. Сотрудничать с журналом, помогать самиздату и участвовать в работе открытой редакции можно самыми разными способами:' | t) | trustedHtml\" ></p> <h3 class=\"not-left\"><b>{{ 'Публиковать материалы' | t }}</b></h3> <p> {{ 'Если вам есть чем поделиться с читателями,' | t }} <a href=\"/#post\" view-modal=\"#become-author-modal\">{{ 'присылайте в редакцию свои работы' | t }}</a> {{ '— статьи, художественные произведения, исследования.' | t }} <a href=\"guide#together\" rel=\"noopener noreferrer\" target=\"_blank\">{{ 'Самиздат поддерживает независимых авторов' | t }}</a>{{ ', публикующихся на общественных началах, предлагая помощь горизонтальной редакции, среду интеллектуального сотрудничества, внимание и отклик самых лучших читателей по всему миру, а также право участвовать в коллективном редактировании материалов и голосовать за публикации, определяя таким образом содержание открытого журнала.' | t }} </p> <h3 class=\"not-left\"><b>{{ 'Поддерживать проект' | t }}</b></h3> <p ng-bind-html=\"('Дискурс существует только благодаря пожертвованиям читателей. Вы наша единственная опора. Если вам нравится проект, <a href=\\'/help\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>поддержите самиздат и работу платформы</a> для независимой журналистики. Пожертвования пойдут на развитие сайта, выпуск новых материалов, оплату серверов, труда программистов, дизайнеров и редакторов, ежедневно помогающих людям со всего мира рассказывать интересные и важные истории.' | t) | trustedHtml\" ></p> <h3 class=\"not-left\"><b>{{ 'Сотрудничать с журналом' | t }}</b></h3> <p ng-bind-html=\"('Если вы хотите помогать журналу с редактурой, корректурой, иллюстрациями, переводами, версткой, подкастами, мероприятиями, фандрайзингом или как-то ещё — <a href=\\'https://forms.gle/GJ3Muqf1UjeCzyDXA\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>присоединяйтесь к открытой редакции</a>. Мы всегда рады единомышленникам и поможем найти полезное для самиздата дело по душе.' | t) | trustedHtml\" ></p> <p ng-bind-html=\"('Если вы разработчик и хотите помогать с развитием сайта Дискурса, <a href=\\'mailto:[email protected]\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>присоединяйтесь к IT-команде</a> самиздата. Открытый код платформы для независимой журналистики, а также всех наших спецпроектов и медиаинструментов находится в <a href=\\'https://github.com/Discours\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>свободном доступе на GitHub</a>.' | t) | trustedHtml\" ></p> <p ng-bind-html=\"('Если вы представляете некоммерческую организацию и хотите сделать с нами совместный проект, получить информационную поддержку или предложить другую форму сотрудничества, пишите на <a href=\\'mailto:[email protected]\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>[email protected]</a>.' | t) | trustedHtml\" ></p> <h3 class=\"not-left\"><b>{{ 'Как еще можно помочь' | t }}</b></h3> <p> <span ng-bind-html=\"('Советуйте Дискурс друзьям и знакомым. Обсуждайте и распространяйте наши публикации — все материалы открытой редакции можно читать и перепечатывать бесплатно. Подпишитесь на самиздат <a href=\\'https://vk.com/discoursio\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>ВКонтакте</a>, в <a href=\\'https://facebook.com/discoursio\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>Фейсбуке</a> и в <a href=\\'https://t.me/discoursio\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>Телеграме</a>, а также на' | t) | trustedHtml\"></span> <a href=\"/#subscribe\" view-modal=\"#subscribe-modal\">{{ 'рассылку лучших материалов' | t }}</a><span ng-bind-html=\"(', чтобы не пропустить ничего интересного. <a href=\\'https://forms.gle/9UnHBAz9Q3tjH5dAA\\' rel=\\'noopener noreferrer\\' target=\\'_blank\\'>Рассказывайте о впечатлениях</a> от материалов открытой редакции,' | t) | trustedHtml\"></span> <a href=\"/#idea\" view-modal=\"#send-idea-modal\" ng-click=\"closePartners($event)\">{{ 'делитесь идеями' | t }}</a><span ng-bind-html=\"(', интересными темами, о Ð
1ºÐ¾Ñ‚орых хотели бы узнать больше, и историями, которые нужно рассказать.' | t) | trustedHtml\"></span> </p> </div> </div> <div id=\"connection\" class=\"tw:mb-10 tw:-mt-section\"></div> <div class=\"tw:row-flex\"> <div class=\"tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12\"> <h2 class=\"h2 black-bg-heading black-bg-heading-margin\"> <span class=\"wrapped\">{{ 'Будем на связи' | t }}</span> </h2> </div> <div class=\"tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top\"> <p> {{ 'Если вы хотите предложить материал, сотрудничать, рассказать о проблеме, которую нужно осветить, сообщить об ошибке или баге, что-то обсудить, уточнить или посоветовать, пожалуйста,' | t }} <a href=\"/#idea\" view-modal=\"#send-idea-modal\" ng-click=\"closePartners($event)\">{{ 'напишите нам здесь' | t }}</a> {{ 'или на почту' | t }} <a href=\"mailto:[email protected]\" rel=\"noopener noreferrer\" target=\"_blank\">[email protected]</a>. {{ 'Мы обязательно ответим и постараемся реализовать все хорошие задумки.' | t }} </p> </div> </div> </div>"),e.put("/pages/about/principles.html",'<update-meta class="black-link" title="{{ \'Принципы сообщества открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" name="description" content="{{ \'Ценности сообщества и правила взаимодействия горизонтальной редакции\' | t }}" ></update-meta> <update-meta class="black-link" name="keywords" content="{{ \'Discours.io, сообщества, ценности, правила редакции, многоголосие, созидание\' | t }}" ></update-meta> <update-meta class="black-link" property="og:title" content="{{ \'Принципы сообщества открытой редакции\' | t }}" ></update-meta> <update-meta class="black-link" property="og:description" content="{{ \'Ценности сообщества и правила взаимодействия горизонтальной редакции\' | t }}" ></update-meta> <update-meta property="og:image" content="/images/principles.jpg"></update-meta> <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="675"></update-meta> <div class="ds-container open-post about-post tw:mt-5"> <div class="tw:row-flex" id="basics"> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12"> <h1 class="h2 black-bg-heading black-bg-heading-margin"> <span class="wrapped">{{ \'Принципы сообщества\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top"> <ol style="padding-inline-start: 15px"> <li> <p> <span class="lead">{{ \'Горизонтальность.\' | t }}</span> {{ \'Мы все разные, и это классно. Вертикалей в мире достаточно, мы — горизонтальное сообщество и ценим наши различия, потому что знаем — в них наша сила. Благодаря разнообразию сотен голосов, усиливающих друг друга, в сообществе складывается неповторимая синергия, которая помогает вместе достигать большего.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Многоголосие.\' | t }}</span> {{ \'Мы ценим свободу слова и аргументированные мнения. Предоставляя трибуну каждому, кому есть что сказать, самиздат отражает полифонию позиций, знаний и опыта, которые открывают более полную картину реальности.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Взаимопомощь.\' | t }}</span> {{ \'Мы помогаем друг другу, потому что хотим, чтобы в мире было еще больше хорошего. Обсуждая что-то, мы всегда интересуемся, чем можем помочь. В самиздате можно найти специалистов практически в любых сферах и получить поддержку от сотен людей. Благодаря коллективной экспертизе глобаÐ
1»ÑŒÐ½Ð¾Ð³Ð¾ сообщества в самиздате выходят крутейшие публикации, которыми можно вечно гордиться.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Взаимоуважение.\' | t }}</span> {{ \'Мы ценим, искренне уважаем друг друга и вместо борщевиков враждебности культивируем цветы добра, мира, знания и юмора. Нам некогда доказывать друг другу, кто круче. Гораздо приятнее сотрудничать, помогать и создавать что-то важное, интересное и полезное.\' | t }} </p> </li> <li> <i></i> <p> <span class="lead">{{ \'Созидание.\' | t }}</span> {{ \'Мы создаем, потому что любим создавать. Мы открыто делимся опытом, дарим идеи, обмениваемся мнениями и благодарим за критику, используя ее для совершенствования мастерства и саморазвития. Мы знаем, что мир не идеальное место, и делаем всё возможное, чтобы он стал лучше.\' | t }} </p> </li> </ol> </div> </div> <div class="tw:row-flex" id="communication"> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:md:w-2/12"> <h2 class="h2 black-bg-heading black-bg-heading-margin"> <span class="wrapped">{{ \'Как здесь принято себя вести\' | t }}</span> </h2> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:md:mt-content-top"> <p> {{ \'Открытая редакция объединяет сотни потрясающих людей со всего мира, которые делают крутейшие вещи. Это пространство, где доверяют, вдохновляют, исследуют и создают новое вместе. Поскольку все в сообществе очень разные, как-то мы собрались и решили зафиксировать базовые ценности открытой редакции, а заодно придумали универсальные правила взаимодействия, чтобы общение было не только плодотворным, но и приятным для всех участников сообщества.\' | t }} </p> <ol style="padding-inline-start: 15px"> <li> <p> <span class="lead">{{ \'Действуем, помогаем и делимся.\' | t }}</span> {{ \'В редакции мы создаем сами и помогаем другим создавать свои проекты — советами, делом, участием, вовлеченностью. Мы открыто делимся опытом, мнениями и идеями, потому что ценим силу сотрудничества и знаем, что идеи реализуются скорее, лучше и веселее, если над ними трудиться сообща.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Критикуем и реагируем конструктивно.\' | t }}</span> {{ \'Мы исходим из того, что каждый в сообществе имеет добрые намерения, и придерживаемся принципов доброжелательной критики, стараясь делиться друг с другом советами и общаться так, чтобы это было полезно для развития всего сообщества. Убедительная аргументация помогает и адресату, и всем участникам сообщества досконально изучить тему и глубже разобраться в проблеме.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Общаемся дружелюбно и с уважением.\' | t }}</span> {{ \'Подлинное сотрудничество и осмысленные дискуссии невозможны без вежливости и взаимоуважения. Помните, по ту сторону монитора находятся реальные люди, а любые трудности скорее решаются не агрессией, но диалогом. Не стоит кричать (даже капслоком), переходить на личности (это признак токсичности) и бросаться грубостями — так вы рискуете не только растерять доверие окружающих, но и остаться непонятым.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Благодарим за помощь.\' | t }}</span> {{ \'Благодарите коллег даже за самые, казалось бы, простые вещи. «Спасибо» не зря называют волшебным словом — на искренней благодарности держится любое подлинное сотрудничество. Поддержка воодушевляет на новые подвиги и напоминает, что мир делают прекрасным не машины, а живые люди.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Даем еще один шанс.\' | t }}</span> {{ \'Все совершают ошибки, и за один проступок не стоит вычеркивать людей из жизни. Ошибки нужны, чтобы на них учиться и делать выводы. Однако если многократно и систематически нарушать правила сообщества, наверняка можно заслужить минусы в карму от других участников и потерять доступ к сообществу.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Вместе создаем идеальную среду общения.\' | t }}</span> {{ \'Открытая редакция — это утопическое пространство обогащающей и осмысленной коммуникации. Атмосфера горизонтального сообщества складывается из действий каждого, поэтому мы действуем так, чтобы способствовать сотворчеству, коллективному познанию и развитию самиздата и нашей альтернативной интеллектуальной медиасреды.\' | t }} </p> </li> <li> <p> <span class="lead">{{ \'Помним, что всё в сообществе зависит от нас.\' | t }}</span> {{ \'Если нам чего-то не хватает, мы начинаем действовать — рассказываем об идее, находим единомышленников, готовим и запускаем проект. Так в сообществе становится на одну крутую активность больше. Так появилось наше сообщество. Так появился самиздат и все проекты открытой редакции. Чтобы в сообществе случилось что-то прекрасное, достаточно просто положить этому начало.\' | t }} </p> </li> </ol> </div> </div> </div>'),e.put("/pages/about/privacy-policy.html","<update-meta title=\"{{ 'Политика конфиденциальности' | t }}\"></update-meta> <update-meta name=\"description\" content=\"{{ 'Какие данные собирает Дискурс и как их использует' | t }}\" ></update-meta> <update-meta property=\"og:title\" content=\"{{ 'Политика конфиденциальности' | t }}\"></update-meta> <update-meta property=\"og:description\" content=\"{{ 'Какие данные собирает Дискурс и как их использует' | t }}\" ></update-meta> <div class=\"ds-container open-post\"> <div class=\"tw:row-flex\"> <div class=\"tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 tw:mt-4\"> <h1 class=\"tw:font-bold tw:mb-section\" align=\"center\"> {{ 'Политика конфиденциальности' | t }} </h1> </div> <div class=\"tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 post-body\"> <p>{{ 'Здесь описано, какие данные о вас собирает Дискурс, зачем они нужны и как их удалить.' | t }}</p> <ol style=\"list-style-type: decimal\"> <li> <p>{{ 'Сайт может собирать у пользователей следующие данные:' | t }}</p> <ol style=\"list-style-type: square\"> <li> <p>{{ 'Данные, которые пользователи сообщают о себе сами при подаче заявки, регистрации, авторизации или заполнения профиля, в том числе ФИО и контактную информацию. Конфиденциальные данные, такие как идентификатор и электронный адрес, используются для идентификации пользователя. Данные профиля, размещённые публично по желанию пользователя, которое выражается фактом их предоставления, используется для демонстрации другим пользователям той информации о себе, которую пользователь готов предоставить.' | t }}</p> </li> <li id=\"sign-in-services\"> <p>{{ 'Данные от сервиса, через который вы входите на сайт, — Google, VK, Facebook или GitHub: имя, адрес электронной почты, фото профиля и идентификатор аккаунта. Другие данные аккаунта сайт не сохраняет, а доступа к вашим письмам, контактам и доÐ
1ºÑƒÐ¼ÐµÐ½Ñ‚ам у нас нет.' | t }}</p> </li> <li> <p>{{ 'Данные, собранные автоматическим путём, такие как cookie-файлы, в том числе cookie сервиса статистики Google Analytics. Они используются для подсчёта посещаемости и улучшения работы сайта.' | t }}</p> </li> </ol> </li> <li> <p ng-bind-html=\"('Данные от сервиса входа нужны, чтобы создать вашу учётную запись и узнавать вас при следующих входах. Имя и фото появятся в вашем публичном профиле — изменить их можно в настройках. Эти данные хранятся вместе с учётной записью, пока она существует. Мы не продаём их, не передаём третьим лицам и не используем для рекламы. Чтобы удалить их вместе с учётной записью, напишите на <a href=\\'mailto:[email protected]\\'>[email protected]</a>.' | t) | trustedHtml\" ></p> </li> <li> <p>{{ 'Издательство обеспечивает конфиденциальность персональных данных и применяет все необходимые организационные и технические меры по их защите.' | t }}</p> </li> <li> <p ng-bind-html=\"('По желанию пользователя Издательство готово удалить любую информацию о нем, собранную автоматическим путем. Для этого следует написать на адрес электронной почты <a href=\\'mailto:[email protected]\\'>[email protected]</a>.' | t) | trustedHtml\" ></p> </li> <li> <p>{{ 'Если в информации, предоставляемой Издательству Пользователем, содержатся персональные данные последнего, то фактом их предоставления он соглашается на их обработку любым способом, не запрещенным законодательством РФ.' | t }}</p> <p ng-bind-html=\"('Общедоступные видео на сайте могут транслироваться с YouTube и регулируются <a href=\\'https://policies.google.com/privacy\\'>политикой конфиденциальности Google</a>. Загрузка видео на сайт также означает согласие с <a href=\\'https://www.youtube.com/t/terms\\'>Условиями использования YouTube</a>.' | t) | trustedHtml\" ></p> </li> <li> <p>{{ 'Данные, которые мы получаем от вас, мы используем только в соответствии с принципами обработки данных, указанными в этом документе.' | t }}</p> </li> </ol> <p ng-bind-html=\"('Остальные условия работы сайта — в <a href=\\'/terms-of-use\\'>правилах сайта</a>.' | t) | trustedHtml\" ></p> </div> </div> </div>"),e.put("/pages/about/terms-of-use.html","<update-meta title=\"{{ 'Правила сайта' | t }}\"></update-meta> <update-meta name=\"description\" content=\"{{ 'Правила открытого журнала' | t }}\"></update-meta> <update-meta name=\"keywords\" content=\"{{ 'Discours.io, правила сайта, terms of use' | t }}\" ></update-meta> <update-meta property=\"og:title\" content=\"{{ 'Правила сайта' | t }}\"></update-meta> <update-meta property=\"og:description\" content=\"{{ 'Правила открытого журнала' | t }}\" ></update-meta> <div class=\"ds-container open-post\"> <div class=\"tw:row-flex\"> <div class=\"tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 tw:mt-4\"> <h1 class=\"tw:font-bold tw:mb-section\" align=\"center\">{{ 'Правила сайта' | t }}</h1> </div> <div class=\"tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 post-body\"> <p>{{ 'Дискурс — это сообщество творческих людей, объединенных идеей делать интересный журнал для всех желающих. Авторы Дискурса сообща посредством прямого голосования определяют содержание журнала.' | t }}</p> <p>{{ 'Для того, чтобы Дискурс работал без помех, разработаны настоящие Правила.' | t }}</p> <h3 id=\"определения\">{{ 'Определения' | t }}</h3> <p><strong>{{ 'Сайт' | t }}</strong> — {{ 'портал discours.io' | t }}</p> <p><strong>{{ 'Пользователь' | t }}</strong> — {{ 'лицо, пользующееся Сайтом, либо юридическое лицо, обладающее правами на интеллектуальную собственность.' | t }}</p> <p><strong>{{ 'Публикация контента' | t }}</strong> — {{ 'размещение Пользователем посредством Сайта объектов авторских прав и другой информации для других пользователей.' | t }}</p> <p><strong>{{ 'Издательство' | t }}</strong> — {{ 'администрация сайта, которая занимается технической и издательской деятельностью для обеспечения функционирования Сайта и Альманаха. Издательство не вмешивается в принятие редакционных решений авторским сообществом.' | t }}</p> <p><strong>{{ 'Альманах «Дискурс»' | t }}</strong> {{ '(свидетельство о регистрации СМИ: ПИ № ФС77-63947 от 18.12.15) — печатное периодическое издание, которое выходит раз в год и состоит из лучших публикаций на Сайте за это время.' | t }}</p> <h3 id=\"авторские-права\">{{ 'Авторские права' | t }}</h3> <ol style=\"list-style-type: decimal\"> <li> <p>{{ 'Вся информация на сайте (включая тексты, изображения, видеоматериалы, аудиозаписи, программный код, дизайн сайта и т.д.) является объектом интеллектуальной собственности ее правообладателей и охраняется законодательством РФ.' | t }}</p> </li> <li> <p>{{ 'Публикуя контент на сайте, Пользователь на безвозмездной основе предоставляет Издательству право на воспроизведение, распространение, перевод, редактирование контента. Данное право предоставляется Издательству на весь срок действия авторских прав Пользователя.' | t }}</p> </li> <li> <p>{{ 'Пользователь предоставляет Издательству право редактировать контент, в том числе вносить в него изменения, сокращения и дополнения, снабжать его иллюстрациями и пояснениями, исправлять ошибки и уточнять фактические сведения, при условии, что этим не искажается авторский замысел.' | t }}</p> </li> <li> <p ng-bind-html=\"('Обнародование контента осуществляется Издательством в соответствии с условиями лицензии <a href=\\'https://creativecommons.org/licenses/by-nc-nd/4.0/deed.ru\\'>
1Creative Commons BY-NC-ND 4.0</a>. Все материалы сайта предназначены исключительно для личного некоммерческого использования. Права на дизайн и программный код сайта принадлежат Издательству.' | t) | trustedHtml\" ></p> </li> <li> <p ng-bind-html=\"('Все аудиовизуальные произведения являются собственностью своих авторов и правообладателей и используются только в образовательных и информационных целях. Если вы являетесь собственником того или иного произведения и не согласны с его размещением на сайте, пожалуйста, напишите на <a href=\\'mailto:[email protected]\\'>[email protected]</a>.' | t) | trustedHtml\" ></p> </li> <li> <p>{{ 'Цитирование, распространение, доведение до всеобщего сведения материалов Сайта приветствуется. При использовании материалов сайта необходимо указать имя автора и активную ссылку на материал на Сайте.' | t }}</p> </li> </ol> <h3 id=\"правила-поведения-на-сайте\">{{ 'Правила поведения' | t }}</h3> <ol style=\"list-style-type: decimal\"> <li> <p>{{ 'Находясь на Сайте, Пользователь подтверждает свое совершеннолетие, правоспособность, а также согласие с настоящими Правилами и политикой конфиденциальности и готовность нести полную ответственность за их соблюдение.' | t }}</p> </li> <li> <p>{{ 'На сайте запрещено: а) Публиковать контент, авторские права на который принадлежат третьим лицам, без согласия этих лиц. Если авторские права на контент принадлежат нескольким лицам, то его публикация предполагает согласие их всех. б) Размещать коммерческую и политическую рекламу. в) Целенаправленно препятствовать нормальному функционированию сообщества и сайта discours.io. г) Выдавать себя за другого человека и представляться его именем. д) Размещать информацию, которая не соответствует целям создания Сайта, ущемляет интересы других пользователей или третьих лиц, нарушает законы Российской Федерации.' | t }}</p> </li> <li> <p>{{ 'Пользователь несет всю ответственность за содержание публикуемого контента и свое взаимодействие с другими пользователями, и обязуется возместить все расходы в случае предъявления каких-либо претензий третьими лицами. Издательство не несет ответственности за содержание публикуемой пользователями информации, в том числе за размещенные на сайте комментарии. Переписка между Пользователем и Издательством считается юридически значимой. Настоящие Правила могут быть изменены Издательством, изменения вступают в силу с момента публикации на Сайте.' | t }}</p> </li> <li> <p>{{ 'Если Пользователь очевидно и целенаправленно нарушает правила, Издательство может принять в отношении автора следующие меры: вынести предупреждение и обязать автора устранить допущенное нарушение, удалить контент, нарушающий правила, заблокировать или удалить аккаунт нарушителя.' | t }}</p> </li> </ol> <h3 id=\"privacy-policy\">{{ 'Политика конфиденциальности' | t }}</h3> <p ng-bind-html=\"('Какие данные собирает Сайт и как Издательство их использует, описано в <a href=\\'/privacy-policy\\'>политике конфиденциальности</a>.' | t) | trustedHtml\" ></p> <h3 id=\"обратная-связь\">{{ 'Обратная связь' | t }}</h3> <p ng-bind-html=\"('Любые вопросы и предложения по поводу функционирования сайта можно направить по электронной почте <a href=\\'mailto:[email protected]\\'>[email protected]</a> или через форму <a href=\\'/#idea\\' view-modal=\\'#send-idea-modal\\' rel=\\'noopener noreferrer\\'>«предложить идею»</a>.' | t) | trustedHtml\" ></p> </div> </div> </div>"),e.put("/pages/best/index.html",'<update-meta title="{{ \'Избранное\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Лучшие публикации Дискурса за шесть лет\' | t }}" ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, журнал Дискурс, Дискурс, культура, наука, искусство, общество, независимая журналистика, литература, музыка, кино, видео, фотографии\' | t }}" ></update-meta> <update-meta property="og:title" content="{{ \'Избранное\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Лучшие материалы открытой редакции. Обновите страницу и увидите новые избранные публикации\' | t }}" ></update-meta> <div class="ds-container tw:flow-root"> <h2 class="site-nav">{{ \'Избранное\' | t }}</h2> <p class="tag-description" ng-bind-html="\'За годы работы в&amp;nbsp;Дискурсе вышли тысячи прекрасных публикаций. <br />
1На этой странице появляются случайные из лучших материалов журнала, выбранные&amp;nbsp;участниками открытой&amp;nbsp;редакции. Если с&amp;nbsp;этими материалами вы&amp;nbsp;уже&amp;nbsp;знакомы, просто обновите страницу и увидите новые избранные публикации.\' | t | trustedHtml" ></p> <div class="articles-container articles-container--loaded" ng-if="!$ctrl.isMoreLoading"> <article ng-repeat="item in $ctrl.moreItems track by item._id" item="item" class="article article-grid-{{ $index + 1 }}" ng-style="{{ { order: $index } }}" ></article> <banners class="banners-more" types="[\'subscribe\', \'social\', \'supportDiscours\']" style="order: 3" ></banners> </div> <div class="main-loadmore main-loadmore--homepage" ng-if="!$ctrl.isMoreLoading"> <button type="button" class="loadmore" ng-click="$ctrl.loadMore()"> <i class="fas fa-random" style="font-size: 22px"></i> {{ \'Перемешать\' | t }} </button> </div> </div> <div class="tw:text-center tw:mt-10" ng-if="$ctrl.isMoreLoading"> <i class="fas fa-sync-alt fa-spin fa-5x"></i> </div>'),e.put("/pages/events/category.html",'<ng-include src="\'/pages/other/error.html\'" ng-if="$ctrl.events && $ctrl.events.error && $ctrl.events.error.value" ng-init="$root.statusCode = $ctrl.events.error.value" ></ng-include> <div ng-hide="$ctrl.events && $ctrl.events.error && $ctrl.events.error.value"> <update-meta title="{{ ::$ctrl.title() }}"></update-meta> <update-meta name="description" content="{{ \'Категория событий\' | t }} {{ ::$ctrl.title() }}." ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, события, категория\' | t }}, {{ ::$ctrl.title() }}" ></update-meta> <update-meta property="og:title" content="{{ ::$ctrl.title() }}"></update-meta> <update-meta property="og:description" content="{{ \'Категория событий\' | t }} {{ ::$ctrl.title() }}." ></update-meta> <update-meta property="og:image" content="https://discours.io/images/opengraph-logo-black.png" ></update-meta> <update-meta property="og:image:alt" content="Категория событий {{ ::$ctrl.title() }} — Дискурс" ></update-meta> <div class="ds-container"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-3/12"> <ul class="columns-menu tw:mt-20"> <li class="header">{{ \'ТИПЫ МЕРОПРИЯТИЙ\' | t }}</li> <li ng-repeat="category in $ctrl.categories track by category.slug" ng-class="{ active: $ctrl.activeCategory.title == category.title }" ng-if="category.title" > <a href="/events/{{ ::category.slug }}">{{ ::(category.title | uppercase) }}</a> </li> </ul> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12"> <h1 class="column-heading">{{ ::$ctrl.title() }}</h1> <div> <div ng-repeat="event in $ctrl.events track by event._id"> <div class="article-horizontal" ng-class="{\'img-left\': $index % 2 == 0, \'img-right\': $index % 2 == 1 }" > <div class="date">{{ ::event.date }}</div> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12 img-col"> <a href="{{ ::event.link }}" ><img class="article-img tw:block tw:max-w-full tw:h-auto" ng-src="{{ ::(event.thumborId | imageUrl: 900) }}" alt="{{ ::event.title }}" loading="lazy" decoding="async" /></a> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-6/12"> <h3 class="article-title black-bg"> <a href="{{ ::event.link }}">{{ ::event.title }}</a> </h3> <p class="description">{{ ::event.description }}</p> </div> </div> <a class="readmore" href="{{ ::event.link }}">{{ \'подробнее\' | t }}</a> </div> </div> </div> </div> </div> <discours-pagination items-per-page="12" total-items="{{ $ctrl.totalItems }}" current-page="{{ $ctrl.currentPage }}" ></discours-pagination> </div> </div>'),e.put("/pages/events/item.html",'<ng-include src="\'/pages/other/error.html\'" ng-if="$ctrl.item && $ctrl.item.status" ng-init="$root.statusCode = $ctrl.item.status || 404" ></ng-include> <div class="event-page ds-container tw:mt-section" ng-if="$ctrl.item && !$ctrl.item.status"> <div class="content"> <update-meta title="{{ $ctrl.item.title }}"></update-meta> <update-meta name="description" content="{{ $ctrl.item.body | stripTagsAndTruncate: 160 }}." ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, событие\' | t }}, {{ $ctrl.item.title }}" ></update-meta> <update-meta property="og:title" content="{{ $ctrl.item.title }}"></update-meta> <update-meta property="og:description" content="{{ $ctrl.item.body | stripTagsAndTruncate: 160 }}" ></update-meta> <update-meta property="og:image" content="{{ $ctrl.item.thumborId | imageUrl: 400 }}" ng-if="$ctrl.item.thumborId" ></update-meta> <div class="item-info open-post event-post progress"> <div class="tw:row-flex"> <div class="ds-content-col"> <div class="tw:mb-section"> <span class="category-title" role="heading" aria-level="2"> <u>{{ $ctrl.item.category | t }}</u> </span> </div> <div class="event-place" ng-if="$ctrl.item.place"> <span class="event-place-title">{{ \'Где:\' | t }}</span ><span class="event-place-name" ng-bind-html="$ctrl.item.place | trustedHtml"></span> </div> <div class="event-date-time"> <span class="event-date-title">{{ \'Когда:\' | t }}</span ><span class="event-date tw:invisible" >{{ $ctrl.item.publishedAt | amDateFormat: \'D MMM YYYY\' }}</span ><span class="event-time">{{ $ctrl.item.date }}</span> </div> <h1 class="title"> {{ $ctrl.item.title }}<span class="title-icons" ><a class="icon-link title-icon" ng-href="https://api.discours.io/admin/resources/events/records/{{ $ctrl.item._id }}/edit" ng-if="$ctrl.isAdmin" aria-label="{{ \'Редактировать событие\' | t }}" ><i class="fas fa-pencil-alt" aria-hidden="true"></i ><span class="custom-tooltip" aria-hidden="true" >{{ \'Редактировать событие\' | t }}</span ></a ></span > </h1> <img class="tw:block tw:max-w-full tw:h-auto fancybox-img" fancybox-img ng-src="{{ $ctrl.item.thumborId | imageUrl: 900 }}" alt="{{ $ctrl.item.title }} / Discours.io" loading="lazy" decoding="async" width="900" height="600" /> </div> <div class="ds-content-col tw:mt-section"> <div class="post-body" ng-bind-html="$ctrl.item.body | trustedHtml" post-images="post-images" embed-resize ></div> </div> </div> <div class="tw:row-flex tw:mt-section" ng-if="$ctrl.item.tags.length"> <div class="tw:gutter-half tw:w-full tw:md:w-1/12 tw:md:ml-offset-2 tw:text-right"> <p>{{ \'темы:\' | t }}</p> </div> <div class="tw:gutter-half tw:w-full tw:md:w-6/12"> <ul class="tags"> <li ng-repeat="tag in $ctrl.item.tags track by tag.slug"> <a href="/tags/{{ tag.slug }}">{{ tag.title }}</a> </li> </ul> </div> </div> </div> </div> </div>'),e.put("/pages/newsletter/archive.html",'<update-meta title="{{ $ctrl.campaign.subject || (\'Веб-версия письма\' | t) }}"></update-meta> <update-meta name="robots" content="noindex, nofollow"></update-meta> <update-meta name="description" content="{{ $ctrl.campaign.preheader || (\'Веб-версия рассылки Дискурса.\' | t) }}" ></update-meta> <div class="ds-container newsletter-archive"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-10/12 tw:sm:ml-offset-1 tw:md:w-8/12 tw:md:ml-offset-2" > <div ng-if="$ctrl.mode === \'loading\'" class="newsletter-archive__loading" role="status" aria-l
1ive="polite" > {{ \'Загрузка…\' | t }} </div> <div ng-if="$ctrl.mode === \'notfound\'" class="newsletter-archive__msg"> <h1 class="newsletter-archive__title" ng-bind-html="(\'Письмо не&amp;nbsp;найдено\' | t) | trustedHtml" ></h1> <p class="newsletter-archive__lead">{{ \'Веб-версия этой рассылки недоступна.\' | t }}</p> <a href="/" class="ds-btn ds-btn-default" ng-bind-html="(\'На&amp;nbsp;главную\' | t) | trustedHtml" ></a> </div> <div ng-if="$ctrl.mode === \'error\'" class="newsletter-archive__msg"> <h1 class="newsletter-archive__title" ng-bind-html="(\'Не&amp;nbsp;удалось открыть\' | t) | trustedHtml" ></h1> <p class="newsletter-archive__lead">{{ \'Попробуйте обновить страницу позже.\' | t }}</p> <a href="/" class="ds-btn ds-btn-default" ng-bind-html="(\'На&amp;nbsp;главную\' | t) | trustedHtml" ></a> </div> <div ng-if="$ctrl.mode === \'loaded\'" class="newsletter-archive__view"> <div class="newsletter-archive__head"> <h1 class="newsletter-archive__subject">{{ $ctrl.campaign.subject }}</h1> <p class="newsletter-archive__date" ng-if="$ctrl.campaign.lastSentAt"> {{ $ctrl.campaign.lastSentAt | date:\'d MMMM y\' }} </p> </div> <iframe class="newsletter-archive__frame" ds-srcdoc="$ctrl.html" sandbox="allow-popups allow-popups-to-escape-sandbox" title="{{ \'Веб-версия письма\' | t }}" loading="lazy" ></iframe> <footer class="newsletter-archive__foot"> <div class="newsletter-archive__cta" ng-if="$ctrl.subState !== \'done\'"> <span class="newsletter-archive__cta-label" ng-bind-html="(\'Подпишитесь на&amp;nbsp;рассылку Дискурса:\' | t) | trustedHtml" ></span> <form class="newsletter-archive__cta-form" ng-submit="$ctrl.subscribe()" data-lpignore="true" > <input type="email" required="required" autocomplete="email" inputmode="email" placeholder="{{ \'Ваш e-mail\' | t }}" ng-model="$ctrl.subEmail" ng-disabled="$ctrl.subState === \'loading\'" /> <button type="submit" class="ds-btn ds-btn-primary" ng-disabled="$ctrl.subState === \'loading\'" > <span ng-if="$ctrl.subState !== \'loading\'">{{ \'Подписаться\' | t }}</span> <span ng-if="$ctrl.subState === \'loading\'">{{ \'Подписываем…\' | t }}</span> </button> </form> <p class="newsletter-archive__cta-err" ng-if="$ctrl.subState === \'error\'" ng-bind-html="(\'Не&amp;nbsp;удалось подписаться, попробуйте позже.\' | t) | trustedHtml" ></p> </div> <p class="newsletter-archive__cta-done" ng-if="$ctrl.subState === \'done\'"> {{ \'Спасибо! Будем присылать лучшие материалы.\' | t }} </p> <a href="/" class="ds-btn ds-btn-default" ng-bind-html="(\'На&amp;nbsp;главную\' | t) | trustedHtml" ></a> </footer> </div> </div> </div> </div>'),e.put("/pages/newsletter/subscriptions.html",'<update-meta title="{{ \'Подписки на рассылки\' | t }}"></update-meta> <update-meta name="robots" content="noindex, nofollow"></update-meta> <update-meta name="description" content="{{ \'Управление подписками на рассылки Дискурса.\' | t }}" ></update-meta> <div class="ds-container newsletter-subs"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-10/12 tw:sm:ml-offset-1 tw:md:w-7/12 tw:md:ml-offset-2" > <div ng-if="$ctrl.mode === \'loading\'" class="newsletter-subs__loading" role="status" aria-live="polite" > {{ \'Загрузка…\' | t }} </div> <div ng-if="$ctrl.mode === \'confirm\'" class="newsletter-subs__card"> <div class="newsletter-subs__icon" aria-hidden="true">✉</div> <h1 class="newsletter-subs__title" ng-bind-html="(\'Отписаться от&amp;nbsp;рассылки?\' | t) | trustedHtml" ></h1> <p class="newsletter-subs__lead" ng-if="$ctrl.previewAudiences().length" ng-bind-html="(\'Вы больше не&amp;nbsp;будете получать:\' | t) | trustedHtml" ></p> <ul class="newsletter-subs__list" ng-if="$ctrl.previewAudiences().length"> <li ng-repeat="a in $ctrl.previewAudiences()">{{ a.label }}</li> </ul>
1 <p class="newsletter-subs__lead" ng-if="!$ctrl.previewAudiences().length" ng-bind-html="(\'Вы отпишетесь от&amp;nbsp;рассылок Дискурса.\' | t) | trustedHtml" ></p> <p class="newsletter-subs__email" ng-if="$ctrl.preview.email"> <span ng-bind-html="(\'на&amp;nbsp;адрес\' | t) | trustedHtml"></span> <b>{{ $ctrl.preview.email }}</b> </p> <div class="newsletter-subs__actions"> <button type="button" class="ds-btn ds-btn-primary" ng-disabled="$ctrl.submitting" ng-click="$ctrl.confirmUnsubscribe()" > <span ng-if="!$ctrl.submitting">{{ \'Отписаться\' | t }}</span> <span ng-if="$ctrl.submitting">{{ \'Отписываем…\' | t }}</span> </button> </div> <div class="newsletter-subs__divider" ng-if="$ctrl.listOptions().length || $ctrl.umbrellaOption()" > <span class="newsletter-subs__divider-line"></span> <span ng-bind-html="(\'Или настройте, что&amp;nbsp;получать\' | t) | trustedHtml"></span> <span class="newsletter-subs__divider-line"></span> </div> <div ng-include="\'/partials/newsletter/subscriptions-panel.html\'"></div> <div class="newsletter-subs__actions" ng-if="$ctrl.listOptions().length || $ctrl.umbrellaOption()" > <button type="button" class="ds-btn ds-btn-default" ng-disabled="$ctrl.submitting || !$ctrl.hasPrefChanges()" ng-click="$ctrl.saveByToken()" > <span ng-if="!$ctrl.submitting">{{ \'Сохранить изменения\' | t }}</span> <span ng-if="$ctrl.submitting">{{ \'Сохраняем…\' | t }}</span> </button> </div> </div> <div ng-if="$ctrl.mode === \'done\'" class="newsletter-subs__card"> <div class="newsletter-subs__check" aria-hidden="true">✓</div> <div ng-if="$ctrl.result.kind === \'updated\'"> <h1 class="newsletter-subs__title">{{ \'Настройки сохранены\' | t }}</h1> <p class="newsletter-subs__lead" ng-if="$ctrl.savedSubscribedLabels().length"> {{ \'Вы будете получать:\' | t }} <span ng-repeat="l in $ctrl.savedSubscribedLabels()" >{{ l }}{{ $last ? \'\' : \', \' }}</span >. </p> <p class="newsletter-subs__lead" ng-if="!$ctrl.savedSubscribedLabels().length"> {{ \'Вы будете получать только самые важные письма.\' | t }} </p> </div> <div ng-if="$ctrl.result.kind !== \'updated\'"> <h1 class="newsletter-subs__title">{{ \'Готово\' | t }}</h1> <p class="newsletter-subs__lead" ng-if="$ctrl.resultAudiences().length"> {{ \'Вы отписаны от:\' | t }} <span ng-repeat="a in $ctrl.resultAudiences()" >{{ a.label }}{{ $last ? \'\' : \', \' }}</span >. </p> <p class="newsletter-subs__lead" ng-if="!$ctrl.resultAudiences().length" ng-bind-html="(\'Вы отписаны от&amp;nbsp;рассылки.\' | t) | trustedHtml" ></p> </div> <p class="newsletter-subs__email" ng-if="$ctrl.result.email"> {{ \'Адрес\' | t }} <b>{{ $ctrl.result.email }}</b> </p> <div class="newsletter-subs__actions"> <a href="/" class="ds-btn ds-btn-default" ng-bind-html="(\'На&amp;nbsp;главную\' | t) | trustedHtml" ></a> </div> </div> <div ng-if="$ctrl.mode === \'error\'" class="newsletter-subs__card"> <div class="newsletter-subs__icon newsletter-subs__icon--error" aria-hidden="true">!</div> <h1 class="newsletter-subs__title" ng-bind-html="(\'Не&amp;nbsp;получилось\' | t) | trustedHtml" ></h1> <p class="newsletter-subs__lead">{{ $ctrl.errorMessage }}</p> <div class="newsletter-subs__actions" ng-if="$ctrl.token && $ctrl.retryAction"> <button type="button" class="ds-btn ds-btn-default" ng-disabled="$ctrl.submitting || !$ctrl.retryAction" ng-click="$ctrl.retryTokenAction()" > {{ \'Попробовать снова\' | t }} </button> </div> </div> <div ng-if="$ctrl.mode === \'expired\'" class="newsletter-subs__card"> <div class="newsletter-subs__icon newsletter-subs__icon--error" aria-hidden="true">!</div> <h1 class="newsletter-subs__title">{{ \'Ссылка устарела\' | t }}</h1> <p class="newsletter-subs__lead">{{ $ctrl.errorMessage }}</p> <div class="newsletter-subs__actions"> <a href="/" class="ds-btn ds-btn-default" ng-bind-html="(\'На&amp;nbsp;главную\' | t) | trustedHtml" ></a> </div> </div> <div ng-if="$ctrl.mode === \'status\'" class="newsletter-subs__manage"> <h1 class="column-heading" ng-bind-html="(\'Подписки на&amp;nbsp;рассылки\' | t) | trustedHtml" ></h1> <p class="newsletter-subs__lead" ng-if="$ctrl.status.email"> {{ \'Адрес:\' | t }} <b>{{ $ctrl.status.email }}</b> </p> <div ng-include="\'/partials/newsletter/subscriptions-panel.html\'"></div> <div class="newsletter-subs__actions newsletter-subs__actions--left"> <button type="button" class="ds-btn ds-btn-primary" ng-disabled="$ctrl.saving || !$ctrl.hasPrefChanges()" ng-click="$ctrl.saveAuthorized()" >
1 <span ng-if="!$ctrl.saving">{{ \'Сохранить изменения\' | t }}</span> <span ng-if="$ctrl.saving">{{ \'Сохраняем…\' | t }}</span> </button> </div> </div> <div ng-if="$ctrl.mode === \'info\'" class="newsletter-subs__card"> <div class="newsletter-subs__icon" aria-hidden="true">✉</div> <h1 class="newsletter-subs__title" ng-bind-html="(\'Подписки на&amp;nbsp;рассылки\' | t) | trustedHtml" ></h1> <p class="newsletter-subs__lead" ng-bind-html="(\'Это страница управления рассылками Дискурса. Чтобы отписаться, перейдите по&amp;nbsp;ссылке «Отписаться» внизу любого письма рассылки.\' | t) | trustedHtml" ></p> <div class="newsletter-subs__actions"> <a href="/login" class="ds-btn ds-btn-default">{{ \'Войти\' | t }}</a> </div> </div> </div> </div> </div>'),e.put("/pages/other/404.html",'<ng-include src="\'/pages/other/error.html\'" ng-init="$root.statusCode = 404; $root.isStubPage = true" ></ng-include>'),e.put("/pages/other/api-crush.html",'<update-meta title="{{ \'Мы скоро вернемся\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Неполадки — это временно. Сайт Дискурса скоро снова заработает\' | t }}" ></update-meta> <update-meta name="keywords" content="Discours.io"></update-meta> <update-meta property="og:title" content="{{ \'Мы скоро вернемся\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Неполадки — это временно. Сайт Дискурса скоро снова заработает\' | t }}" ></update-meta> <div class="ds-container"> <div class="api-crush donate-result-message"> <h1 class="api-crush__title">{{ \'Мы скоро вернемся\' | t }}</h1> <p class="api-crush__info">{{ \'Друзья, у нас технические неполадки. Вероятно, наши сервера не справляются с нагрузкой, или на нас напал баг. Мы уже пытаемся поднять сайт, но это может занять некоторое время. А пока вы можете послушать наш\' | t }} <a href="https://world-stories.mave.digital/" target="_blank" rel="noopener noreferrer">{{ \'подкаст\' | t }}</a> {{ \'или почитать наши публикации в\' | t }} <a href="https://t.me/discoursio" target="_blank" rel="noopener noreferrer">{{ \'Телеграме\' | t }}</a>. {{ \'Если вы хотите помочь, пожалуйста, напишите на\' | t }} <a href="mailto:[email protected]">[email protected]</a>.</p> <div class="api-crush__image"> <img src="/images/wakeup.png" title="{{ \'Мы уже поднимаем сайт! Надеемся, Дискурс скоро заработает\' | t }}" alt="{{ \'Мы уже поднимаем сайт! Надеемся, Дискурс скоро заработает\' | t }}" /> </div> </div> </div>'),e.put("/pages/other/auth-entry.html",'<div ng-if="$ctrl.inviteFlow"> <update-meta title="{{ $ctrl.title }}"></update-meta> <div class="ds-container"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-10/12 tw:sm:ml-offset-1 tw:md:w-8/12 tw:md:ml-offset-2" > <section class="auth-entry tw:text-center"> <h1>{{ $ctrl.heading }}</h1> <p class="auth-entry__description tw:text-lg tw:font-light">{{ $ctrl.description }}</p> <p class="tw:text-error auth-entry__error" ng-if="$ctrl.errorMessage"> {{ $ctrl.errorMessage }} </p> <p class="auth-entry__status" ng-if="$ctrl.processing"> {{ \'Обрабатываем приглашение, не закрывайте страницу.\' | t }} </p> <p class="auth-entry__action"> <a href="" class="ds-btn ds-btn-primary ds-btn-lg" ng-click="$ctrl.openPrimary($event)" ng-if="!$ctrl.processing" > {{ $ctrl.primaryLabel }} </a> <a href="" class="ds-btn ds-btn-default" ng-click="$ctrl.retryAccept($event)" ng-if="$ctrl.errorMessage && $ctrl.inviteFlow && !$ctrl.processing" > {{ \'Попробовать ещё раз\' | t }}
1 </a> </p> <p class="auth-entry__secondary"> <a href="" ng-click="$ctrl.openSecondary($event)">{{ $ctrl.secondaryLabel }}</a> </p> <p> <a href="" ng-click="$ctrl.goHome($event)">{{ \'Вернуться на главную\' | t }}</a> </p> </section> </div> </div> </div> </div>'),e.put("/pages/other/blocked-in-russia.html",'<update-meta title="{{ \'Заблокировано Генеральной прокуратурой РФ\' | t }}"></update-meta> <update-meta name="robots" content="noindex, nofollow"></update-meta> <update-meta name="description" content="{{ \'Этот материал запрещен на территории России\' | t }}" ></update-meta> <update-meta name="keywords" content="Discours.io"></update-meta> <update-meta property="og:title" content="{{ \'Заблокировано Генеральной прокуратурой РФ\' | t }}" ></update-meta> <update-meta property="og:description" content="{{ \'Этот материал запрещен на территории России\' | t }}" ></update-meta> <div class="ds-container"> <div class="api-crush donate-result-message"> <h1 class="api-crush__title tw:mt-6">{{ \'Заблокировано Генеральной прокуратурой РФ\' | t }}</h1> <p class="api-crush__info tw:mb-5"> {{ \'Друзья, материал\' | t }} <strong>{{ $ctrl.blockedArticleData.title }}</strong> {{ \'заблокирован на территории РФ на&nbsp;основании требования Генеральной прокуратуры РФ от\' | t }}<br /><strong>{{ $ctrl.blockedArticleData.detailsOfSolution }}</strong>. </p> <p class="api-crush__info"> {{ \'В&nbsp;соответствии с&nbsp;решением властей доступ к&nbsp;этой публикации для тех, кто открывает их из России, закрыт. Редакция «Дискурса» не&nbsp;согласна с&nbsp;этим решением и&nbsp;обжалует его в суде.\' | t }} </p> <br /> <a href="/" class="loadmore landing loadmore--return landing"> {{ \'Вернуться на главную\' | t }} </a> </div> </div>'),e.put("/pages/other/censored-in-russia.html",'<update-meta title="{{ \'Зацензурировано\' | t }}"></update-meta> <update-meta name="robots" content="noindex, nofollow"></update-meta> <update-meta name="description" content="{{ \'Цензура запрещает писать об этом\' | t }}" ></update-meta> <update-meta name="keywords" content="Discours.io"></update-meta> <update-meta property="og:title" content="{{ \'Зацензурировано\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Цензура запрещает писать об этом\' | t }}" ></update-meta> <div class="ds-container"> <div class="api-crush donate-result-message"> <h1 class="api-crush__title tw:mt-6">{{ \'Запрещено цензурой\' | t }}</h1> <p class="api-crush__info"> {{ \'Друзья, после принятия закона о\' | t }} <s>{{ \'запрете правды\' | t }}</s> {{ \'«фейках» в&nbsp;России больше нельзя называть\' | t }} <s>{{ \'войну\' | t }}</s> {{ \'«спецоперацию»&nbsp;— войной. Чтобы не&nbsp;подвергать авторов и&nbsp;художников рискам преследования за&nbsp;то, что они называют вещи своими именами и не боятся говорить о&nbsp;том, что волнует людей, мы&nbsp;вынуждены закрыть доступ к&nbsp;части материалов самиздата для тех, кто открывает их из России. Из&nbsp;других стран, где цензура не&nbsp;узаконена, все наши публикации по-прежнему доступны.\' | t }} </p> <p class="api-crush__info tw:mt-4"> {{ \'Для просмотра закрытых материалов необязательно переезжать физически, достаточно просто\' | t }} <a href="https://t.me/discoursio/4959" target="_blank" rel="noopener noreferrer" >{{ \'сменить\' | t }}</a > {{ \'свое виртуальное местоположение, например, с помощью VPN или других\' | t }} <a href="https://discours.io/articles/social/cybersecurity-tips" target="_blank" rel="noopener noreferrer" >{{ \'способов обхода\' | t }}</a > {{ \'блокировок.\' | t }} </p> <p class="api-crush__info tw:mt-4"> <a href="/" class="loadmore landing loadmore--return landing" ng-click="$ctrl.returnToArticle(); $event.preventDefault()" > {{ \'Я уже переместился, покажите&nbsp;статью!\' | t }}
1 </a> </p> </div> </div>'),e.put("/pages/other/donate-fail.html",'<update-meta title="{{ \'Ошибка\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Ошибка\' | t }}"></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, благотворительность\' | t }}"></update-meta> <update-meta property="og:title" content="{{ \'Ошибка\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Ошибка\' | t }}"></update-meta> <div class="tw:text-center donate-result-message"> <h1> {{ \'К сожалению, что-то пошло не так,\' | t }}<br /> {{ \'ваш перевод не прошёл\' | t }} </h1> <p> {{ \'Возможно, вы ошиблись при вводе реквизитов карты. Пожалуйста, попробуйте\' | t }} <a href="/help">{{ \'ещё раз\' | t }}</a>. {{ \'Если с этим всё в порядке, напишите нам на\' | t }} <a href="mailto:[email protected]">[email protected]</a>, {{ \'мы постараемся помочь.\' | t }} </p> </div>'),e.put("/pages/other/donate-offer.html",'<update-meta title="Публичная оферта о заключении договора пожертвования"></update-meta> <update-meta name="description" content="Публичная оферта о заключении договора пожертвования" ></update-meta> <update-meta name="keywords" content="Discours.io, оферта, договор пожертвования, donate offer" ></update-meta> <update-meta property="og:title" content="Публичная оферта о заключении договора пожертвования" ></update-meta> <update-meta property="og:description" content="Публичная оферта о заключении договора пожертвования" ></update-meta> <div class="ds-container open-post open-post--offer"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 tw:mt-section-lg"> <h1 class="title" style="text-align: center"> Публичная оферта о&#160;заключении договора пожертвования </h1> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-7/12 tw:sm:ml-offset-2 post-body"> <ol> <li> <p> Дискурс предлагает вам осуществить пожертвование в&#160;любой удобной вам форме на&#160;следующих условиях. </p> </li> <li> <p> Настоящая публичная оферта (далее&#160;&#8212; оферта) является предложением Автономной некоммерческой организации &#171;Центр поддержки независимой журналистики и&#160;культурных инициатив &#171;Дискурс&#187; (далее&#160;&#8212; Дискурс) заключить договор пожертвования на&#160;уставные цели Дискурса с&#160;любым лицом, кто отзовется на&#160;оферту (далее&#160;&#8212; Жертвователь). </p> </li> <li> <p> Принятием настоящей оферты признается перечисление Жертвователем денежных средств на&#160;расчетный счет Дискурса. Принятие оферты Жертвователем равносильно заключению договора пожертвования на&#160;условиях, изложенных в&#160;оферте, и&#160;означает, что Жертвователь ознакомился со&#160;всеми условиями настоящей оферты, целями деятельности Дискурса, осознает значение своих действий, имеет полное право на&#160;их&#160;совершение и&#160;полностью принимает условия настоящей оферты. Принимая условия настоящей оферты, Жертвователь подтверждает добровольный и&#160;безвозмездный характер пожертвования. </p> </li> <li> <p> Местом размещения оферты является город Москва, Российская Федерация. Данное предложение является публичной офертой в&#160;соответствии с&#160;положениями п.2 ст.437 Гражданского кодекса РФ. Оферта регулируется и&#160;толкуется в&#160;соответствии с&#160;законодательством Российской Федерации. Оферта адресована исключительно гражданам Российской Федерации. </p> </li> <li> <p> Публичная оферта вступает в&#160;силу со&#160;дня, следующего за&#160;днем размещения ее&#160;на&#160;сайте Дискурса в&#160;сети Интернет по&#160;адресу: <a href="https://discours.io">https://discours.io</a>, и&#160;действует бессрочно. Дискурс вправе отменить оферту в&#160;любое время без объяснения причин. В&#160;настоящую оферту могут быть внесены изменения и&#160;дополнения без предварительного уведомления, которые вступают в&#160;силу со&#160;дня, следующего за&#160;днем их&#160;размещения на&#160;сайте Дискурса. </p> </li> <li> <p> Назначение пожертвования: средства считаются поступившими на&#160;ведение уставной деятельности и&#160;содержание Дискурса. </p> </li> <li> <p> Жертвователь по&#160;своему усмотрению вправе выбрать разовое пожертвование или ежемесячное пожертвование в&#160;сумме, определяемой Жертвователем самостоятельно. При выборе ежемесячного пожертвования Жертвователь дает свое согласие на&#160;ежемесячное списание указанной им&#160;суммы с&#160;его счета. </p> </li> <li> <p> Жертвователь вправе в&#160;любой момент изменить сумму пожертвования, дату списания или отказаться от&#160;ежемесячных переводов, известив об&#160;этом Дискурс по&#160;электронной почте, по&#160;адресу: <a href="mailto:[email protected]">[email protected]</a>. </p> </li> <li> <p> Датой пожертвования&#160;и, соответственно, датой принятия оферты считается дата поступления денежных средств от&#160;Жертвователя на&#160;счет Дискурса. </p> </li> <li> <p> Дискурс вправе в&#160;любое время до&#160;передачи ей&#160;пожертвования и&#160;в&#160;течение 15&#160;дней с&#160;момента осуществления пожертвования от&#160;него отказаться. В&#160;случае отказа от&#160;пожертвования Дискурс возвращает Жертвователю денежные средства, поступившее от&#160;него на&#160;счет Дискурса, в&#160;течение 15&#160;дней с&#160;момента принятия решения об&#160;отказе. В&#160;случае невозможности передать пожертвование Жертвователю пожертвование остается в&#160;распоряжении Дискурса. </p> </li> <li> <p> Жертвователь вправе отказаться от&#160;своего пожертвования в&#160;течение 15&#160;дней с&#160;момента его совершения, известив об&#160;этом Дискурс по&#160;электронной почте по&#160;адресу: <a href="mailto:[email protected]">[email protected]</a>. Дискурс обязуется возвратить Жертвователю поступившие от&#160;него денежные средства в&#160;течение 15&#160;дней с&#160;момента получения извещения от&#160;Жертвователя. </p> </li> <li> <p> Соглашаясь с&#160;настоящей офертой, Жертвователь дает согласие на&#160;получение писем на&#160;указанный им&#160;адрес электронной почты, подтверждающих совершение платежа, и&#160;информационных рассылок от&#160;Дискурса, от&#160;которых может отказаться в&#160;любой момент. </p> </li> <li> <p> Защита персональных данных Жертвователя осуществляется в&#160;соответствии с&#160;Федеральным законом &#8470;&#160;152-ФЗ &#171;О&#160;персональных данных&#187;. Жертвователь настоящим дает свое согласие на&#160;обработку своих персональных данных любыми не&#160;запрещенными законом способами для целей исполнения настоящего предложения. </p> </li> <li> <p> Жертвователь понимает, что в&#160;силу &#8470;&#160;7-ФЗ &#171;О&#160;
1некоммерческих организациях&#187;, в&#160;случае получения Дискурсом денежных средств от&#160;иностранных источников для Дискурса могут наступить неблагоприятные последствия. Настоящим Жертвователь подтверждает, что на&#160;момент принятия оферты он&#160;является гражданином Российской Федерации, действует от&#160;своего имени, за&#160;свой счет и&#160;в&#160;своем интересе. В&#160;случае, если Дискурсу станет известно, что на&#160;момент принятия оферты Жертвователь не&#160;являлся гражданином Российской Федерации, либо действовал от&#160;имени, за&#160;счет или в&#160;интересах третьих лиц, Дискурс имеет право в&#160;одностороннем внесудебном порядке отказаться от&#160;исполнения договора, потребовать возмещения причиненных Дискурсу убытков и&#160;вернуть полученные денежные средства Жертвователю. </p> </li> <li> <p> Реквизиты Дискурса<br /> АНО &#171;Центр поддержки независимой журналистики и&#160;культурных инициатив &#171;Дискурс&#187;<br /> ИНН: 7735160099&#160;<br /> КПП: 773501001&#160;<br /> БИК: 044525999<br /> Наименование банка: Точка ПАО Банка &#171;ФК&#160;Открытие&#187;<br /> Расчетный счет Дискурса: 40703810501500000120<br /> Корреспондентский счет банка: 30101810845250000999<br /> Назначение платежа: Пожертвование на&#160;уставную деятельность АНО &#171;Центр поддержки независимой журналистики и&#160;культурных инициатив &#171;Дискурс&#187; </p> </li> </ol> </div> </div> </div>'),e.put("/pages/other/donate-success.html",'<update-meta title="{{ \'Спасибо за поддержку!\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Ваши пожертвования позволяют нам планировать работу, развивать Дискурс и продолжать делать качественную независимую журналистику. Мы вас не подведём\' | t }}" ></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, благотворительность\' | t }}"></update-meta> <update-meta property="og:title" content="{{ \'Спасибо за поддержку!\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Ваши пожертвования позволяют нам планировать работу и делать качественную независимую журналистику. Мы вас не подведём\' | t }}" ></update-meta> <div class="tw:text-center donate-result-message"> <h1>{{ \'Огромное спасибо за поддержку и доверие!\' | t }}</h1> <p>{{ \'Нам действительно важен каждый рубль — только благодаря пожертвованиям мы можем планировать работу, развивать самиздат и продолжать делать качественную независимую журналистику.\' | t }}</p> <p>{{ \'Мы вас не подведем.\' | t }}</p> <div class="api-crush__image thankpage"> <img src="/images/joinfull.webp" alt="{{ \'Спасибо за поддержку и доверие! Мы не подведём\' | t }}" loading="lazy" /> </div> </div>'),e.put("/pages/other/home-better.html","<update-meta title=\"{{ '#ДомаЛучше' | t }}\"></update-meta> <update-meta name=\"description\" content=\"{{ 'Афиша временно закрыта, но заработает сразу после эпидемии' | t }}\" ></update-meta> <update-meta name=\"keywords\" content=\"Discours.io\"></update-meta> <update-meta property=\"og:title\" content=\"{{ '#ДомаЛучше' | t }}\"></update-meta> <update-meta property=\"og:description\" content=\"{{ 'Афиша временно закрыта, но заработает сразу после эпидемии' | t }}\" ></update-meta> <div class=\"api-crush donate-result-message\"> <h1 class=\"api-crush__title\">{{ '#ДомаЛучше' | t }}</h1> <p class=\"api-crush__info home\"> {{ 'На время эпидемии мы отключаем афишу событий, и советуем вам оставаться дома. Надеемся, распространение COVID-19 закончится как можно скорее.' | t }} </p> <p class=\"api-crush__info home\" ng-bind-html=\"('Пока сходить никуда не получится, читайте наши новые публикации — лонгрид об экономических причинах и <a href=\\'https://discours.io/articles/society/sotsialnoe-zarazhenie-mikrobiologicheskaya-klassovaya-voyna-v-kitae\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>социальной природе пандемии</a>, рассказы атеистов <a href=\\'https://discours.io/articles/social/nas-gotovy-dazhe-ubit-no-uk-rf-ne-daet-im-stolko-svobody-kak-zhiv-tsya-ateistam-na-severnom-kavkaze\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>о жизни в религиозных обществах</a> Северного Кавказа, <a href=\\'https://discours.io/articles/empiric/on-shvatil-mo-litso-i-udaril-golovoy-ob-stenu-monolog-muzhchiny-perezhivshego-domashnee-nasilie\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>монолог</a> мужчины, пережившего домашнее насилие, обзор <a href=\\'https://discours.io/articles/chapter
1s/teleprogramma-zastoya-kak-unyloe-televidenie-80-h-predopredelilo-uspeh-simpsonov\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>главных клише</a> американского телевидения 80-х, воспоминания личного секретаря Сталина о том, как генсек <a href=\\'https://discours.io/articles/social/kak-stalin-zahvatyval-vlast-zapreschennye-vospominaniya-sekretarya-vozhdya\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>пришёл к безраздельной власти</a> в стране. Разделы <a href=\\'https://discours.io/expo/image\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>Галерея</a> и <a href=\\'https://discours.io/expo/literature\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>Литература</a> также постоянно обновляются, а в <a href=\\'https://discours.io/articles/chapters\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>Главах</a> регулярно выходят ключевые <a href=\\'https://discours.io/articles/chapters\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>фрагменты</a> из новых нон-фикшн книг.' | t) | trustedHtml\" ></p> <p class=\"api-crush__info home\" ng-bind-html=\"('Благодаря <a href=\\'/help\\' target=\\'_blank\\' rel=\\'noopener noreferrer\\'>вашей поддержке</a>, Дискурс продолжает работать и в самоизоляции. Берегите себя, а&nbsp;мы&nbsp;постараемся, чтобы и&nbsp;на&nbsp;карантине вам&nbsp;было&nbsp;интересно.' | t) | trustedHtml\" ></p> </div>"),e.put("/pages/other/in-work.html",'<update-meta title="{{ \'В разработке\' | t }}"></update-meta> <update-meta name="robots" content="noindex, nofollow"></update-meta> <update-meta name="description" content="{{ \'Этот раздел сайта находится в разработке\' | t }}" ></update-meta> <div class="ds-container tw:text-center tw:pt-20 tw:pb-20"> <h1>{{ \'Приносим свои извинения.\' | t }}</h1> <h2>{{ \'Данный раздел находится в разработке\' | t }}</h2> <p class="tw:mt-8"> <a class="error-home-btn" href="/">{{ \'Вернуться на главную\' | t }}</a> </p> </div>'),e.put("/pages/other/quotes.html",'<update-meta title="{{ \'Предсказание по цитатам — спецпроект к юбилею самиздата\' | t }}" ></update-meta> <update-meta name="description" content="{{ \'Случайные цитаты из лучших публикаций Дискурса. Откройте новую цитату и найдите свой материал на день.\' | t }}" ></update-meta> <update-meta property="og:title" content="{{ \'Предсказание по цитатам — спецпроект к юбилею самиздата\' | t }}" ></update-meta> <update-meta property="og:description" content="{{ \'Случайные цитаты из лучших публикаций Дискурса. Откройте новую цитату и найдите свой материал на день.\' | t }}" ></update-meta> <update-meta property="og:image" content="https://discours.io/images/quotes.png"></update-meta> <update-meta name="twitter:card" content="summary_large_image"></update-meta> <update-meta name="twitter:image" content="https://discours.io/images/quotes.png"></update-meta> <div class="ds-container quotes-page"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:md:w-8/12 tw:md:ml-offset-2"> <div class="quotes-header"> <h1 class="quotes-title">{{ \'Предсказание по цитатам\' | t }}</h1> <p class="quotes-description"> {{ \'Откройте случайную цитату из лучших публикаций Дискурса и найдите свой материал на день. Каждая цитата ведет к полной статье, из которой она взята.\' | t }} </p> </div> <div class="quote-stack"> <div class="quote-stack__card quote-stack__card--3" ng-style="{\'--rotation\': $ctrl.stackRotations[2] + \'deg\', \'--offset\': $ctrl.stackOffsets[2] + \'px\'}" ></div> <div class="quote-stack__card quote-stack__card--2" ng-style="{\'--rotation\': $ctrl.stackRotations[1] + \'deg\', \'--offset\': $ctrl.stackOffsets[1] + \'px\'}" ></div> <div class="quote-stack__card quote-stack__card--1" ng-style="{\'--rotation\': $ctrl.stackRotations[0] + \'deg\', \'--offset\': $ctrl.stackOffsets[0] + \'px\'}" ></div> <div class="quote-card" ng-class="{\'quote-card--with-image\': $ctrl.article.thumborId}" ng-click="$ctrl.goToArticle()" > <div class="quote-card__bg" ng-if="$ctrl.article.thumborId" ng-style="{\'background-image\': \'url(\' + ($ctrl.article.thumborId | imageUrl: 800:600) + \')\'}" ></div> <div ng-class="{\'quote-loading--visible\': $ctrl.loading}" class="quote-loading"> <div class="spinner"></div> <p>{{ \'Загрузка цитаты...\' | t }}</p> </div> <div ng-show="$ctrl.error && !$ctrl.loading" class="quote-error"> <p>{{ $ctrl.error }}</p> </div> <div ng-class="{\'quote-content--loading\': $ctrl.loading}" class="quote-content" ng-if="$ctrl.currentQuote || $ctrl.hasLoadedOnce" > <div class="quote-icon">"</div> <blockquote class="quote-text" ng-bind-html="$ctrl.currentQuote | trustedHtml" ></blockquote> <div class="quote-divider"></div> <div class="quote-article-info" ng-if="$ctrl.article"> <div class="quote-article-title"> <a ng-if="$ctrl.article.link && $ctrl.article.link !== \'#\'" ng-href="{{ $ctrl.article.censoredLink || $ctrl.article.link }}" class="quote-article-link" target="_blank" rel="noopener noreferrer" ng-bind-html="$ctrl.article.title | formatTitle" > </a>
1 <span ng-if="!$ctrl.article.link || $ctrl.article.link === \'#\'" class="quote-article-link" ng-bind-html="$ctrl.article.title | formatTitle" ></span> </div> <div class="quote-article-author" ng-if="$ctrl.article.author"> <a ng-if="$ctrl.article.authorLink" ng-href="{{ $ctrl.article.authorLink }}" class="quote-author-link" target="_blank" rel="noopener noreferrer" > {{ $ctrl.article.author }} </a> <span ng-if="!$ctrl.article.authorLink" class="quote-author-name" >{{ $ctrl.article.author }}</span > </div> <div ng-if="$ctrl.article.error" class="quote-article-error"> <small class="quote-article-error-text" >{{ \'Не удалось загрузить полную информацию о статье\' | t }}</small > </div> </div> </div> </div> </div> <div class="quotes-actions"> <button type="button" ng-click="$ctrl.loadRandomQuote()" class="ds-btn btn-quote btn-quote-primary" ng-disabled="$ctrl.loading" > <span>{{ \'Новая цитата\' | t }}</span> </button> <button type="button" ng-click="$ctrl.exportAsCard()" class="ds-btn btn-quote btn-quote-secondary" ng-disabled="$ctrl.isExporting || $ctrl.loading" ng-class="{\'exporting\': $ctrl.isExporting}" > <span ng-if="!$ctrl.isExporting"> <i class="fas fa-download"></i> {{ \'Сохранить открытку\' | t }} </span> <span ng-if="$ctrl.isExporting"> <i class="fas fa-spinner fa-spin"></i> {{ \'Сохранение...\' | t }} </span> </button> </div> <div class="quote-share-section" ng-class="{\'quote-share-hidden\': !$ctrl.hasLoadedOnce}"> <div class="quote-share-label">{{ \'Поделиться цитатой:\' | t }}</div> <div class="quote-share-buttons"> <button type="button" ng-click="$ctrl.shareVK()" class="btn-share btn-share-vk" title="{{ \'Поделиться ВКонтакте\' | t }}" aria-label="{{ \'Поделиться ВКонтакте\' | t }}" > <i class="fab fa-vk"></i> </button> <button type="button" ng-click="$ctrl.shareTelegram()" class="btn-share btn-share-telegram" title="{{ \'Поделиться в Telegram\' | t }}" aria-label="{{ \'Поделиться в Telegram\' | t }}" > <i class="fab fa-telegram-plane"></i> </button> <button type="button" ng-click="$ctrl.shareFacebook()" class="btn-share btn-share-facebook" title="{{ \'Поделиться в Facebook\' | t }}" aria-label="{{ \'Поделиться в Facebook\' | t }}" > <i class="fab fa-facebook-f"></i> </button> <button type="button" ng-click="$ctrl.shareTwitter()" class="btn-share btn-share-twitter" title="{{ \'Поделиться в X (Twitter)\' | t }}" aria-label="{{ \'Поделиться в X\' | t }}" > <i class="fab fa-twitter"></i> </button> <button type="button" ng-click="$ctrl.copyQuote()" class="btn-share btn-share-copy" title="{{ \'Копировать цитату\' | t }}" aria-label="{{ \'Копировать цитату\' | t }}" ng-class="{\'copied\': $ctrl.copySuccess}" > <i class="fas" ng-class="$ctrl.copySuccess ?\'fa-check\' : \'fa-copy\'"></i> </button> </div> <button type="button" ng-click="$ctrl.exportAsCard()" class="ds-btn btn-quote btn-quote-secondary--mobile" ng-disabled="$ctrl.isExporting || $ctrl.loading" ng-class="{\'exporting\': $ctrl.isExporting}" > <span ng-if="!$ctrl.isExporting"> <i class="fas fa-download"></i> {{ \'Сохранить открытку\' | t }} </span> <span ng-if="$ctrl.isExporting"> <i class="fas fa-spinner fa-spin"></i> {{ \'Сохранение...\' | t }} </span> </button> </div> </div> </div> </div>'),e.put("/pages/other/search.html",'<update-meta title="{{ \'Поиск\' | t }} {{ $ctrl.q }}"></update-meta> <update-meta name="description" content="{{ \'Поиск\' | t }} {{ $ctrl.q }}."></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, поиск, q\' | t }}"></update-meta> <update-meta property="og:title" content="{{ $ctrl.q }}"></update-meta> <update-meta property="og:description" content="{{ \'Поиск\' | t }} {{ $ctrl.q }}."></update-meta> <div class="ds-container"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:sm:w-9/12 tw:sm:ml-offset-2"> <h1>{{ \'поиск:\' | t }}</h1> <form action="{{ $ctrl.searchAction() }}" method="GET" role="search"> <div class="tw:row-flex tw:mb-section"> <div class="tw:gutter-half tw:w-full tw:sm:w-10/12"> <div class="discours-form search-form"> <div class="form-group"> <input class="ds-input" type="text" ng-model="$ctrl.q" name="q" aria-label="{{ \'Поисковый запрос\' | t }}" /> <p ng-if="$ctrl.suggestion"> <i >{{ \'Возможно, вы имели в виду\' | t }} <a class="black-link" href="/search?q={{ $ctrl.suggestion }}" >{{ $ctrl.suggestion }}</a >?</i > </p> </div> </div> </div> <div class="tw:gutter-half tw:w-full tw:sm:w-2/12"> <button class="paper-button" type="submit">{{ \'Найти\' | t }}</button> </div> </div> </form> <p class="tw:mt-2.5 tw:font-bold"> {{ $ctrl.itemsCount }} {{ $ctrl.itemsCount | russianNumber:\'результат\':\'результата\':\'результатов\' }} </p> <div class="search-item tw:gutter-half tw:w-full tw:sm:w-10/12 tw:sm:ml-offset-2" ng-repeat="item in $ctrl.authorItems track by item._id" > <a class="title" href="{{ item.path }}">{{ item.realname }}</a> </div> <div class="tw:row-flex search-item" ng-repeat="item in $ctrl.items track by item._id"> <div class="search-item__content"> <img class="search-item__icon" ng-src="{{ item.icon ? item.icon : \'/images/icons/doc.png\' }}" /> <p> <a class="title" href="{{ item.link }}">{{ item.title }}</a> </p> <a href="{{ item.authorLink }}" class="search-item__author">{{ item.author }}</a> </div> <div class="search-item__post-date"> <small>{{ item.createdAt | amDateFormat: \'DD.MM.YYYY\' }}</small> </div> </div> <discours-pagination items-per-page="10" total-items="{{ $ctrl.itemsCount }}" current-page="{{ $ctrl.currentPage }}" ></discours-pagination> </div> </div> </div>'),e.put("/pages/other/thanks.html",'<update-meta title="{{ \'Благодарности\' | t }}"></update-meta> <update-meta name="description" content="{{ \'Спасибо Вам!\' | t }}"></update-meta> <update-meta name="keywords" content="{{ \'Discours.io, thanks, благодарности, спасибо\' | t }}" ></update-meta> <update-meta property="og:title" content="{{ \'Благодарности\' | t }}"></update-meta> <update-meta property="og:description" content="{{ \'Благодарности\' | t }}"></update-meta> <update-meta property="og:image" content="/images/thanks.jpg"></update-meta>
1 <update-meta property="og:image:width" content="1200"></update-meta> <update-meta property="og:image:height" content="750"></update-meta> <div class="ds-container open-post tw:mt-5"> <div class="tw:row-flex"> <div class="tw:gutter-half tw:w-full tw:md:w-2/12"> <h1 class="h2 black-bg-heading black-bg-heading-margin"> <span>{{ \'Благодарности\' | t }}</span> </h1> </div> <div class="tw:gutter-half tw:w-full tw:md:w-7/12 post-body tw:mt-20"> <h3 class="not-left"> <b>{{ \'Неоценимый вклад в Дискурс внесли и вносят\' | t }}</b> </h3> <p> Артем Бадрызлов, Мария Бессмертная, Дамир Бикчурин, Илья Викторов, Александр Воробьев, Константин Ворович, Ян Выговский, Эльдар Гариффулин, Павел Гафаров, Виктория Гендлина, Софья Гетц, Павел Глазков, Александр Гусев, Данила Давыдов, Константин Дубовик, Вячеслав Еременко, Кристина Ибрагим, Екатерина Ильина, Анна Капаева, Яна Климова, Александр Коренков, Ирэна Лесневская, Евгения Липич, Игорь Лобанов, Анастасия Лозовая, Григорий Ломизе, Евгений Медведев, Павел Никулин, Николай Носачевский, Андрей Орловский, Михаил Панин, Антон Панов, Павел Пепперштейн, Любовь Покровская, Антон Ревин, Илья Розовский, Денис Светличный, Павел Соколов, Сергей Стрельников, Глеб Струнников, Николай Тарковский, Кирилл Филимонов, Алексей Хапов, Екатерина Харитонова </p> <h3 class="not-left"><b>{{ \'Авторы\' | t }}</b></h3> <p ng-bind-html="\'Мы&amp;nbsp;безмерно благодарны <a href=\\\'https://discours.io/authors\\\' target=\\\'_blank\\\' rel=\\\'noopener noreferrer\\\'>каждому автору</a> за&amp;nbsp;участие и&amp;nbsp;поддержку проекта. Сегодня, когда для большинства деньги стали целью и&amp;nbsp;основным источником мотивации, бескорыстная помощь и&amp;nbsp;основанный на&amp;nbsp;энтузиазме труд бесценны. Именно вы&amp;nbsp;своим трудом каждый день делаете Дискурс таким, какой он&amp;nbsp;есть.\' | t | trustedHtml" ></p> <h3 class="not-left"><b>{{ \'Иллюстраторы\' | t }}</b></h3> <p> Ольга Аверинова, Регина Акчурина, Айгуль Берхеева, Екатерина Вакуленко, Анастасия Викулова, Мария Власенко, Ванесса Гаврилова, Ольга Горше, Ксения Горшкова, Ангелина Гребенюкова, Илья Diliago, Антон Жаголкин, Саша Керова, Ольга Машинец, Злата Мечетина, Тала Никитина, Никита Поздняков, Матвей Сапегин, Татьяна Сафонова, Виктория Шибаева </p> <h3 class="not-left"><b>{{ \'Меценаты\' | t }}</b></h3> <p ng-bind-html="\'Дискурс существует исключительно на пожертвования читателей. Мы&amp;nbsp;бесконечно признательны всем, кто нас поддерживает. Ваши пожертвования&amp;nbsp;— финансовый фундамент журнала. Благодаря вам мы&amp;nbsp;развиваем платформу качественной журналистики, которая помогает самым разным авторам быть услышанными. Стать нашим меценатом и подписаться на ежемесячную поддержку проекта можно <a href=\\\'/help\\\' target=\\\'_self\\\'>здесь</a>.\' | t | trustedHtml" ></p> </div> </div> </div>'),e.put("/pages/other/upload-image.html",'<div class="ds-container post-form"> <div class="tw:row-flex"> <div class="title-input-row tw:gutter-half tw:sm:w-4/12 tw:sm:ml-offset-4"> <form class="discours-form" name="articleForm" ng-hide="contentItemSent"> <div class="form-group"> <button class="big-button" type="button" ngf-select="$ctrl.uploadImage($file)" ng-disabled="$ctrl.imageUploading[\'root\']" ng-hide="contentItem.thumborId" > <img src="/images/icons/attach.png" alt="{{ \'Прикрепить\' | t }}" /><span class="fas fa-sync-alt fa-spin floating-text left" ng-show="$ctrl.imageUploading[\'root\']" ></span ><span class="floating-text">{{ \'выбрать изображение\' | t }}</span> </button> <div class="post-image-wrapper" ng-if="contentItem.thumborId"> <button type="button" class="close" ng-click="contentItem.thumborId = null;" aria-label="{{ \'Удалить обложку\' | t }}" > x </button> <img class="tw:block tw:max-w-full tw:h-auto thumbmail" ng-src="{{ contentItem.thumborId | imageUrl: 300 }}" /> <p>{{ contentItem.thumborId }}</p> </div> </div> </form> </div> </div> </div>')}catch(a){}}(0)},14248(){angular.module("discours.components").component("aboutPage",{templateUrl:"/pages/about/guide.html",controller:["$scope","apiClient","eventBus","loginService","permissionsService",function(a,t,s,n,e){function l(){var s=n.userId()||null;s!==i&&(i=s,c=!1,o=!1,g.editorialLinks=null);var l=!c&&n.isLoggedIn()&&e.canViewAllApplications();g.isStaff=l,l?o||(o=!0,t.getList("editorial/links").then(function(t){a.$$destroyed||(g.editorialLinks=t||null)}
1).catch(function(t){a.$$destroyed||(g.editorialLinks=null,t&&403===t.status&&(c=!0,g.isStaff=!1))})):g.editorialLinks=null}var g=this,o=!1,c=!1,i=null;g.$onInit=function(){g.editorialLinks=null,l();var t=function(){a.$applyAsync(l)};s.onScope(a,s.EVENTS.USER_LOGGED_IN,t),s.onScope(a,s.EVENTS.USER_STORAGE_CHANGED,t)}}]})},15371(){angular.module("discours.components").component("censoredInRussiaPage",{templateUrl:"/pages/other/censored-in-russia.html",controller:["$window",function(a){var t=this;t.$onInit=function(){t.returnToArticle=function(){var t=localStorage.getItem("currentContentLocation")||"/";"string"==typeof t&&t.startsWith("/")&&!t.startsWith("//")||(t="/"),a.location.href=t}}}]})},18919(){angular.module("discours.components").component("donateBestArticle",{bindings:{articleData:"<"},templateUrl:"/components/donateBestArticle/donateBestArticle.html",controller:[function(){return this.$onInit=()=>{},this}]})},22198(a,t,s){"use strict";function n(a,t){var s=t&&"number"==typeof t.ttl?t.ttl:3e5,n=e[a];if(n&&n.promise){if(0===s||Date.now()-n.timestamp<s)return n.promise;delete e[a]}var l=function(a,t){var s=new AbortController,n=setTimeout(function(){s.abort()},t);return fetch(a,{signal:s.signal}).then(function(a){return clearTimeout(n),a},function(s){if(clearTimeout(n),s&&"AbortError"===s.name)throw new Error("Таймаут загрузки ("+t+"мс): "+a);throw s})}(a,t&&t.timeout||15e3).then(function(a){if(!a.ok)throw new Error("HTTP "+a.status+" "+a.statusText);return a.json()}).catch(function(t){throw delete e[a],t});return e[a]={promise:l,timestamp:Date.now()},l}s.d(t,{h:()=>n});var e={}},23946(a,t,s){"use strict";s.r(t),s.d(t,{default:()=>o}),s(13484);var n=s(36817),e=s(67314);angular.module("discours.services").factory("quoteCardExporterService",["$log","THUMBOR_SERVER",function(a,t){function s(){return window.location.href.replace(/localhost:\d+/,"discours.io").replace(/^http:/,"https:")}function l(a){window.open(a,"share","width=635,height=436,scrollbars=no,status=no,toolbar=no,menubar=no,location=no")}function g(a){return new Promise(function(t,s){var n=document.createElement("textarea");n.value=a,n.style.cssText="position:fixed;left:-9999px;top:0",document.body.appendChild(n),n.focus(),n.select();try{document.execCommand("copy"),t()}catch(a){s(a)}document.body.removeChild(n)})}function o(a){return(0,n._e)(a).trim()}function c(a){return a?(e.PREPOSITIONS_PLAIN_RE.lastIndex=0,a.replace(e.PREPOSITIONS_PLAIN_RE,function(a,t,s){return" "+s+" "})):a||""}function i(a){if(!a||"string"!=typeof a)return a;const t=a.trim(),s=t.lastIndexOf(" ");return-1===s?t:t.substring(0,s)+" "+t.substring(s+1)}function d(a){return a._cleanText||(a._cleanText=o(a.quoteText)),a._cleanText}const r={EXPORT_SIZE:1080,EXPORT_HEIGHT:864,SCALE:2,MARGIN:50,MIN_SPINNER_TIME:400,BACKGROUND_COLOR:"#ffffff",TEXT_COLOR:"#000000",ACCENT_COLOR:"#666666",LOGO_TEXT:"ДИСКУРС"},p=45;return{exportAsImage:function(s){var n=Date.now();return(window.html2canvas?Promise.resolve(window.html2canvas):new Promise(function(t,s){const n=document.createElement("script");n.src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js",n.onload=function(){window.html2canvas?t(window.html2canvas):s(new Error("html2canvas not found after loading"))},n.onerror=function(){a.error("Failed to load html2canvas from CDN"),s(new Error("Failed to load html2canvas"))},document.head.appendChild(n)})).then(function(a){var n=function(a){const s=document.createElement("div");s.className="quote-export-container";const n=o(a.quoteText),e=!!a.thumborId,l=e&&(g=a.thumborId)?t+"/unsafe/1080x800/"+g:null;var g;Object.assign(s.style,{width:r.EXPORT_SIZE+"px",minHeight:r.EXPORT_HEIGHT+"px",backgroundColor:r.BACKGROUND_COLOR,fontFamily:"'Muller', 'Helvetica Neue', Arial, sans-serif",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:r.MARGIN+"px",boxSizing:"border-box",position:"fixed",left:"-9999px",top:"0",overflow:"hidden"});const d="-"+(r.MARGIN+2)+"px";if(e){const a=document.createElement("div");Object.assign(a.style,{position:"absolute",top:d,left:d,right:d,bottom:d,backgroundImage:"url("+l+")",backgroundSize:"cover",backgroundPosition:"center",zIndex:"0"}),s.appendChild(a);const t=document.createElement("div");Object.assign(t.style,{position:"absolute",top:d,left:d,right:d,bottom:d,background:"linear-gradient(180deg, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.75) 50%, rgba(0,0,0,0.82) 100%)",zIndex:"1"}),s.appendChild(t)}const h=document.createElement("div");Object.assign(h.style,{display:"flex",flexDirection:"column",alignItems:"center",textAlign:"center",maxWidth:"100%",width:"100%",flex:"1",position:"relative",zIndex:"2"});
1const u=e?"#EAE8E3":r.TEXT_COLOR,m=e?"#ffffff":r.TITLE_COLOR,v=e?"rgba(255,255,255,0.7)":r.ACCENT_COLOR,E=e?"rgba(255,255,255,0.5)":r.TEXT_COLOR,_=e?"0 2px 8px rgba(0,0,0,0.8), 0 4px 20px rgba(0,0,0,0.6)":"none",f=e?"0 1px 4px rgba(0,0,0,0.6)":"none",b=document.createElement("div");b.textContent='"',Object.assign(b.style,{fontSize:"72px",color:e?"rgba(255,255,255,0.5)":r.ACCENT_COLOR,opacity:e?"1":"0.25",lineHeight:"1",marginBottom:"16px",fontFamily:"Georgia, serif",textShadow:e?"0 2px 10px rgba(0,0,0,0.5)":"none"});const X=document.createElement("div");let F=c(n);var y;F=i(F),X.textContent=F,Object.assign(X.style,{fontSize:(y=n.length,(y<100?44:y<200?38:y<300?34:y<400?30:26)+"px"),lineHeight:"1.4",color:u,marginBottom:"30px",maxWidth:"92%",textShadow:_});const I=document.createElement("div");Object.assign(I.style,{width:"50px",height:"2px",backgroundColor:E,marginBottom:"25px"});const S=document.createElement("div");S.textContent=function(a){if(!a||"string"!=typeof a)return a||"";const t=a.trim().split(/([.:?!])\s+/),s=[];let n="";for(let a=0;a<t.length;a++){const e=t[a];"."===e||":"===e||"?"===e||"!"===e?(n+=e,n.trim()&&s.push(n.trim()),n=""):e.trim()&&(n+=e)}n.trim()&&s.push(n.trim());const e=[];for(let a=0;a<s.length;a++){const t=s[a],n=s[a+1];0===e.length?e.push(t):t.length<p&&n?e[e.length-1]+=" "+t:e.push(t)}return e.map(function(a){return i(c(a))}).join("\n")}(a.articleTitle||""),Object.assign(S.style,{fontSize:"23px",fontWeight:"600",color:m,marginBottom:"10px",maxWidth:"75%",textAlign:"center",textShadow:f,whiteSpace:"pre-line"});const w=document.createElement("div");a.author&&(w.textContent=a.author,Object.assign(w.style,{fontSize:"24px",fontFamily:"'Spectral', Georgia, serif",fontStyle:"italic",color:e?"#ccc":r.ACCENT_COLOR,marginBottom:"0",textShadow:f}));const N=document.createElement("div");N.textContent=r.LOGO_TEXT,Object.assign(N.style,{fontSize:"16px",fontWeight:"700",letterSpacing:"3px",color:v,paddingTop:"20px",textShadow:f});const W=document.createElement("div");return Object.assign(W.style,{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",flex:"1",width:"100%"}),W.appendChild(b),W.appendChild(X),W.appendChild(I),a.articleTitle&&W.appendChild(S),a.author&&W.appendChild(w),h.appendChild(W),h.appendChild(N),s.appendChild(h),s}(s);return document.body.appendChild(n),a(n,{scale:r.SCALE,backgroundColor:r.BACKGROUND_COLOR,logging:!1,useCORS:!0,allowTaint:!0,width:n.offsetWidth,height:n.offsetHeight}).then(function(a){return document.body.removeChild(n),a})}).then(function(a){var t=Date.now()-n,s=Math.max(0,r.MIN_SPINNER_TIME-t);return new Promise(function(t){setTimeout(function(){t(a)},s)})}).then(function(t){return new Promise(function(n){t.toBlob(function(t){var e=URL.createObjectURL(t),l=document.createElement("a");l.href=e,l.download="quote-"+(s.slug||Math.random().toString(36).substring(2,10))+".png",document.body.appendChild(l),l.click(),document.body.removeChild(l),URL.revokeObjectURL(e),a.debug("Quote card exported:",s.slug),n()},"image/png")})})},shareVK:function(a){const t=encodeURIComponent(s()),n=d(a);l("https://vk.com/share.php?url="+t+"&title="+encodeURIComponent("«"+n+"»")),"function"==typeof window.gtag&&window.gtag("event","Share Quote",{method:"vk"})},shareTelegram:function(a){const t=encodeURIComponent(s()),n=d(a);l("https://t.me/share/url?url="+t+"&text="+encodeURIComponent("«"+n+"»")),"function"==typeof window.gtag&&window.gtag("event","Share Quote",{method:"telegram"})},shareFacebook:function(a){const t=encodeURIComponent(s()),n=d(a);l("https://www.facebook.com/sharer/sharer.php?u="+t+"&quote="+encodeURIComponent("«"+n+"»")),"function"==typeof window.gtag&&window.gtag("event","Share Quote",{method:"facebook"})},shareTwitter:function(a){const t=encodeURIComponent(s()),n=function(a){if(a.length<=180)return a;let t=a.substring(0,180);const s=t.lastIndexOf(" ");return s>.7*180&&(t=t.substring(0,s)),t}(d(a));l("https://twitter.com/intent/tweet?url="+t+"&text="+encodeURIComponent("«"+n+"...»")),"function"==typeof window.gtag&&window.gtag("event","Share Quote",{method:"twitter"})},copyToClipboard:function(a){var t,n=d(a),e=a.articleLink||s(),l="«"+n+"»\n\n— "+a.articleTitle+(a.author?"\n"+a.author:"")+"\n\n"+e;return t=navigator.clipboard&&window.isSecureContext?navigator.clipboard.writeText(l).catch(function(){return g(l)}):g(l),"function"==typeof window.gtag&&window.gtag("event","Share Quote",{method:"copy"}),t}}}]),s(24037),s(58846),s(95805);var l=s(86406),g=s(22198);angular.module("discours.components").component("quotesPage",{templateUrl:"/pages/other/quotes.html",controller:["$q","$log","$sce","$timeout","contentItemsService","quoteCardExporterService","Notification",function(a,t,s,n,e,o,c){function i(a,t){r.article=a;var n=(0,l.T)(t);r.currentQuote=s.trustAsHtml(n),r.currentQuoteRaw=t,r.loading=!1,r.hasLoadedOnce=!0}function d(){var a=null;if(r.article){var t=r.article.censoredLink||r.article.link;
1t&&"#"!==t&&(a=t.startsWith("/")?"https://discours.io"+t:t.startsWith("http")?t:"https://discours.io/"+t)}return{quoteText:r.currentQuoteRaw||"",articleTitle:r.article?r.article.title:"",author:r.article?r.article.author:"",slug:r.article?r.article.slug:"",thumborId:r.article?r.article.thumborId:null,articleLink:a}}var r=this;r.$onInit=function(){r.loading=!1,r.currentQuote=null,r.currentQuoteRaw=null,r.article=null,r.quotes=[],r.isExporting=!1,r.copySuccess=!1,r.hasLoadedOnce=!1,r.error=null,r.stackRotations=[0,0,0],r.stackOffsets=[0,0,0],r.stackRotations=[3*(Math.random()-.5),4*(Math.random()-.5),3*(Math.random()-.5)],r.stackOffsets=[4*Math.random()+2,6*Math.random()+4,8*Math.random()+6],r.loadQuotes()};var p=window.quotesHelpers||{};r.loadQuotes=function(){if("function"!=typeof p.extractQuotesArray)return t.error("quotesHelpers not loaded"),void(r.loading=!1);r.loading=!0,a.resolve((0,g.h)("/data/quotes.json")).then(function(a){var s=p.extractQuotesArray(a,function(a,s){t.warn(a,s)});if(r.quotes=s.filter(p.isValidQuoteEntry),r.quotes.length!==s.length&&t.warn("Skipped invalid quotes",{invalidCount:s.length-r.quotes.length}),0===r.quotes.length)return t.error("Quotes array is empty"),r.loading=!1,void(r.error="Цитаты не найдены.");r.loadRandomQuote()}).catch(function(a){t.error("Failed to load quotes",a),r.loading=!1,r.error="Не удалось загрузить цитату. Проверьте консоль браузера для деталей."})},r.loadRandomQuote=function(){var a=r.quotes;if(a&&0!==a.length){r.loading=!0;var s=Math.floor(Math.random()*a.length);a.length>1&&r.article&&a[s].slug===r.article.slug&&(s=(s+1)%a.length);var n=a[s];if(!n||!n.quote||!n.slug)return t.warn("Skipping invalid quote object",n),void(r.loading=!1);var l=n.slug,g=n.quote;e.resolveArticleReference(l).then(function(a){i(a,g)}).catch(function(a){t.error("Failed to load article",l,a),i(function(a){return{slug:a,title:"Материал: "+a,link:"#",author:"",authorLink:"",error:!0}}(l),g)})}else t.warn("No quotes available")},r.exportAsCard=function(){if(!r.isExporting&&r.currentQuoteRaw){r.isExporting=!0;var a=d();o.exportAsImage(a).then(function(){c.success("Открытка сохранена!")}).catch(function(a){t.error("Export failed",a),c.error("Не удалось сохранить открытку")}).finally(function(){r.isExporting=!1})}},r.shareVK=function(){o.shareVK(d())},r.shareTelegram=function(){o.shareTelegram(d())},r.shareFacebook=function(){o.shareFacebook(d())},r.shareTwitter=function(){o.shareTwitter(d())},r.goToArticle=function(){if(r.article){var a=r.article.censoredLink||r.article.link;a&&"#"!==a&&window.open(a,"_blank","noopener,noreferrer")}},r.copyQuote=function(){o.copyToClipboard(d()).then(function(){r.copySuccess=!0,c.success("Цитата скопирована!"),n(function(){r.copySuccess=!1},2e3)}).catch(function(){c.error("Не удалось скопировать")})}}]}),s(70528),s(15371),s(55216),s(46820),s(66769),s(24332),s(14248),s(93531),s(10961),s(13185),s(24757),s(18919),s(9803),s(11559);const o={modules:["discours.controllers","discours.services","discours.components","discours.templates"]}},24037(){function a(a){return!!(!Array.isArray(a)||a.failed||a.error||a.pager&&(a.pager.failed||a.pager.error))}function t(a){var t=Number(a&&a.rating);return Number.isFinite(t)?t:0}function s(a){var t=a&&(a.publishedAt||a.createdAt||a.updatedAt),s=t?new Date(t).getTime():0;return Number.isFinite(s)?s:0}function n(a){var n=new Set,e=[];return a.forEach(function(a){(a||[]).forEach(function(a){a&&a._id&&!n.has(a._id)&&(n.add(a._id),e.push(a))})}),e.sort(function(a,n){var e=t(n)-t(a);if(0!==e)return e;var l=s(n)-s(a);return 0!==l?l:String(n._id).localeCompare(String(a._id))}),e}var e=50,l=200,g=["Article","Literature"];angular.module("discours.components").component("almanacPage",{templateUrl:"/pages/about/almanac.html",controller:["$q","$log","contentItemsService","subscriptionService",function(t,s,o,c){function i(){var a=Math.min(p.totalAvailable,l);p.visibleCount=Math.min(p.visibleCount,a,p.allArticles.length),p.articles=p.allArticles.slice(0,p.visibleCount),p.hasMore=!p.loadError&&p.visibleCount<a&&(p.allArticles.length>p.visibleCount||d(!1).length>0)}function d(a){return g.filter(function(t){var s=p.typeStates[t];return a?s.needsRetry:!s.done})}function r(c,r){var u=d(r);return p.pendingVisibleCount=Math.min(c,l),0===u.length?(p.loadError=!1,p.visibleCount=p.pe
1ndingVisibleCount,p.pendingVisibleCount=0,i(),t.when()):(p.loadingMore=!0,function(t,s,l,g){return s.all(g.map(function(n){var g;try{g=t.query(function(a,t){return{noBody:"1",perPage:e,skip:t,type:a,sort:"rating"}}(n,l[n].skip))}catch(a){return s.when({error:a,failed:!0,items:[],type:n})}return s.when(g).then(function(t){return{failed:a(t),items:Array.isArray(t)?t:[],type:n}},function(a){return{error:a,failed:!0,items:[],type:n}})})).then(function(a){return{failed:a.some(function(a){return a.failed}),items:n(a.map(function(a){return a.items})),responses:a}})}(o,t,p.typeStates,u).then(function(a){if(!h){if(p.allArticles=n([p.allArticles,a.items]),function(a){a.forEach(function(a){var t,s=p.typeStates[a.type];a.failed?s.needsRetry=!0:(s.needsRetry=!1,s.total=(t=a.items)&&t.pager&&Number.isFinite(Number(t.pager.totalCount))?Number(t.pager.totalCount):Array.isArray(t)?t.length:0,s.skip+=e,s.done=s.skip>=s.total||a.items.length<e)});var t=g.reduce(function(a,t){var s=p.typeStates[t].total;return a+(Number.isFinite(s)?s:0)},0);p.totalAvailable=Math.max(t,p.allArticles.length)}(a.responses),a.failed)return p.loadError=!0,0===p.visibleCount&&(p.visibleCount=p.pendingVisibleCount),void i();p.loadError=!1,p.visibleCount=p.pendingVisibleCount,p.pendingVisibleCount=0,i()}}).catch(function(a){h||(u.forEach(function(a){p.typeStates[a].needsRetry=!0}),p.loadError=!0,i(),s.error("[almanacPage] Error loading almanac items:",a))}).finally(function(){h||(p.loadingInitial=!1,p.loadingMore=!1)}))}var p=this,h=!1;p.$onInit=function(){return p.allArticles=[],p.articles=[],p.visibleCount=0,p.pendingVisibleCount=e,p.typeStates={},g.forEach(function(a){p.typeStates[a]={done:!1,needsRetry:!1,skip:0,total:null}}),p.totalAvailable=0,p.hasMore=!1,p.loadError=!1,p.subscribeObj={},p.loadingInitial=!0,p.loadingMore=!1,p.subscribeFormSent=!1,p.subscribeFormLoading=!1,r(e,!1)},p.loadMore=function(){if(!p.loadingMore&&!p.loadError&&p.hasMore)return r(Math.min(p.visibleCount+e,p.totalAvailable,l),!1)},p.retryLoad=function(){if(!p.loadingMore&&p.loadError)return r(p.pendingVisibleCount||Math.min(Math.max(p.visibleCount,e),l),!0)},p.$onDestroy=function(){h=!0},p.subscribeForm=function(a){return a.preventDefault(),p.subscribeFormLoading=!0,c.postAlmanac(p.subscribeObj.email).then(function(){p.subscribeFormLoading=!1,p.subscribeFormSent=!0}
1).catch(function(){p.subscribeFormLoading=!1}),!1}}]})},24332(){angular.module("discours.controllers").controller("AuthEntryCtrl",["$location","$scope","$timeout","$window","apiClient","loginService","Notification",function(a,t,s,n,e,l,g){function o(a){if("string"!=typeof a||!a.trim())return null;try{const t=new URL(a,n.location.origin);return t.origin!==n.location.origin?null:t.pathname+t.search+t.hash}catch(a){return null}}function c(){return f?"/drafts?status=collaborations":"/"}function i(){return _||c()}function d(s){const n=o(s)||c();t.$applyAsync(function(){a.url(n)})}function r(){if(!f)return void d(i());if(X||h.processing||t.$$destroyed)return;X=!0,h.processing=!0,h.errorMessage="";const a=encodeURIComponent(E),s=encodeURIComponent(v);e.put("collab/document/"+a+"/invites/"+s,null,{status:"accepted"}).then(function(){g.success({message:"Приглашение принято"}),d(i())}).catch(function(a){if(!t.$$destroyed)return 409===(a&&a.status)?(g.info({message:"Приглашение уже принято"}),void d(i())):(t.$applyAsync(function(){X=!1,h.processing=!1,h.errorMessage="Не удалось принять приглашение автоматически. Попробуйте ещё раз."}),void g.error({message:"Не удалось принять приглашение"}))})}function p(){h.processing||F||(F=!0,h.errorMessage="",l.openAuthModal({registerMode:u,title:f?u?"Регистрация для приглашения":"Вход для приглашения":void 0,subtitle:f?"После авторизации приглашение будет принято автоматически.":void 0,onSuccess:function(){f?r():u&&!_||d(i())}}))}const h=this,u="/register"===a.path(),m=a.search()||{},v=function(a){const t="string"==typeof a?a.trim():"";return/^[A-Za-z0-9_-]{6,64}$/.test(t)?t:null}(m.invite),E=function(a){const t="string"==typeof a?a.trim():"";return/^content-item:[A-Za-z0-9_-]+$/.test(t)?t:null}(m.document||m.doc),_=o(m.next||m.redirect),f=!(!v||!E);let b=null,X=!1,F=!1;h.isRegisterMode=u,h.inviteFlow=f,h.processing=!1,h.errorMessage="",h.title=u?"Регистрация":"Вход",h.heading=f?u?"Зарегистрируйтесь, чтобы принять приглашение":"Войдите, чтобы принять приглашение":u?"Создайте аккаунт на Дискурсе":"Войдите в Дискурс",h.description=f?"После авторизации вы сразу попадёте к приглашённому черновику.":"Откроется стандартное окно входа или регистрации.",h.primaryLabel=u?"Открыть регистрацию":"Открыть вход",h.secondaryLabel=u?"Уже есть аккаунт":"Создать аккаунт",h.retryAccept=function(a){a&&"function"==typeof a.preventDefault&&a.preventDefault(),r()},h.openPrimary=function(a){a&&"function"==typeof a.preventDefault&&a.preventDefault(),F=!1,p()},h.openSecondary=function(t){t&&"function"==typeof t.preventDefault&&t.preventDefault(),a.path(u?"/login":"/register"),a.search(m)},h.goHome=function(t){t&&"function"==typeof t.preventDefault&&t.preventDefault(),a.path("/"),a.search({})},l.isLoggedIn()?s(function(){f?r():d(!u||_?i():"/profile")},0):f?b=s(p,0):(F=!0,l.openAuthModal({registerMode:u,onSuccess:function(){_?a.url(_):u&&a.url("/profile")}}),a.path("/").search({}).replace()),t.$on("$destroy",function(){b&&(s.cancel(b),b=null)})}])},24757(){angular.module("discours.components").component("newsletterArchivePage",{templateUrl:"/pages/newsletter/archive.html",controller:["apiClient","$routeParams",function(a,t){var s=this;s.mode="loading",s.campaign=null,s.html="",s.$onInit=function(){var n=String(t.id||"").trim();n?a.getOne("newsletter/archive",n).then(function(a){s.campaign=a||{},s.html=s.campaign.html||"",s.mode="loaded"}).catch(function(t){a.isCanceled&&a.isCanceled(t)||(s.mode=t&&404===t.status?"notfound":"error")}):s.mode="notfound"},s.subEmail="",s.subState="",s.subscribe=function(){var t=String(s.subEmail||"").trim();t&&"loading"!==s.subState&&(s.subState="loading",a.post("email-subscriptions",{email:t}).then(function(){s.subState="done"}).catch(function(t){a.isCanceled&&a.isCanceled(t)||(s.subState="error")}))}}]}).directive("dsSrcdoc",function(){return{restrict:"A",link:function(a,t,s){a.$watch(function(){return a.$eval(s.dsSrcdoc)},function(a){null!=a&&(t[0].srcdoc=String(a))})}}})},46820(a,t,s){var n=s(56224),e=n.initFormValidation,l=n.getPasswordStrength;angular.module("discours.components").component("resetPasswordPage",{bindings:{isEnroll:"<"},templateUrl:"/pages/authors/reset-password.html",controller:["loginService","Notification","$route","$location",function(a,t,s,n){var g=this,o=s.current.params.token;g.$onInit=function(){g.submitting=!1,g.password="",g.confirmPassword="",g.passwordStrength="weak",g.passwordStrengthLabel="",g.tokenMissing=!o,g.passwordVisible={},g.togglePasswordVisibility=function(a){g.passwordVisible[a]=!g.passwordVisible[a]},e(g,{password:{type:"passwordMinLength",getValue:function(){return g.password}},confirmPassword:{type:"confirmPassword",getValue:function(){return g.confirmPassword},confirmRef:function(){return g.password}}},{onValidate:function(a){"password"===a&&g.fieldErrors.confirmPassword&&g.validateField("confirmPassword")}})},g.updatePasswordStrength=function(){var a=l(g.password);g.passwordStrength=a.strength,g.passwordStrengthLabel=a.label},g.submitDisabled=function(){return 0===g.password.length||0===g.confirmPassword.length||g.submitting||!!g.fieldErrors.password||!!g.fieldErrors.confirmPassword},g.submit=function(s){s&&s.preventDefault(),g.validateAll(["password","confirmPassword"])||(g.submitting=!0,a.resetPassword(g.password,g.confirmPassword,o).then(function(){g.submitting=!1,t.success("Теперь вы можете войти с новым паролем"),n.path("/")}
1).catch(function(a){g.submitting=!1,t.error("string"==typeof a?a:a&&a.data&&a.data.message||a&&a.data&&a.data.error&&a.data.error.message||a&&a.message||"Произошла ошибка")}))}}]})},55216(){var a=[{id:"u3CouM7yXmqfn3b9H",detailsOfSolution:"06.04.2022 № 27-31-2022/Ид4584-22",title:"«Остановите бойню». Что говорят о войне с Украиной россияне, вышедшие на антивоенные протесты"},{id:"MBLnozMK3AdgopmuK",detailsOfSolution:"13.04.2022 № 27-31-2022/Ид5037-22",title:"День Позора. Заявление открытой редакции против войны между Россией и Украиной"}];angular.module("discours.components").component("blockedInRussiaPage",{templateUrl:"/pages/other/blocked-in-russia.html",controller:["$location","$routeParams",function(t,s){var n=this;n.$onInit=function(){n.returnToMain=function(){t.search({}).path("/").replace()},n.blockedArticleData=a.find(function(a){return a.id===s.id})}}]})},56224(a){function t(a){var t=(a||"").trim();return t?l.test(t)?null:"Это нерабочий адрес. Проверьте, правильно ли указана почта":"Заполните это поле"}function s(a){return a&&a.trim()?null:"Введите пароль"}function n(a){return!a||a.length<8?"Пароль ненадежный, минимум 8 символов":null}function e(a,t){return a?t!==a?"Пароли не совпадают":null:"Заполните это поле"}var l=/^[^\s@]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}$/;a.exports={MIN_PASSWORD_LENGTH:8,EMAIL_REGEX:l,validateEmail:t,validatePasswordRequired:s,validatePasswordMinLength:n,validateConfirmPassword:e,getPasswordStrength:function(a){if(!a||"string"!=typeof a||a.length>256)return{strength:"weak",label:"Слабый (мин. 8 символов)"};if(a.length<8)return{strength:"weak",label:"Слабый (мин. 8 символов)"};var t=/[a-zA-Zа-яА-ЯёЁ]/.test(a),s=/\d/.test(a),n=/[^a-zA-Zа-яА-ЯёЁ0-9]/.test(a);if(a.length>=16&&t)return{strength:"strong",label:"Сильный"};if(t&&(s||n))return{strength:"strong",label:"Сильный"};var e="Средний";return t?s||n||(e="Средний (добавьте цифры или спецсимволы)"):e="Средний (добавьте буквы)",{strength:"medium",label:e}},initFormValidation:function(a,l,g){function o(g){var o=l[g];if(!o)return"undefined"!=typeof console&&console,!1;delete a.fieldErrors[g];var i=o.getValue(),d=null;switch(o.type){case"email":d=t(i);break;case"passwordRequired":d=s(i);break;case"passwordMinLength":d=n(i);break;case"confirmPassword":d=e(i,o.confirmRef?o.confirmRef():"");break;default:"undefined"!=typeof console&&console}return d&&(a.fieldErrors[g]=d),c.onValidate&&c.onValidate(g,d),!!d}var c=g||{};a.fieldErrors={},a.fieldTouched={},a.passwordVisible={},a.togglePasswordVisibility=function(t){a.passwordVisible[t]=!a.passwordVisible[t]},a.validateField=function(a){return o(a)},a.validateAll=function(t){for(var s=!1,n=0;n<t.length;n++)a.fieldTouched[t[n]]=!0,o(t[n])&&(s=!0);return s},a.onFieldBlur=function(t){var s=l[t];s&&s.trimSetter&&s.trimSetter((s.getValue()||"").trim()),a.fieldTouched[t]&&o(t)},a.onFieldChange=function(t){(a.fieldTouched[t]||a.fieldErrors[t])&&o(t);var s=l[t];s&&s.onChangeExtra&&s.onChangeExtra()},a.resetValidation=function(t){if(t)for(var s=0;s<t.length;s++)delete a.fieldErrors[t[s]],delete a.fieldTouched[t[s]],delete a.passwordVisible[t[s]];else a.fieldErrors={},a.fieldTouched={},a.passwordVisible={}}}}},58846(){angular.module("discours.components").component("searchPage",{bindings:{searchData:"<"},templateUrl:"/pages/other/search.html",controller:["$routeParams","localeService",function(a,t){var s=this;s.searchAction=function(){return t.getLocalizedPath("/search",t.getLocale())},s.$onInit=function(){s.q=a.q,s.authorItems=[],s.items=s.searchData,s.itemsCount=s.searchData.pager&&s.searchData.pager.totalCount||0,s.suggestion=s.searchData.suggestion,s.currentPage=a.page||0}}]})},66769(){angular.module("discours.components").component("verifyEmailPage",{templateUrl:"/pages/authors/verify-email.html",controller:["loginService","Notification","$route","$location","localStorageService","$interval","eventBus",function(a,t,s,n,e,l,g){var o=this;o.state="verifying",o.email="",o.errorMessage="",o.cooldown=0;var c=null;o.$onInit=function(){a.verifyEmail(s.current.params.token).then(function(){e.set("emailVerified",!0),t.success("Email успешно подтверждён! Теперь вам доступны все функции"),n.path("/")}
1).catch(function(a){var t;o.state=a&&429===a.status?"rate_limited":(t=a&&a.reason)&&i[t]||"expired"})};var i={link_used_or_unknown:"used",link_expired:"expired",link_malformed:"malformed"};o.openLogin=function(){g.openLogin({title:"Войдите, чтобы продолжить"})},o.askNewLink=function(){o.state="expired"},o.requestNewLink=function(t){null!=t&&t.preventDefault(),o.email&&0!==o.email.trim().length?o.cooldown>0||(o.state="sending",o.errorMessage="",a.requestVerification(o.email).then(function(){o.state="sent"}).catch(function(a){o.state="expired",a&&a.remainingSeconds?(o.cooldown=a.remainingSeconds,c&&l.cancel(c),c=l(function(){o.cooldown--,o.cooldown<=0&&(l.cancel(c),c=null)},1e3),o.errorMessage="Подождите "+a.remainingSeconds+" сек. перед повторной отправкой"):o.errorMessage=a&&a.message||"Произошла ошибка, попробуйте позже"})):o.errorMessage="Введите ваш email"},o.$onDestroy=function(){c&&(l.cancel(c),c=null)}}]})},70528(){"use strict";angular.module("discours.components").component("changelogPage",{templateUrl:"/pages/about/changelog.html",controller:["$timeout",function(a){var t=null,s=null,n=null,e=null,l=null,g=null,o=null,c=null;this.$onInit=function(){a(function(){function a(a){if(f!==a){if(f){f.classList.remove("is-active");var t=E.get(f);t&&t.classList.remove("is-active")}if(f=a){f.classList.add("is-active");var s=E.get(f);s&&s.classList.add("is-active");var n=document.querySelector(".changelog-toc");if(n&&f.offsetParent){var e=f.offsetTop-n.offsetTop,l=n.scrollTop,g=n.clientHeight;(e<l||e>l+g-40)&&(n.scrollTop=e-g/3)}}}}function i(s,n){var e=document.getElementById(s);if(e){var l=e.closest(".changelog-section");l&&!l.open&&(l.open=!0),e.scrollIntoView({behavior:"smooth",block:"start"}),function(a){var s=document.querySelector(".changelog-summary.is-highlighted");s&&s.classList.remove("is-highlighted"),t&&clearTimeout(t);var n=a.closest(".changelog-summary");n&&(n.classList.add("is-highlighted"),t=setTimeout(function(){n.classList.remove("is-highlighted"),t=null},2e3))}(e),a(n)}}function d(){for(var t=null,s=0;s<p.length;s++){var n=p[s].querySelector("h2[id]");if(n){if(!(p[s].getBoundingClientRect().top<=100))break;t=n.id}}t&&u[t]&&a(u[t])}function r(){function a(a,t){var s=document.createElementNS(n,"svg");Object.keys(a).forEach(function(t){s.setAttribute(t,a[t])});var e=document.createElementNS(n,"path");return Object.keys(t).forEach(function(a){e.setAttribute(a,t[a])}),s.appendChild(e),s}function t(){e.setAttribute("aria-expanded","false"),o.setAttribute("aria-hidden","true"),g.setAttribute("aria-hidden","true"),o.classList.remove("is-open"),g.classList.remove("is-visible"),document.body.style.overflow="","function"==typeof e.focus&&e.focus()}if(l)return l;var s=document.querySelector(".changelog-toc__list");if(!s)return null;var n="http://www.w3.org/2000/svg",e=document.createElement("button");e.type="button",e.className="changelog-mobile-toc__fab",e.setAttribute("aria-label","Оглавление версий"),e.setAttribute("aria-expanded","false"),e.appendChild(a({width:"20",height:"20",viewBox:"0 0 24 24",fill:"none","aria-hidden":"true"},{d:"M3 5h2v2H3V5zm4 0h14v2H7V5zM3 11h2v2H3v-2zm4 0h14v2H7v-2zM3 17h2v2H3v-2zm4 0h14v2H7v-2z",fill:"currentColor"}));var g=document.createElement("div");g.className="changelog-mobile-toc__backdrop",g.setAttribute("aria-hidden","true");var o=document.createElement("div");o.className="changelog-mobile-toc__panel",o.setAttribute("role","dialog"),o.setAttribute("aria-label","Оглавление changelog"),o.setAttribute("aria-hidden","true"),o.setAttribute("aria-modal","true");var c=document.createElement("div");c.className="changelog-mobile-toc__header";var d=document.createElement("span");d.className="changelog-mobile-toc__title",d.textContent="Версии",c.appendChild(d);var r=document.createElement("button");r.type="button",r.className="changelog-mobile-toc__close",r.setAttribute("aria-label","Закрыть оглавление"),r.appendChild(a({width:"16",height:"16",viewBox:"0 0 24 24",fill:"none","aria-hidden":"true"},{d:"M6 6l12 12M6 18L18 6",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round"})),c.appendChild(r),o.appendChild(c);var p=s.cloneNode(!0);p.classList.add("changelog-mobile-toc__list"),o.appendChild(p);for(var h=s.querySelectorAll('a[href^="#"]'),u=p.querySelectorAll('a[href^="#"]'),m=Math.min(h.length,u.length),v=0;v<m;v++)E.set(h[v],u[v]),_.set(u[v],h[v]),h[v].classList.contains("is-active")&&u[v].classList.add("is-active");document.body.appendChild(g),document.body.appendChild(o),document.body.appendChild(e);var f=function(){e.setAttribute("aria-expanded","true"),o.setAttribute("aria-hidden","false"),g.setAttribute("aria-hidden","false"),o.classList.add("is-open"),g.classList.add("is-visible"),document.body.style.overflow="hidden"},b=function(){t()},X=function(){t()},F=function(a){var s=a.target.closest('a[href^="#"]');if(s){a.preventDefault();var n=s.getAttribute("href").substring(1);t(),i(n,_.get(s)||s)}},y=function(a){"Escape"===a.key&&o.classList.contains("is-open")&&t()};return e.addEventListener("click",f),g.addEventListener("click",b),r.addEventListener("click",X),p.addEventListener("click",F),document.addEventListener("keydown",y),l=function(){try{e.removeEventListener("click",f)}catch(a){}try{g.removeEventListener("click",b)}catch(a){}try{r.removeEventListener("click",X)}catch(a){}try{p.removeEventListener("click",F)}catch(a){}try{document.removeEventListener("keydown",y)}catch(a){}try{document.body.removeChild(e)}catch(a){}try{document.body.removeChild(g)}catch(a){}try{document.body.removeChild(o)}catch(a){}document.body.style.overflow="",E.clear(),_.clear(),l=null}}var p=document.querySelectorAll(".changelog-section"),h=document.querySelectorAll(".changelog-toc__list a");if(p.length&&h.length){for(var u={},m=0;m<h.length;m++){var v=h[m].getAttribute("href");v&&"#"===v.charAt(0)&&(u[v.substring(1)]=h[m])}var E=new Map,_=new Map,f=null,b=document.querySelector(".changelog-toc");
1b&&b.addEventListener("click",s=function(a){var t=a.target.closest('a[href^="#"]');t&&(a.preventDefault(),i(t.getAttribute("href").substring(1),t))}),n=function(){e||(e=requestAnimationFrame(function(){e=null,d()}))},window.addEventListener("scroll",n,{passive:!0}),d(),(o=window.matchMedia("(max-width: 991px)")).matches&&r(),c=function(){function a(){o&&(o.disconnect(),o=null),n.classList.remove("changelog-filter--stuck"),g.style.height=""}function t(){o||(o=new IntersectionObserver(function(a){var t=a[0];t&&(t.isIntersecting?(n.classList.remove("changelog-filter--stuck"),g.style.height=""):(g.style.height=n.offsetHeight+"px",n.classList.add("changelog-filter--stuck")))},{rootMargin:"-56px 0px 0px 0px",threshold:0})).observe(l)}function s(s){s.matches?t():a()}var n=document.querySelector(".changelog-filter");if(!n)return null;var e=window.matchMedia("(max-width: 991px)"),l=document.createElement("div");l.className="changelog-filter-sentinel",l.setAttribute("aria-hidden","true"),n.parentNode.insertBefore(l,n);var g=document.createElement("div");g.className="changelog-filter-placeholder",g.setAttribute("aria-hidden","true"),n.nextSibling?n.parentNode.insertBefore(g,n.nextSibling):n.parentNode.appendChild(g);var o=null;return e.matches&&t(),"function"==typeof e.addEventListener?e.addEventListener("change",s):"function"==typeof e.addListener&&e.addListener(s),function(){a(),"function"==typeof e.removeEventListener?e.removeEventListener("change",s):"function"==typeof e.removeListener&&e.removeListener(s),l.parentNode&&l.parentNode.removeChild(l),g.parentNode&&g.parentNode.removeChild(g)}}(),g=function(a){a.matches?l||r():l&&l()},"function"==typeof o.addEventListener?o.addEventListener("change",g):"function"==typeof o.addListener&&o.addListener(g)}},100)},this.$onDestroy=function(){if(t&&clearTimeout(t),e&&cancelAnimationFrame(e),n&&window.removeEventListener("scroll",n),s){var a=document.querySelector(".changelog-toc");a&&a.removeEventListener("click",s)}l&&l(),c&&c(),o&&g&&("function"==typeof o.removeEventListener?o.removeEventListener("change",g):"function"==typeof o.removeListener&&o.removeListener(g))}}]})},93531(){angular.module("discours.components").component("bestPage",{bindings:{bestArticles:"<"},templateUrl:"/pages/best/index.html",controller:["$window","contentItemsService",function(a,t){var s=this;s.$onInit=function(){s.moreItems=[].concat(s.bestArticles),s.currentPage=1,s.isMoreLoading=!1},s.loadMore=function(){a.scrollTo({top:0,behavior:"smooth"}),s.isMoreLoading=!0,t.query({page:s.currentPage,perPage:14,randomItems:120,sort:"rating",noBody:1}).then(function(a){s.moreItems=[].concat(a),s.currentPage++,s.isMoreLoading=!1}).catch(function(a){s.isMoreLoading=!1})}}]})},95805(){angular.module("discours.components").component("helpUsPage",{templateUrl:"/pages/about/help-us.html",controller:["$routeParams","$window","$location","$timeout","donateBestArticles",function(a,t,s,n,e){function l(){return t.innerWidth<768}function g(){return t.innerWidth<992}function o(){var a=l(),t=g();a===d.isMobile&&t===d.isTabletOrSmaller||n(function(){d.isMobile=a,d.isTabletOrSmaller=t,(d.visibleProjectsCount===c||d.visibleProjectsCount===i)&&(d.visibleProjectsCount=a?c:i)},0)}var c=3,i=4,d=this;d.isMobile=l(),d.isTabletOrSmaller=g(),d.bestArticles=e,d.visibleProjectsCount=d.isMobile?c:i;var r=a.openDonate||"support"===s.hash();d.isMobileDonateOpen=d.isTabletOrSmaller&&!!r,d.loadMoreProjects=function(){d.visibleProjectsCount+=d.isMobile?2:4},d.openMobileDonate=function(){d.isMobileDonateOpen=!0},d.closeMobileDonate=function(){d.isMobileDonateOpen=!1},angular.element(t).on("resize",o),d.$onDestroy=function(){angular.element(t).off("resize",o)}}]})}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.