From 39fcd9ec6ea4cc538e0aa7f6c41c6abca6d251e2 Mon Sep 17 00:00:00 2001 From: Jess Rogerson Date: Thu, 21 May 2026 14:22:46 +1000 Subject: [PATCH] feat: Light mode default, auth, Posts rename, display scaling, TinyMCE improvements --- Controllers/ApiController.cs | 13 +- Controllers/DevicesController.cs | 46 +- Routing/DeviceSlugConstraint.cs | 9 +- Views/Admin/Index.cshtml | 10 +- Views/Devices/Create.cshtml | 28 +- Views/Devices/Delete.cshtml | 2 +- Views/Devices/Index.cshtml | 8 +- Views/Devices/Playlist.cshtml | 2 +- Views/Display/NotFound.cshtml | 6 +- Views/Display/Show.cshtml | 21 +- Views/Shared/_AdminLayout.cshtml | 21 +- Views/Slides/Create.cshtml | 87 +- Views/Slides/Edit.cshtml | 66 +- wwwroot/css/display.css | 57 +- wwwroot/js/admin.js | 15 +- wwwroot/js/display.js | 17 +- wwwroot/lib/tinymce/CHANGELOG.md | 3998 +++++++++++++++++ .../js/tinymce/icons/default/icons.min.js | 1 + .../lib/tinymce/js/tinymce/langs/README.md | 3 + wwwroot/lib/tinymce/js/tinymce/license.md | 6 + .../js/tinymce/models/dom/model.min.js | 1 + wwwroot/lib/tinymce/js/tinymce/notices.txt | 21 + .../tinymce/plugins/accordion/plugin.min.js | 1 + .../js/tinymce/plugins/advlist/plugin.min.js | 1 + .../js/tinymce/plugins/anchor/plugin.min.js | 1 + .../js/tinymce/plugins/autolink/plugin.min.js | 1 + .../tinymce/plugins/autoresize/plugin.min.js | 1 + .../js/tinymce/plugins/autosave/plugin.min.js | 1 + .../js/tinymce/plugins/charmap/plugin.min.js | 1 + .../js/tinymce/plugins/code/plugin.min.js | 1 + .../tinymce/plugins/codesample/plugin.min.js | 9 + .../plugins/directionality/plugin.min.js | 1 + .../plugins/emoticons/js/emojiimages.js | 1 + .../plugins/emoticons/js/emojiimages.min.js | 1 + .../js/tinymce/plugins/emoticons/js/emojis.js | 1 + .../plugins/emoticons/js/emojis.min.js | 1 + .../tinymce/plugins/emoticons/plugin.min.js | 1 + .../tinymce/plugins/fullscreen/plugin.min.js | 1 + .../tinymce/plugins/help/js/i18n/keynav/ar.js | 93 + .../plugins/help/js/i18n/keynav/bg-BG.js | 93 + .../plugins/help/js/i18n/keynav/bg_BG.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/ca.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/cs.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/da.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/de.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/el.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/en.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/es.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/eu.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/fa.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/fi.js | 93 + .../plugins/help/js/i18n/keynav/fr-FR.js | 93 + .../plugins/help/js/i18n/keynav/fr_FR.js | 93 + .../plugins/help/js/i18n/keynav/he-IL.js | 93 + .../plugins/help/js/i18n/keynav/he_IL.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/hi.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/hr.js | 93 + .../plugins/help/js/i18n/keynav/hu-HU.js | 93 + .../plugins/help/js/i18n/keynav/hu_HU.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/id.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/it.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/ja.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/kk.js | 93 + .../plugins/help/js/i18n/keynav/ko-KR.js | 93 + .../plugins/help/js/i18n/keynav/ko_KR.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/ms.js | 93 + .../plugins/help/js/i18n/keynav/nb-NO.js | 93 + .../plugins/help/js/i18n/keynav/nb_NO.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/nl.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/pl.js | 93 + .../plugins/help/js/i18n/keynav/pt-BR.js | 93 + .../plugins/help/js/i18n/keynav/pt-PT.js | 93 + .../plugins/help/js/i18n/keynav/pt_BR.js | 93 + .../plugins/help/js/i18n/keynav/pt_PT.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/ro.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/ru.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/sk.js | 93 + .../plugins/help/js/i18n/keynav/sl-SI.js | 93 + .../plugins/help/js/i18n/keynav/sl_SI.js | 93 + .../plugins/help/js/i18n/keynav/sv-SE.js | 93 + .../plugins/help/js/i18n/keynav/sv_SE.js | 93 + .../plugins/help/js/i18n/keynav/th-TH.js | 93 + .../plugins/help/js/i18n/keynav/th_TH.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/tr.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/uk.js | 93 + .../tinymce/plugins/help/js/i18n/keynav/vi.js | 93 + .../plugins/help/js/i18n/keynav/zh-CN.js | 87 + .../plugins/help/js/i18n/keynav/zh-TW.js | 93 + .../plugins/help/js/i18n/keynav/zh_CN.js | 87 + .../plugins/help/js/i18n/keynav/zh_TW.js | 93 + .../js/tinymce/plugins/help/plugin.min.js | 1 + .../js/tinymce/plugins/image/plugin.min.js | 1 + .../tinymce/plugins/importcss/plugin.min.js | 1 + .../plugins/insertdatetime/plugin.min.js | 1 + .../js/tinymce/plugins/link/plugin.min.js | 1 + .../js/tinymce/plugins/lists/plugin.min.js | 1 + .../js/tinymce/plugins/media/plugin.min.js | 1 + .../tinymce/plugins/nonbreaking/plugin.min.js | 1 + .../tinymce/plugins/pagebreak/plugin.min.js | 1 + .../js/tinymce/plugins/preview/plugin.min.js | 1 + .../tinymce/plugins/quickbars/plugin.min.js | 1 + .../js/tinymce/plugins/save/plugin.min.js | 1 + .../plugins/searchreplace/plugin.min.js | 1 + .../js/tinymce/plugins/table/plugin.min.js | 1 + .../plugins/visualblocks/plugin.min.js | 1 + .../tinymce/plugins/visualchars/plugin.min.js | 1 + .../tinymce/plugins/wordcount/plugin.min.js | 1 + .../js/tinymce/skins/content/dark/content.js | 10 + .../skins/content/dark/content.min.css | 10 + .../tinymce/skins/content/default/content.js | 10 + .../skins/content/default/content.min.css | 10 + .../tinymce/skins/content/document/content.js | 10 + .../skins/content/document/content.min.css | 10 + .../skins/content/tinymce-5-dark/content.js | 10 + .../content/tinymce-5-dark/content.min.css | 10 + .../skins/content/tinymce-5/content.js | 10 + .../skins/content/tinymce-5/content.min.css | 10 + .../tinymce/skins/content/writer/content.js | 10 + .../skins/content/writer/content.min.css | 10 + .../skins/ui/oxide-dark/content.inline.js | 10 + .../ui/oxide-dark/content.inline.min.css | 10 + .../js/tinymce/skins/ui/oxide-dark/content.js | 10 + .../skins/ui/oxide-dark/content.min.css | 10 + .../js/tinymce/skins/ui/oxide-dark/skin.js | 1 + .../tinymce/skins/ui/oxide-dark/skin.min.css | 1 + .../skins/ui/oxide-dark/skin.shadowdom.js | 1 + .../ui/oxide-dark/skin.shadowdom.min.css | 1 + .../tinymce/skins/ui/oxide/content.inline.js | 10 + .../skins/ui/oxide/content.inline.min.css | 10 + .../js/tinymce/skins/ui/oxide/content.js | 10 + .../js/tinymce/skins/ui/oxide/content.min.css | 10 + .../tinymce/js/tinymce/skins/ui/oxide/skin.js | 1 + .../js/tinymce/skins/ui/oxide/skin.min.css | 1 + .../tinymce/skins/ui/oxide/skin.shadowdom.js | 1 + .../skins/ui/oxide/skin.shadowdom.min.css | 1 + .../skins/ui/tinymce-5-dark/content.inline.js | 10 + .../ui/tinymce-5-dark/content.inline.min.css | 10 + .../skins/ui/tinymce-5-dark/content.js | 10 + .../skins/ui/tinymce-5-dark/content.min.css | 10 + .../tinymce/skins/ui/tinymce-5-dark/skin.js | 1 + .../skins/ui/tinymce-5-dark/skin.min.css | 1 + .../skins/ui/tinymce-5-dark/skin.shadowdom.js | 1 + .../ui/tinymce-5-dark/skin.shadowdom.min.css | 1 + .../skins/ui/tinymce-5/content.inline.js | 10 + .../skins/ui/tinymce-5/content.inline.min.css | 10 + .../js/tinymce/skins/ui/tinymce-5/content.js | 10 + .../skins/ui/tinymce-5/content.min.css | 10 + .../js/tinymce/skins/ui/tinymce-5/skin.js | 1 + .../tinymce/skins/ui/tinymce-5/skin.min.css | 1 + .../skins/ui/tinymce-5/skin.shadowdom.js | 1 + .../skins/ui/tinymce-5/skin.shadowdom.min.css | 1 + .../js/tinymce/themes/silver/theme.min.js | 1 + wwwroot/lib/tinymce/js/tinymce/tinymce.d.ts | 3420 ++++++++++++++ wwwroot/lib/tinymce/js/tinymce/tinymce.min.js | 10 + 154 files changed, 12897 insertions(+), 133 deletions(-) create mode 100644 wwwroot/lib/tinymce/CHANGELOG.md create mode 100644 wwwroot/lib/tinymce/js/tinymce/icons/default/icons.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/langs/README.md create mode 100644 wwwroot/lib/tinymce/js/tinymce/license.md create mode 100644 wwwroot/lib/tinymce/js/tinymce/models/dom/model.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/notices.txt create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/accordion/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/advlist/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/anchor/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/autolink/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/autoresize/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/autosave/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/charmap/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/code/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/codesample/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/directionality/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/emoticons/js/emojiimages.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/emoticons/js/emojiimages.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/emoticons/js/emojis.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/emoticons/js/emojis.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/emoticons/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/fullscreen/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ar.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/bg-BG.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/bg_BG.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ca.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/cs.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/da.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/de.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/el.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/en.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/es.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/eu.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/fa.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/fi.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/fr-FR.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/fr_FR.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/he-IL.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/he_IL.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/hi.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/hr.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/hu-HU.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/hu_HU.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/id.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/it.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ja.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/kk.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ko-KR.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ko_KR.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ms.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/nb-NO.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/nb_NO.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/nl.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/pl.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/pt-BR.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/pt-PT.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/pt_BR.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/pt_PT.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ro.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/ru.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/sk.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/sl-SI.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/sl_SI.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/sv-SE.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/sv_SE.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/th-TH.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/th_TH.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/tr.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/uk.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/vi.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/zh-CN.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/zh-TW.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/zh_CN.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/js/i18n/keynav/zh_TW.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/help/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/image/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/importcss/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/insertdatetime/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/link/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/lists/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/media/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/nonbreaking/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/pagebreak/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/preview/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/quickbars/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/save/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/searchreplace/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/table/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/visualblocks/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/visualchars/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/plugins/wordcount/plugin.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/dark/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/dark/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/default/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/default/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/document/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/document/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/tinymce-5-dark/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/tinymce-5-dark/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/tinymce-5/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/tinymce-5/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/writer/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/content/writer/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/content.inline.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/content.inline.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/skin.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/skin.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/skin.shadowdom.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/content.inline.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/content.inline.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/skin.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/skin.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/skin.shadowdom.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/oxide/skin.shadowdom.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/content.inline.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/skin.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/skin.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5-dark/skin.shadowdom.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/content.inline.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/content.inline.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/content.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/content.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/skin.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/skin.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/skin.shadowdom.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/skins/ui/tinymce-5/skin.shadowdom.min.css create mode 100644 wwwroot/lib/tinymce/js/tinymce/themes/silver/theme.min.js create mode 100644 wwwroot/lib/tinymce/js/tinymce/tinymce.d.ts create mode 100644 wwwroot/lib/tinymce/js/tinymce/tinymce.min.js diff --git a/Controllers/ApiController.cs b/Controllers/ApiController.cs index 5b65208..8eb0f17 100644 --- a/Controllers/ApiController.cs +++ b/Controllers/ApiController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using NoticeBoard.Data; using Ical.Net; @@ -6,6 +7,7 @@ using Ical.Net.DataTypes; namespace NoticeBoard.Controllers; +[Authorize] public class ApiController : Controller { private readonly IWebHostEnvironment _env; @@ -78,18 +80,18 @@ public class ApiController : Controller if (!Directory.Exists(uploadsPath)) return Json(new { files = Array.Empty() }); + var imageExts = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".ico" }; var files = Directory.GetFiles(uploadsPath) .Select(f => new FileInfo(f)) + .Where(f => imageExts.Contains(f.Extension.ToLower())) .OrderByDescending(f => f.CreationTimeUtc) .Select(f => new { - name = f.Name, - url = $"/uploads/{f.Name}", - size = f.Length, - created = f.CreationTimeUtc + title = f.Name, + value = $"/uploads/{f.Name}" }); - return Json(new { files }); + return Json(files); } [HttpPost] @@ -107,6 +109,7 @@ public class ApiController : Controller return NotFound(); } + [AllowAnonymous] [HttpGet] public async Task ParseIcs(string url) { diff --git a/Controllers/DevicesController.cs b/Controllers/DevicesController.cs index 4a0a086..7861b45 100644 --- a/Controllers/DevicesController.cs +++ b/Controllers/DevicesController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using NoticeBoard.Data; @@ -5,6 +6,7 @@ using NoticeBoard.Models; namespace NoticeBoard.Controllers; +[Authorize] public class DevicesController : Controller { private readonly AppDbContext _db; @@ -113,8 +115,6 @@ public class DevicesController : Controller return RedirectToAction(nameof(Index)); } - // === Playlist Management === - public async Task Playlist(int id) { var device = await _db.Devices @@ -166,29 +166,6 @@ public class DevicesController : Controller return RedirectToAction(nameof(Playlist), new { id }); } - [HttpPost] - public async Task UpdatePlaylist(int id, int[] slideIds, int[] durations, bool[] enabled) - { - var deviceSlides = await _db.DeviceSlides - .Where(ds => ds.DeviceId == id) - .ToListAsync(); - - for (int i = 0; i < slideIds.Length; i++) - { - var ds = deviceSlides.FirstOrDefault(x => x.Id == slideIds[i]); - if (ds != null) - { - ds.DisplayOrder = i + 1; - if (i < durations.Length) ds.DurationSeconds = durations[i]; - ds.Enabled = i < enabled.Length && enabled[i]; - } - } - - await _db.SaveChangesAsync(); - TempData["Success"] = "Playlist updated."; - return RedirectToAction(nameof(Playlist), new { id }); - } - [HttpPost] public async Task ReorderPlaylist([FromBody] ReorderRequest request) { @@ -229,19 +206,6 @@ public class DevicesController : Controller } } -public class ReorderRequest -{ - public int[]? ItemIds { get; set; } -} - -public class UpdateDurationRequest -{ - public int DeviceSlideId { get; set; } - public int DurationSeconds { get; set; } -} - -public class ToggleEnabledRequest -{ - public int DeviceSlideId { get; set; } - public bool Enabled { get; set; } -} +public class ReorderRequest { public int[]? ItemIds { get; set; } } +public class UpdateDurationRequest { public int DeviceSlideId { get; set; } public int DurationSeconds { get; set; } } +public class ToggleEnabledRequest { public int DeviceSlideId { get; set; } public bool Enabled { get; set; } } diff --git a/Routing/DeviceSlugConstraint.cs b/Routing/DeviceSlugConstraint.cs index b9f64b1..0e39626 100644 --- a/Routing/DeviceSlugConstraint.cs +++ b/Routing/DeviceSlugConstraint.cs @@ -1,14 +1,10 @@ -using NoticeBoard.Data; -using Microsoft.EntityFrameworkCore; - namespace NoticeBoard.Routing; public class DeviceSlugConstraint : IRouteConstraint { - // Reserved paths that should NOT be treated as device slugs private static readonly HashSet ReservedPaths = new(StringComparer.OrdinalIgnoreCase) { - "admin", "api", "d", "error", "css", "js", "lib", "uploads", "favicon.ico" + "admin", "api", "account", "d", "error", "css", "js", "lib", "uploads", "favicon.ico" }; public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, @@ -16,11 +12,8 @@ public class DeviceSlugConstraint : IRouteConstraint { if (values.TryGetValue(routeKey, out var value) && value is string slug) { - // Block reserved paths if (ReservedPaths.Contains(slug)) return false; - - // Only match lowercase alphanumeric + hyphens return slug.All(c => char.IsLetterOrDigit(c) || c == '-'); } return false; diff --git a/Views/Admin/Index.cshtml b/Views/Admin/Index.cshtml index ebcbe16..3c68a73 100644 --- a/Views/Admin/Index.cshtml +++ b/Views/Admin/Index.cshtml @@ -18,7 +18,7 @@
@ViewBag.DeviceCount
-
Kittens (Devices)
+
Posts (Devices)
@@ -37,7 +37,7 @@
-
Active Kittens
+
Active Posts
Add
@@ -67,8 +67,8 @@ {
-

No kittens configured yet.

- Create your first kitten +

No posts configured yet.

+ Create your first post
}
@@ -83,7 +83,7 @@ MeowAn individual slide PURRPersistent User Requests & Reminders — a playlist ASKAttention Seeking Kitty — priority override - KittenA display node/device + PostA display node/device Scratching PostThis backend SunbeamThe framework diff --git a/Views/Devices/Create.cshtml b/Views/Devices/Create.cshtml index 93a344a..8e8864c 100644 --- a/Views/Devices/Create.cshtml +++ b/Views/Devices/Create.cshtml @@ -1,20 +1,16 @@ @model Device -@{ ViewData["Title"] = "New Kitten"; } -
-
-
-
- @Html.AntiForgeryToken() -
-
/
Lowercase, no spaces. Auto-generated from name if left empty.
-
-
-
-
Cancel
-
-
-
-
+@{ ViewData["Title"] = "New Post"; } +
+
+ @Html.AntiForgeryToken() +
+
/
Lowercase, no spaces. Auto-generated from name if left empty.
+
+
+
+
Cancel
+
+
@section Scripts { @@ -42,17 +41,25 @@
  • - Kittens + Posts Devices
  • diff --git a/Views/Slides/Create.cshtml b/Views/Slides/Create.cshtml index c609a21..f0a47d5 100644 --- a/Views/Slides/Create.cshtml +++ b/Views/Slides/Create.cshtml @@ -35,7 +35,6 @@
    -
    Enter a URL to an .ics calendar feed, or upload an ICS file below.
    @@ -65,13 +64,12 @@
    -
    How the background image fills the slide area.
    @@ -104,19 +102,88 @@ function initTinyMCE() { if (tinymce.get('contentEditor')) return; tinymce.init({ - selector: '#contentEditor', height: 500, + selector: '#contentEditor', + height: 500, license_key: 'gpl', menubar: 'file edit view insert format table', plugins: 'advlist autolink lists link image charmap preview anchor searchreplace visualblocks code fullscreen insertdatetime media table help wordcount', - toolbar: 'undo redo | blocks | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image media table | removeformat code fullscreen', + toolbar: 'undo redo | blocks fontsize | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image media table | removeformat code fullscreen', + font_size_formats: '8pt 10pt 12pt 14pt 16pt 18pt 20pt 24pt 28pt 32pt 36pt 48pt 64pt 72pt 96pt', images_upload_handler: function (blobInfo) { return new Promise(function (resolve, reject) { var fd = new FormData(); fd.append('file', blobInfo.blob(), blobInfo.filename()); fetch('/api/upload', { method: 'POST', body: fd }).then(r => r.json()).then(d => resolve(d.location)).catch(e => reject(e)); }); }, - content_style: 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 16px; color: #fff; background: #1a1a2e; }', - skin: 'oxide-dark', content_css: 'dark', promotion: false, branding: false + file_picker_types: 'image', + file_picker_callback: function (cb, value, meta) { + fetch('/api/listuploads').then(r => r.json()).then(function (files) { + var input = document.createElement('input'); + input.type = 'file'; input.accept = 'image/*'; + + // If we have existing uploads, show a picker dialog + if (files && files.length > 0) { + var dialog = document.createElement('div'); + dialog.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;'; + var panel = document.createElement('div'); + panel.style.cssText = 'background:#fff;border-radius:12px;padding:1.5em;max-width:700px;max-height:80vh;overflow-y:auto;width:90%;'; + panel.innerHTML = '

    Select Image or Upload New

    '; + + var grid = document.createElement('div'); + grid.style.cssText = 'display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;margin-bottom:1em;'; + + files.forEach(function (f) { + var thumb = document.createElement('div'); + thumb.style.cssText = 'cursor:pointer;border:2px solid #ddd;border-radius:8px;overflow:hidden;aspect-ratio:1;background:#f0f0f0;'; + thumb.innerHTML = ''; + thumb.title = f.title; + thumb.addEventListener('click', function () { + cb(f.value, { title: f.title }); + document.body.removeChild(dialog); + }); + thumb.addEventListener('mouseenter', function () { this.style.borderColor = '#3b6fd4'; }); + thumb.addEventListener('mouseleave', function () { this.style.borderColor = '#ddd'; }); + grid.appendChild(thumb); + }); + + panel.appendChild(grid); + + var uploadBtn = document.createElement('button'); + uploadBtn.textContent = 'Upload New Image'; + uploadBtn.style.cssText = 'background:#3b6fd4;color:#fff;border:none;padding:0.6em 1.5em;border-radius:6px;cursor:pointer;margin-right:0.5em;'; + uploadBtn.addEventListener('click', function () { + document.body.removeChild(dialog); + input.click(); + }); + + var cancelBtn = document.createElement('button'); + cancelBtn.textContent = 'Cancel'; + cancelBtn.style.cssText = 'background:#eee;color:#333;border:none;padding:0.6em 1.5em;border-radius:6px;cursor:pointer;'; + cancelBtn.addEventListener('click', function () { document.body.removeChild(dialog); }); + + panel.appendChild(uploadBtn); + panel.appendChild(cancelBtn); + dialog.appendChild(panel); + dialog.addEventListener('click', function (e) { if (e.target === dialog) document.body.removeChild(dialog); }); + document.body.appendChild(dialog); + } else { + input.click(); + } + + input.addEventListener('change', function () { + if (!this.files[0]) return; + var fd = new FormData(); fd.append('file', this.files[0]); + fetch('/api/upload', { method: 'POST', body: fd }) + .then(r => r.json()) + .then(d => cb(d.location, { title: this.files[0].name })); + }); + }).catch(function () { input.click(); }); + }, + content_style: 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 16px; color: #000; background: #fff; }', + skin: 'oxide', + content_css: 'default', + promotion: false, + branding: false }); } if (document.getElementById('slideType').value === '0') initTinyMCE(); diff --git a/Views/Slides/Edit.cshtml b/Views/Slides/Edit.cshtml index 8178b3b..f816790 100644 --- a/Views/Slides/Edit.cshtml +++ b/Views/Slides/Edit.cshtml @@ -65,13 +65,12 @@
    -
    How the background image fills the slide area.
    @@ -103,19 +102,68 @@ function initTinyMCE() { if (tinymce.get('contentEditor')) return; tinymce.init({ - selector: '#contentEditor', height: 500, + selector: '#contentEditor', + height: 500, license_key: 'gpl', menubar: 'file edit view insert format table', plugins: 'advlist autolink lists link image charmap preview anchor searchreplace visualblocks code fullscreen insertdatetime media table help wordcount', - toolbar: 'undo redo | blocks | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image media table | removeformat code fullscreen', + toolbar: 'undo redo | blocks fontsize | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image media table | removeformat code fullscreen', + font_size_formats: '8pt 10pt 12pt 14pt 16pt 18pt 20pt 24pt 28pt 32pt 36pt 48pt 64pt 72pt 96pt', images_upload_handler: function (blobInfo) { return new Promise(function (resolve, reject) { var fd = new FormData(); fd.append('file', blobInfo.blob(), blobInfo.filename()); fetch('/api/upload', { method: 'POST', body: fd }).then(r => r.json()).then(d => resolve(d.location)).catch(e => reject(e)); }); }, - content_style: 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 16px; color: #fff; background: #1a1a2e; }', - skin: 'oxide-dark', content_css: 'dark', promotion: false, branding: false + file_picker_types: 'image', + file_picker_callback: function (cb, value, meta) { + fetch('/api/listuploads').then(r => r.json()).then(function (files) { + var input = document.createElement('input'); + input.type = 'file'; input.accept = 'image/*'; + if (files && files.length > 0) { + var dialog = document.createElement('div'); + dialog.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;'; + var panel = document.createElement('div'); + panel.style.cssText = 'background:#fff;border-radius:12px;padding:1.5em;max-width:700px;max-height:80vh;overflow-y:auto;width:90%;'; + panel.innerHTML = '

    Select Image or Upload New

    '; + var grid = document.createElement('div'); + grid.style.cssText = 'display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;margin-bottom:1em;'; + files.forEach(function (f) { + var thumb = document.createElement('div'); + thumb.style.cssText = 'cursor:pointer;border:2px solid #ddd;border-radius:8px;overflow:hidden;aspect-ratio:1;background:#f0f0f0;'; + thumb.innerHTML = ''; + thumb.title = f.title; + thumb.addEventListener('click', function () { cb(f.value, { title: f.title }); document.body.removeChild(dialog); }); + thumb.addEventListener('mouseenter', function () { this.style.borderColor = '#3b6fd4'; }); + thumb.addEventListener('mouseleave', function () { this.style.borderColor = '#ddd'; }); + grid.appendChild(thumb); + }); + panel.appendChild(grid); + var uploadBtn = document.createElement('button'); + uploadBtn.textContent = 'Upload New Image'; + uploadBtn.style.cssText = 'background:#3b6fd4;color:#fff;border:none;padding:0.6em 1.5em;border-radius:6px;cursor:pointer;margin-right:0.5em;'; + uploadBtn.addEventListener('click', function () { document.body.removeChild(dialog); input.click(); }); + var cancelBtn = document.createElement('button'); + cancelBtn.textContent = 'Cancel'; + cancelBtn.style.cssText = 'background:#eee;color:#333;border:none;padding:0.6em 1.5em;border-radius:6px;cursor:pointer;'; + cancelBtn.addEventListener('click', function () { document.body.removeChild(dialog); }); + panel.appendChild(uploadBtn); panel.appendChild(cancelBtn); + dialog.appendChild(panel); + dialog.addEventListener('click', function (e) { if (e.target === dialog) document.body.removeChild(dialog); }); + document.body.appendChild(dialog); + } else { input.click(); } + input.addEventListener('change', function () { + if (!this.files[0]) return; + var fd = new FormData(); fd.append('file', this.files[0]); + fetch('/api/upload', { method: 'POST', body: fd }).then(r => r.json()).then(d => cb(d.location, { title: this.files[0].name })); + }); + }).catch(function () { input.click(); }); + }, + content_style: 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 16px; color: #000; background: #fff; }', + skin: 'oxide', + content_css: 'default', + promotion: false, + branding: false }); } if (document.getElementById('slideType').value === '0') initTinyMCE(); diff --git a/wwwroot/css/display.css b/wwwroot/css/display.css index c8d0638..0346edb 100644 --- a/wwwroot/css/display.css +++ b/wwwroot/css/display.css @@ -1,13 +1,30 @@ /* === Sunbeam Display Engine === */ * { margin: 0; padding: 0; box-sizing: border-box; } -html, body { width: 100%; height: 100%; overflow: hidden; background: #0a0a14; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; color: #ffffff; cursor: none; } -#display-container { position: relative; width: 100vw; height: 100vh; } +html, body { width: 100%; height: 100%; overflow: hidden; background: #000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; color: #ffffff; cursor: none; } + +/* === Scaling Wrapper — keeps aspect ratio, scales to fit screen === */ +#scale-wrapper { + width: var(--display-width); + height: var(--display-height); + position: absolute; + top: 50%; + left: 50%; + transform-origin: center center; + /* transform is set dynamically by JS */ +} + +/* === Slide Container === */ +#display-container { position: relative; width: 100%; height: 100%; } + +/* === Slide Layers === */ .slide-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; opacity: 0; transition: opacity 1s ease-in-out; z-index: 1; overflow: hidden; } .slide-layer.active { opacity: 1; z-index: 2; } body[data-transition="slide"] .slide-layer { transition: transform 0.8s ease-in-out, opacity 0.6s ease-in-out; transform: translateX(100%); } body[data-transition="slide"] .slide-layer.active { transform: translateX(0); } body[data-transition="slide"] .slide-layer.slide-out { transform: translateX(-100%); } body[data-transition="none"] .slide-layer { transition: none; } + +/* === Slide Content === */ .slide-inner { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-size: cover; background-position: center; background-repeat: no-repeat; } .slide-inner .content-wrap { width: 100%; height: 100%; overflow: hidden; } .slide-inner h1 { font-size: 3em; font-weight: 700; } @@ -19,6 +36,8 @@ body[data-transition="none"] .slide-layer { transition: none; } .slide-inner table td, .slide-inner table th { padding: 0.6em 1em; border: 1px solid rgba(255, 255, 255, 0.2); } .slide-inner table th { background: rgba(255, 255, 255, 0.1); font-weight: 600; } .slide-inner iframe.embed-frame { width: 100%; height: 100%; border: none; } + +/* === ICS Calendar === */ .ics-display { width: 100%; height: 100%; padding: 3em; display: flex; flex-direction: column; overflow: hidden; } .ics-display h2 { font-size: 2.4em; font-weight: 700; margin-bottom: 0.8em; display: flex; align-items: center; gap: 0.4em; } .ics-display h2::before { content: '\01F4C5'; } @@ -27,14 +46,42 @@ body[data-transition="none"] .slide-layer { transition: none; } .event-card .event-title { font-size: 1.3em; font-weight: 600; margin-bottom: 0.3em; } .event-card .event-time { font-size: 0.95em; opacity: 0.75; margin-bottom: 0.3em; } .event-card .event-location { font-size: 0.9em; opacity: 0.6; } + +/* === Progress Bar === */ #progress-bar { position: fixed; bottom: 0; left: 0; width: 100%; height: 4px; background: rgba(255, 255, 255, 0.1); z-index: 100; } #progress-fill { height: 100%; width: 0%; background: linear-gradient(90deg, #f0a030, #ff6b6b); transition: width 0.3s linear; } -#clock-overlay { position: fixed; bottom: 16px; right: 20px; z-index: 100; text-align: right; text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); pointer-events: none; } -#clock-time { display: block; font-size: 1.6em; font-weight: 600; opacity: 0.7; font-variant-numeric: tabular-nums; } -#clock-date { display: block; font-size: 0.85em; opacity: 0.5; } + +/* === Clock Overlay — centered, larger, dark grey === */ +#clock-overlay { + position: absolute; + bottom: 40px; + left: 50%; + transform: translateX(-50%); + z-index: 50; + text-align: center; + pointer-events: none; +} + +#clock-time { + display: block; + font-size: 2.4em; + font-weight: 600; + color: rgba(80, 80, 80, 0.6); + font-variant-numeric: tabular-nums; +} + +#clock-date { + display: block; + font-size: 1.1em; + color: rgba(80, 80, 80, 0.45); +} + +/* === Status Indicator === */ #status-dot { position: fixed; top: 12px; right: 12px; width: 8px; height: 8px; border-radius: 50%; background: #4caf50; z-index: 100; opacity: 0.6; } #status-dot.error { background: #f44336; animation: pulse 1.5s infinite; } @keyframes pulse { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } } + +/* === Loading / Empty === */ .loading-state { display: flex; align-items: center; justify-content: center; height: 100%; font-size: 1.5em; opacity: 0.5; } .loading-state::after { content: ''; width: 24px; height: 24px; border: 3px solid rgba(255,255,255,0.3); border-top-color: #f0a030; border-radius: 50%; animation: spin 0.8s linear infinite; margin-left: 0.8em; } @keyframes spin { to { transform: rotate(360deg); } } diff --git a/wwwroot/js/admin.js b/wwwroot/js/admin.js index 636be0e..8754ef2 100644 --- a/wwwroot/js/admin.js +++ b/wwwroot/js/admin.js @@ -2,7 +2,7 @@ // === Theme Toggle === (function () { - var saved = localStorage.getItem('sb-theme') || 'dark'; + var saved = localStorage.getItem('sb-theme') || 'light'; document.documentElement.setAttribute('data-theme', saved); document.addEventListener('DOMContentLoaded', function () { @@ -11,7 +11,7 @@ var btn = document.getElementById('themeToggle'); if (btn) { btn.addEventListener('click', function () { - var current = document.documentElement.getAttribute('data-theme') || 'dark'; + var current = document.documentElement.getAttribute('data-theme') || 'light'; var next = current === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', next); localStorage.setItem('sb-theme', next); @@ -30,17 +30,12 @@ } })(); -// Auto-dismiss alerts after 5 seconds +// Auto-dismiss alerts document.querySelectorAll('.alert-dismissible').forEach(function (alert) { - setTimeout(function () { - var bsAlert = bootstrap.Alert.getOrCreateInstance(alert); - bsAlert.close(); - }, 5000); + setTimeout(function () { var bsAlert = bootstrap.Alert.getOrCreateInstance(alert); bsAlert.close(); }, 5000); }); // Confirm dangerous actions document.querySelectorAll('[data-confirm]').forEach(function (el) { - el.addEventListener('click', function (e) { - if (!confirm(this.dataset.confirm)) e.preventDefault(); - }); + el.addEventListener('click', function (e) { if (!confirm(this.dataset.confirm)) e.preventDefault(); }); }); diff --git a/wwwroot/js/display.js b/wwwroot/js/display.js index 72390c7..60a3648 100644 --- a/wwwroot/js/display.js +++ b/wwwroot/js/display.js @@ -3,9 +3,24 @@ 'use strict'; var slug = document.body.dataset.slug; var transition = document.body.dataset.transition || 'fade'; + var designW = parseInt(document.body.dataset.width) || 1920; + var designH = parseInt(document.body.dataset.height) || 1080; var slides = window.playlistData || []; var currentIndex = -1, slideTimer = null, layerA = document.getElementById('slide-a'), layerB = document.getElementById('slide-b'), activeLayer = 'a', pollInterval = 30000, icsCache = {}; + // === Aspect-Ratio Scaling === + function scaleDisplay() { + var wrapper = document.getElementById('scale-wrapper'); + if (!wrapper) return; + var winW = window.innerWidth, winH = window.innerHeight; + var scaleX = winW / designW, scaleY = winH / designH; + var scale = Math.min(scaleX, scaleY); + wrapper.style.transform = 'translate(-50%, -50%) scale(' + scale + ')'; + } + scaleDisplay(); + window.addEventListener('resize', scaleDisplay); + + // === Clock === function updateClock() { var now = new Date(); var timeEl = document.getElementById('clock-time'), dateEl = document.getElementById('clock-date'); @@ -111,6 +126,6 @@ if (e.key === 'c') { document.body.style.cursor = document.body.style.cursor === 'default' ? 'none' : 'default'; } }); - console.log('[Sunbeam] Display engine starting for "' + slug + '" with ' + slides.length + ' slide(s)'); + console.log('[Sunbeam] Display engine starting for "' + slug + '" with ' + slides.length + ' slide(s) at ' + designW + 'x' + designH); showNextSlide(); setInterval(pollForUpdates, pollInterval); })(); diff --git a/wwwroot/lib/tinymce/CHANGELOG.md b/wwwroot/lib/tinymce/CHANGELOG.md new file mode 100644 index 0000000..c1cc774 --- /dev/null +++ b/wwwroot/lib/tinymce/CHANGELOG.md @@ -0,0 +1,3998 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), +and is generated by [Changie](https://github.com/miniscruff/changie). + +## 8.5.1 - 2026-05-19 + +### Security +- Fixed media plugin `data-mce-object` injection leading to stored XSS. #TINY-14357 +- Fixed stored XSS vulnerability through `mce:protected` comments. #TINY-14353 +- Fixed stored XSS vulnerability through `data-mce-` prefixed `src`, `href`, `style` attributes. #TINY-14333 + +## 8.5.0 - 2026-04-29 + +### Added +- New `content_language` option to set the `lang` attribute on the iframe's `html` element or the inline editor's target element. #TINY-11214 + +### Improved +- Improved visual styling of inline diff highlights in Suggested Edits and TinyMCE AI plugin. #TINY-13958 + +### Fixed +- Script and style elements would incorrectly be removed by DomPurify when considered valid in the schema. #TINY-9655 +- Iframe elements with children would incorrectly be removed by DomPurify. #TINY-9655 +- Certain combinations of divs inside of lists would cause issues turning off lists. #TINY-14070 +- Certain selections would delete the editor body, causing issues. #TINY-14149 +- URIs with non-Latin1 characters were returning an error. #TINY-13938 +- Alert and confirm dialogs were not announced properly by some screen readers. #TINY-13812 + +## 8.4.0 - 2026-03-31 + +### Added +- New `view_show` option to display a specified view on initialization. #TINY-11967 +- New `errorHandler` option for `dropzone` dialog components. #TINY-13420 +- The noneditable feature can now be disabled with the new `allow_noneditable` option. #TINY-10121 +- Editor option `content_id` for uniquely identifying the edited document. #TINY-13379 +- New `table_default_header_rows` and `table_default_header_cols` options to set the default header size for new tables #TINY-13391 + +### Improved +- The file upload feature of `link` and `image` dialogs now provide feedback when an unsupported file type is selected. #TINY-13420 +- Directionality buttons now only appear active when directionality is set on the selected block. #TINY-13337 +- Directionality buttons now always toggle the directionality attribute on selected blocks. #TINY-13337 + +### Changed +- The `border-color` style with multiple rgb colors would be compressed into `border` incorrectly #TINY-13393 +- Element Path now uses the ARIA-role "group" with an aria-label #TINY-13338 + +### Fixed +- Now `link` dialog allows uploading empty files. #TINY-13421 +- The `link` dialog now allows uploading empty files. #TINY-13421 +- Bundled content CSS is now loaded into preview iframes. #TINY-13190 +- Attempting to remove a link after a `contentEditable="false"` element was not possible. #TINY-13108 +- Some formatting was not applied to items in multilevel list. #TINY-13324 +- Dialogs did not retain focus when using the block/unblock API. #TINY-12918 +- Setting width via Table Properties did not apply when `table_style_by_css` was `false`. #TINY-12797 + +### Security +- Updated DOMPurify version to 3.3.2. #TINY-14180 + +## 8.3.2 - 2026-01-14 + +### Fixed +- The `uc-video` element is no longer refreshed on undo/redo. #TINY-12884 + +## 8.3.1 - 2025-12-17 + +### Fixed +- Views could not be opened during the `init` event. #TINY-13463 + +## 8.3.0 - 2025-12-10 + +### Added +- New `documents_file_types` option. #TINY-13278 +- New `files_upload_handler` and `link_uploadtab` options in the Link plugin. #TINY-13278 +- New upload tab to the link dialog when `files_upload_handler`, `documents_file_types`, and `link_uploadtab` are all specified. #TINY-13278 +- New options for `dropzone` dialog components: `dropAreaLabel`, `buttonLabel`, `allowedFileTypes` and `allowedFileExtensions`. #TINY-13278 + +### Improved +- Accordions can now be opened and closed when the editor is in a read-only state. #TINY-12316 +- An editor notification is now shown when a premium plugin is not allowed for a given license key. #TINY-12937 + +### Fixed +- Help Plugin did not contain an entry for the Fullpage HTML plugin. #TINY-13183 +- Accordions could be toggled when the editor was in a disabled state. #TINY-12315 +- Removing a line height sometimes did not remove it from all selected lines. #TINY-13053 +- The `font-size` style on `li` elements was not copied to new list items. #TINY-13224 +- Toolbar split button chevron tooltip is now the same as the main button tooltip unless explicitly set. #TINY-13271 +- Inline formats were not applied to the marker of a list item when its content was a single block element. #TINY-13197 + +## 8.2.2 - 2025-11-17 + +### Fixed +- Ability to load licensekeymanager in different hosting scenarios. #TINY-13203 + +## 8.2.1 - 2025-11-06 + +### Fixed +- Resizing the editor's height would add a fixed width value. #TINY-13138 +- Help Plugin did not contain an entry for the Fullpage HTML plugin. #TINY-13183 + +## 8.2.0 - 2025-10-23 + +### Improved +- Some elements would be given an extra newline by the serializer when indented. #TINY-12857 + +### Fixed +- UI elements like focus outlines and placeholders would be visible after printing. #TINY-12584 +- The `open` attribute on `
    ` elements is now normalized to `open="open"` when the accordion plugin is enabled. #TINY-12862 +- Tooltips on toolbar buttons sometimes remained visible if the button icon was updated while hovered. #TINY-12289 +- The editor would upon gaining focus scroll to the center of the editor on some browsers if the top of the editor was out of frame. #TINY-12626 +- Some UI elements related to dragging elements were not properly filtered out when fetching content. #TINY-12384 +- The cursor could get stuck around an absolute CEF element when navigating using arrow keys. #TINY-10526 +- The schema will now allow the `property` RDFa attribute on `meta` elements. #TINY-12858 +- Corrected type of `undoManager.add` method. The `event` parameter is type `EditorEvent` not `Event`. #TINY-12936 +- Chromium browsers would in certain situations scroll the editor unexpectedly when dragging content over the editor. #INT-3373 +- Support for setting component urls for custom elements. #TINY-13006 +- Reverted toolbar button width to `auto`. #TINY-13097 +- Addressed split buttons rendering narrower than in TinyMCE 7, ensuring consistent sizing across the toolbar. #TINY-13111 + +## 8.1.2 - 2025-09-18 + +### Fixed +- Some sidebar UI components were not rendering correctly. #TINY-12925 + +## 8.1.1 - 2025-09-17 + +### Fixed +- Reset rules for CSS were not applying correctly. #TINY-12920 + +## 8.1.0 - 2025-09-17 + +### Improved +- The editor `DomParser` API can now parse full documents when the API's `root_name` option is set to `#document`. #TINY-12589 +- Tooltips can be hovered over to keep open. #TINY-12053 +- Default avatars are now generated with a consistent color based on the user id. #TINY-12532 +- Reverted split button design change. #TINY-12593 +- `editor.getContent()` now includes `indent` and `entity_encoding` properties to control HTML formatting. #TINY-12786 + +### Deprecated +- Deprecated `content_css_cors` config option. #TINY-12578 + +### Fixed +- Clicking on a non selectable element when the selection is off screen no longer scrolls to the selection. #TINY-12245 +- Using CMD+backspace would not preserve inline formatting. #TINY-12071 +- Navigating between elements with `contenteditable="true"` was not possible on Firefox using the keyboard. #TINY-12459 +- NVDA would announce `iframe_aria_text` multiple times. #TINY-11296 +- Cursor movement did not operate correctly after a `figure` was selected. #TINY-12458 +- Links inserted with `&` encoding are now decoded to `&` before inserting. #TINY-12504 +- Attribute `aria-errormessage` was incorrectly added to url input fields by default. #TINY-12037 +- Splitting list items by pressing enter did not move the cursor due to an error in the browser console. #TINY-12830 + +## 8.0.2 - 2025-08-14 + +### Fixed +- Reverted split button design change. #TINY-12593 + +## 8.0.1 - 2025-07-28 + +### Fixed +- Updated missing changelogs. #TINY-12581 + +## 8.0.0 - 2025-07-23 + +### Added +- New `pageUid` property on the `tinymce` global instance. #TINY-12021 +- New `editorUid` property on editor instances. #TINY-12021 +- New `UserLookup` API to provide consistent user management and fetching/caching user information across TinyMCE plugins. #TINY-11974 +- New toolbar icons for suggested edits plugin. #TINY-12070 +- New `crossorigin` option that sets the `crossorigin` attribute on scripts loaded by the editor. #TINY-12228 +- New `allow_html_in_comments` option to allow HTML like contents inside comment data. #TINY-12220 +- New `list_max_depth` option that limits the maximum list indent depth. #TINY-11937 +- Support for TinyMCE 8 license keys and license key manager. #TINY-12001 + +### Improved +- Focus is now restored to a dialog after closing an alert, confirmation or another dialog. #TINY-12038 +- Resize handles are more accessible with `role` and `aria-valuetext` attributes. #TINY-11421 +- Toolbar drawer now closes when the editor loses focus. #TINY-12125 +- The `translate` API now automatically replaces three dots in a row with an ellipsis character. #TINY-12155 +- Tooltips can now be closed by pressing the escape key. #TINY-12054 +- Instructions on how to navigate the color swatch, image select and insert table widget are now announced by the screen readers. #TINY-12189 +- When `editor.resetContent()` is used, the resulting `SetContent` event now has the `initial` flag set in the event arguments. #TINY-12146 +- Updated Image plugin decorative image check to align with accessibility standard #TINY-12545 + +### Changed +- DomParser no longer tries to fix some nodes when parsed with a context. #TINY-11927 +- Split buttons are now rendered as separate action and chevron buttons. #TINY-8665 +- Moved essential parts of the `lists` plugin into core. #TINY-12121 +- Cut action now uses the editor delete implementation instead of the native `execCommand('Delete')` function. #TINY-12171 +- Comments with HTML like content, attributes with HTML like values, elements with HTML Comment like textContent are no longer retained while content sanitization is active. #TINY-12056 +- Added space to promotion message for ease of reading. #TINY-12271 + +### Deprecated +- Deprecated `skipFocus` option for ToggleToolbarDrawer command. To skip focus use `skip_focus` option. #TINY-12044 +- Deprecated 'editor.selection.setContent' method. #TINY-11692 + +### Removed +- Removed undocumented `documentBaseUrl` property from `Editor` instances. `documentBaseURI` is the supported property. #TINY-12182 + +### Fixed +- Long tooltips could overflow narrow browser windows. #TINY-11884 +- Pressing Tab to create a new row in tables with a non-editable final row would freeze the editor. #TINY-12018 +- Fixed an issue where the context form API `onSetup` was referencing the incorrect element. #TINY-11912 +- Attempting to add a newline directly after a block element such as an image would cause an error. #TINY-12045 +- It wasn't possible to navigate out of a `figcaption` using the left and right arrow keys in Firefox. #TINY-11982 +- Nested font sizes were causing too much spacing between lines. #TINY-12073 +- New list item was not inserted correctly when existing list item had a block element inside. #TINY-12102 +- Deleting a whole element would sometimes replace the tag name of adjacent content. #TINY-11868 +- HTML schema did not treat `colgroup` or `col` elements as block elements. #TINY-12092 +- Inserting HTML fragments with invalid contents now retains more of the structure. #TINY-11927 +- The `contextform` subtoolbar is no longer dismissed when using the toolbar back button in inline editors. #TINY-12118 +- Tabbing when a `figure` was selected didn't move the selection on the `figcaption` correctly. #TINY-11753 +- Strikethrough format could be added outside font size format, which renders incorrectly in some browsers. #TINY-12004 +- Using arrow navigation between buttons when a button had the mouse over could stop the navigation. #TINY-12163 +- Codesample dialog would not disable spellcheck as expected. #TINY-12120 +- Cutting a whole HTML element would add an empty paragraph. #TINY-12171 +- Added better support for template elements. #TINY-12157 +- Tooltips no longer show above editor views. #TINY-12289 +- `onboarding` plugin overlay now shows above popups. #TINY-12522 + +## 7.9.1 - 2025-05-29 + +### Improved +- Update `Notices` file and minified notices. #TINY-12091 + +## 7.9.0 - 2025-05-15 + +### Added +- Added new `disc` style option for unordered lists. #TINY-12015 + +### Improved +- The resize cursor now points in the correct direction for each resize mode. Patch contributed by daniloff200. ##GH-10189 +- If `style_formats` is empty, the button is now disabled. #TINY-12005 +- Inline dialog dropdowns reposition when the dialog is dragged or the window is scrolled. #TINY-11368 +- Bullet list icons were have been updated to better represent the default styles. #TINY-12014 + +### Changed +- The ContextFormSizeInput lock button is now centered instead of aligned to the end. #TINY-11916 +- Changed the default value of `advlist_bullet_styles` option to `default,disc,circle,square`. #TINY-12083 + +### Fixed +- Autolink no longer overrides already existing links when autolinking. #TINY-11836 +- Removed the deprecated CSS media selector `-ms-high-contrast`. #TINY-11876 +- The `mceInsertContent` command no longer deletes the parent block element when an anchor is selected. #TINY-11953 +- Table resizers are now visible when inline editor has a z-index property. #TINY-11981 +- Tabbing inside a `figcaption` element no longer displays two text insertion carets. #TINY-11997 +- Pressing Enter before a floating image no longer duplicates the image. #TINY-11676 +- Editor did not scroll into viewport on receiving focus on Chrome and Safari. #TINY-12017 +- Select UI elements was not properly styled on Chrome version 136. #TINY-12131 + +## 7.8.0 - 2025-04-09 + +### Added +- New subtoolbar support for context toolbars. #TINY-11748 +- New `extended_mathml_attributes` and `extended_mathml_elements` options. #TINY-11756 +- New `onboarding` option. #TINY-11931 + +### Improved +- Focus outline was misaligned with comment card border on saving an edit. #TINY-11329 +- The `editor.selection.scrollIntoView()` method now pads the target scroll area with a small margin, ensuring content doesn't sit at the very edge of the viewport. #TINY-11786 + +### Changed +- Changed promotional text and link. #TINY-11905 + +### Fixed +- Setting editor height to a `pt` or `em` value was ignoring min/max height settings. #TINY-11108 + +## 7.7.2 - 2025-03-19 + +### Fixed +- Error was thrown when pressing tab in the last cell of a non-editable table. #TINY-11797 +- Error was thrown when trying to use the context form API after a component was detached. #TINY-11781 +- Deleting an empty block within an
  • element would move cursor to the end of the
  • . #TINY-11763 +- Deleting an empty block that was between two lists would throw an Error when all three elements were nested inside a list. #TINY-11763 + +## 7.7.1 - 2025-03-05 + +### Fixed +- Skin UI content CSS was truncated when bundling, causing CSS styles to be missing. #TINY-11875 +- Context forms used to disappear if their input was disabled in the `onSetup` API. #TINY-11890 + +## 7.7.0 - 2025-02-20 + +### Added +- `link_attributes_postprocess` option that allows overriding attributes of a link that would be inserted through the link dialog. #TINY-11707 + +### Improved +- Improved visual indication of keyboard focus in annotations that contain an image. #TINY-11596 +- The type now defaults to `info` when `editor.notificationManager.open()` is used without a specified type or with an invalid one. #TINY-11661 + +### Changed +- Updated the `link` plugin behavior to move the cursor outside of the link when inserted or edited via the UI. Patch contributed by Philipp91. #GH-9998 + +### Fixed +- Keyboard navigation for size inputs in context forms. #TINY-11394 +- Keyboard navigation for context form sliders. #TINY-11482 +- The `insertContent` API was not replacing selected non-editable elements correctly. #TINY-11714 +- Context toolbar inputs had incorrect margins. #TINY-11624 +- Iframe aria text no longer suggests opening the help dialog when the help plugin is not enabled. #TINY-11672 +- Preview dialog no longer opens anchor links in a new tab. #TINY-11740 +- The `float` property was not properly removed on the image when converting a image into a captioned image. #TINY-11670 +- Expanding selection to word didn't work inside inline editing host elements. #TINY-11304 +- The `semantics` element in MathML was not properly retained when `annotation` elements were allowed. #TINY-11755 +- It was possible to tab to a toolbar group that had all children disabled. #TINY-11665 +- Keyboard navigation would get stuck on the 'more' toolbar button. #TINY-11762 +- Toolbar groups had both a `title` attribute and a custom tooltip, causing overlapping tooltips #TINY-11768 +- Toolbar text field did not render focus correctly. #TINY-11658 + +## 7.6.1 - 2025-01-22 + +### Fixed +- Text input was prevented in form elements in the contents of the editor. #TINY-11446 +- Opening a notification when the toolbar is positioned at the bottom of the editor threw an error. #TINY-11498 +- Table resize bars were not properly aligned for inline editors inside scrollable containers. #TINY-11215 + +## 7.6.0 - 2024-12-11 + +### Added +- It is now possible to create labeled groups in context toolbars. #TINY-11095 +- New `contextsliderform` and `contextsizeinput` context form types. #TINY-11342 +- New `back` function in `ContextFormApi` to go back to the previous toolbar. #TINY-11344 +- New `QuickbarInsertImage` command that is executed by the `quickimage` button. #TINY-11399 +- New `onSetup` function to the context form API. #TINY-11494 +- New `placeholder` to the context form input field API. #TINY-11459 +- New `disabled` option to restore the previous `readonly` mode behavior, allowing the editor to be displayed in a disabled state. #TINY-11488 + +### Improved +- Base64 data was not properly decoded due to unhandled URL-encoded characters. #TINY-9548 +- The `latin` list style type is now recognized as an alias for the `alpha` list style type. #TINY-11515 + +### Fixed +- Image selection was removed when calling `editor.nodeChanged()` while having focus inside the editor UI. #TINY-11437 +- Tooltip would not show for group toolbar button. #TINY-11391 +- Changing the table row type when a `contenteditable=false` cell was selected would not work as expected. #TINY-11383 +- The `samp` format was being applied as a `block` level format, instead of an `inline` format. #TINY-11390 +- Removed title attribute from dialog tree elements as they already have a tooltip. #TINY-11470 +- Fixed CSS bundling for skin UI content CSS. #TINY-11558 +- Fixed incorrect resource keys for CSS bundling JS files. #TINY-11558 + +## 7.5.0 - 2024-11-06 + +### Added +- Added support for using raw CSS in the list of possible colours, using the `color_map_raw` property. #GH-9788 + +### Improved +- Improved color picker aria support. #TINY-11291 + +### Fixed +- Autocompleter would not activate after applying an inline format like font size in some cases. #TINY-11273 +- The `toolbar-sticky-offset` would still be applied after entering fullscreen mode. #TINY-11137 +- Text and background color toolbar buttons would not be fully greyed out in readonly mode. #TINY-11313 +- Closing a nested modal dialog would lose focus from the editor. #TINY-11153 +- Inability to type '{' character on German keyboard layouts. #TINY-11395 + +## 7.4.1 - 2024-10-10 + +### Fixed +- Invalid HTML elements within SVG elements were not removed. #TINY-11332 + +## 7.4.0 - 2024-10-09 + +### Added +- New `context` property for all ui components. This allows buttons and menu items to be enabled or disabled based on whether their context matches a given predicate; status updates are checked on `init`, `NodeChange`, and `SwitchMode` events. #TINY-11211 +- Tree component now allows the addition of a custom icon. #TINY-11131 +- Added focus function to view button api. #TINY-11122 +- New option `allow_mathml_annotation_encodings` to opt-in to keep math annotations with specific encodings. #TINY-11166 +- Added global `color-active` LESS variable for use in editor skins. #TINY-11266 + +### Improved +- In read-only mode the editor now allows normal cursor movement and block element selection, including video playback. #TINY-11264 +- Pasting a table now places the cursor after the table instead of into the last cell. #TINY-11082 +- Dialog list dropdown menus now close when the browser window resizes. #TINY-11123 + +### Fixed +- Mouse hover on partially visible dialog collection elements no longer scrolls. #TINY-9915 +- Caret would unexpectedly shift to the non-editable table row above when pressing Enter. #TINY-11077 +- Deleting a selection in a list element would sometimes prevent the `input` event from being dispatched. #TINY-11100 +- Placing the cursor after a table with a br after it would misplace added newlines before the table instead of after. #TINY-11110 +- Sidebar could not be toggled until the skin was loaded. #TINY-11155 +- The image dialog lost focus after closing an image upload error alert. #TINY-11159 +- Copying tables to the clipboard did not correctly separate cells and rows for the "text/plain" MIME type. #TINY-10847 +- The editor resize handle was incorrectly rendered when all components were removed from the status bar. #TINY-11257 + +## 7.3.0 - 2024-08-07 + +### Added +- Colorpicker number input fields now show an error tooltip and error icon when invalid text has been entered. #TINY-10799 +- New `format-code` icon. #TINY-11018 + +### Improved +- When a full document was loaded as editor content the head elements were added to the body. #TINY-11053 + +### Fixed +- Unnecessary nbsp entities were inserted when typing at the edges of inline elements. #TINY-10854 +- Fixed JavaScript error when inserting a table using the context menu by adjusting the event order in `renderInsertTableMenuItem`. #TINY-6887 +- Notifications didn't position and resize properly when resizing the editor or toggling views. #TINY-10894 +- The pattern commands would execute even if the command was not enabled. #TINY-10994 +- Split button popups were incorrectly positioned when switching to fullscreen mode if the editor was inside a scrollable container. #TINY-10973 +- Sequential html comments would in some cases generate unwanted elements. #TINY-10955 +- The listbox component had a fixed width and was not a responsive ui element. #TINY-10884 +- Prevent default mousedown on toolbar buttons was causing misplaced focus bugs. #TINY-10638 +- Attempting to use focus commands on an editor where the cursor had last been in certain contentEditable="true" elements would fail. #TINY-11085 +- Colorpicker's hex-based input field showed the wrong validation error message. #TINY-11115 + +## 7.2.1 - 2024-07-03 + +### Fixed +- Text content could move unexpectedly when deleting a paragraph. #TINY-10590 +- Cursor would shift to the start of the editor body when focus was shifted to a noneditable cell of a table. #TINY-10127 +- Long translations of the bottom help text would cause minor graphical issues. #TINY-10961 +- Open Link button was disabled when selection partially covered a link or when multiple links were selected. #TINY-11009 + +## 7.2.0 - 2024-06-19 + +### Added +- Added `options.debug` API that logs the initial raw editor options to console. #TINY-10605 +- Added `referrerpolicy` as a valid attribute for an iframe element. #TINY-10374 +- New `onInit` and `stretched` properties to the `HtmlPanel` dialog component. #TINY-10900 +- Added support for querying the state of the `mceTogglePlainTextPaste` command. #TINY-10938 +- Added `for` option to dialog label components to improve accessibility. The value must be another component on the same dialog. #TINY-10971 + +### Improved +- Dialog slider components now emit an onChange event when using arrow keys. #TINY-10428 +- Accessibility for element path buttons, added tooltip to describe the button and removed incorrect `aria-level` attribute. #TINY-10891 +- Improve merging of inserted inline elements by removing nodes with redundant inheritable styles. #TINY-10869 +- Improved Find & Replace dialog accessibility by changing placeholders to labels. #TINY-10871 + +### Changed +- Replaced tiny branding logo with `Build with TinyMCE` text and logo. #TINY-11001 + +### Fixed +- Deleting in a `div` with preceeding `br` elements would sometimes throw errors. #TINY-10840 +- `autoresize_bottom_margin` was not reliably applied in some situations. #TINY-10793 +- Fixed cases where adding a newline around a br, table or img would not move the cursor to a new line. #TINY-10384 +- Focusing on `contenteditable="true"` element when using `editable_root: false` and inline mode causing selection to be shifted. #TINY-10820 +- Corrected the `role` attribute on listbox dialog components to `combobox` when there are no nested menu items. #TINY-10807 +- HTML entities that were double decoded in `noscript` elements caused an XSS vulnerability. #TINY-11019 +- It was possible to inject XSS HTML that was not matching the regexp when using the `noneditable_regexp` option. #TINY-11022 + +## 7.1.2 - 2024-06-05 + +### Fixed +- CSS color values set to `transparent` were incorrectly converted to '#000000`. #TINY-10916 + +## 7.1.1 - 2024-05-22 + +### Fixed +- Insert/Edit image dialog lost focus after the image upload completed. #TINY-10885 +- Deleting into a list from a paragraph that has an `img` tag could cause extra inline styles to be added. #TINY-10892 +- Resolved an issue where emojis configured with the `emojiimages` database were not loading correctly due to a broken CDN. #TINY-10878 +- Iframes in dialogs were not rendering rounded borders correctly. #TINY-10901 +- Autocompleter possible values are no longer capped at a length of 10. #TINY-10942 + +## 7.1.0 - 2024-05-08 + +### Added +- Parser support for math elements. #TINY-10809 +- New `math-equation` icon. #TINY-10804 + +### Improved +- Included `itemprop`, `itemscope` and `itemtype` as valid HTML5 attributes in the core schema. #TINY-9932 +- Notification accessibility improvements: added tooltips, keyboard navigation and shortcut to focus on notifications. #TINY-6925 +- Removed `aria-pressed` from the `More` button in sliding toolbar mode and replaced it with `aria-expanded`. #TINY-10795 +- The editor UI now renders correctly in Windows High Contrast Mode. #TINY-10781 + +### Fixed +- Backspacing in certain html setups resulted in data moving around unexpectedly. #TINY-10590 +- Dialog title markup changed to use an `h1` element instead of `div`. #TINY-10800 +- Dialog title was not announced in macOS VoiceOver, dialogs now use `aria-label` instead of `aria-labelledby` on macOS. #TINY-10808 +- Theme loader did not respect the suffix when it was loading skin CSS files. #TINY-10602 +- Custom block elements with colon characters would throw errors. #TINY-10813 +- Tab navigation in views didn't work. #TINY-10780 +- Video and audio elements could not be played on Safari. #TINY-10774 +- `ToggleToolbarDrawer` command did not toggle the toolbar in `sliding` mode when `{skipFocus: true}` parameter was passed. #TINY-10726 +- The buttons in the custom view header were clipped on when overflowing. #TINY-10741 +- In the custom view, the scrollbar of the container was not visible if its height was greater than the editor. #TINY-10741 +- Fixed accessibility issue by removing duplicate `role="menu"` attribute from color swatches. #TINY-10806 +- Fullscreen mode now prevents focus from leaving the editor. #TINY-10597 +- Open link context menu action did not work with selection surrounding a link. #TINY-10391 +- Styles were not retained when toggling a list on and off. #TINY-10837 +- Caret and placeholder text were invisible in Windows High Contrast Mode. #TINY-9811 +- Firefox did not announce the iframe title when `iframe_aria_text` was set. #TINY-10718 +- Notification width was not constrained to the width of the editor. #TINY-10886 +- Open link context menu action was not enabled for links on images. #TINY-10391 + +## 7.0.1 - 2024-04-10 + +### Fixed +- Toggle list behavior generated wrong html when the `forced_root_block` option was set to `div`. #TINY-10488 +- Tapping inside a composed text on Firefox Android would not close the autocompleter. #TINY-10715 +- An inline editor toolbar now behaves correctly in horizontally scrolled containers. #TINY-10684 +- Tooltips unintended shrinking and incorrectly positioned when shown in horizontally scrollable container. #TINY-10797 +- The status bar was invisible when the editor's height is short. #TINY-10705 + +## 7.0.0 - 2024-03-20 + +### Added +- New `license_key` option that must be set to `gpl` or a valid license key. #TINY-10681 +- New custom tooltip functionality, tooltip will be shown when hovering with a mouse or with keyboard focus. #TINY-9275 +- New `sandbox_iframes_exclusions` option that holds a list of URL host names to be excluded from iframe sandboxing when `sandbox_iframes` is set to `true`. #TINY-10350 +- Added 'getAllEmojis' api function to the emoticons plugin. #TINY-10572 +- Element preset support for the `valid_children` option and Schema.addValidChildren API. #TINY-9979 +- A new `trigger` property for block text pattern configurations, allowing pattern activation with either Space or Enter keys. #TINY-10324 +- onFocus callback for CustomEditor dialog component. #TINY-10596 +- icons for the import from Word, export to Word and export to PDF premium plugins. #TINY-10612 +- `data` is now a valid element in the Schema. #TINY-10611 +- More advanced schema config for custom elements. #TINY-9980 +- Custom tooltip for autocompleter, now visible on both mouse hover and keyboard focus, except single column cases. #TINY-9638 + +### Improved +- Included keyboard shortcut in custom tooltip for `ToolbarButton` and `ToolbarToggleButton`. #TINY-10487 +- Improved showing which element has focus for keyboard navigation. #TINY-9176 +- Custom tooltips will now show for items in `collection` which is rendered inside a dialog, on mouse hover and keyboard focus. #TINY-9637 +- Autocompleter will now work with IMEs. #TINY-10637 +- Make table ghost element better reflect height changes when resizing. #TINY-10658 + +### Changed +- TinyMCE is now licensed GPL Version 2 or later. #TINY-10578 +- `convert_unsafe_embeds` editor option is now defaulted to `true`. #TINY-10351 +- `sandbox_iframes` editor option is now defaulted to `true`. #TINY-10350 +- The DOMUtils.isEmpty API function has been modified to consider nodes containing only comments as empty. #TINY-10459 +- The `highlight_on_focus` option now defaults to true, adding a focus outline to every editor. #TINY-10574 +- Delay before the tooltip to show up, from 800ms to 300ms. #TINY-10475 +- Now `tox-view__pane` has `position: relative` instead of `static`. #TINY-10561 +- Update outbound link for statusbar Tiny logo #TINY-10494 +- Remove the height field from the `table` plugin cell dialog. The `table` plugin row dialog now controls the row height by setting the height on the `tr` element, not the `td` elements. #TINY-10617 +- Change table height resizing handling to remove heights from `td`/`th` elements and only apply to `tr` elements. #TINY-10589 +- Removed incorrect `aria-placeholder` attribute from editor body when `placeholder` option is set. #TINY-10452 +- The `tooltip` property for dialog's footer `togglebutton` is now optional. #TINY-10672 +- Changed the `media_url_resolver` option to use promises. #TINY-9154 +- `Styles` bespoke toolbar button fallback changed to `Formats` if `Paragraph` is not configured in `style_formats` option. #TINY-10603 +- Updated deprecation/removed console message. #TINY-10694 + +### Removed +- Deprecated `force_hex_color` option, with the default now being all colors are forced to hex format as lower case. #TINY-10436 +- Deprecated `remove_trailing_brs` option from DomParser. #TINY-10454 +- `title` attribute on buttons with visible label. #TINY-10453 +- `InsertOrderedList` and `InsertUnorderedList` commands from core, these now only exist in the `lists` plugin. #TINY-10644 +- `closeButton` from the notification API, close buttons in notifications are now required. #TINY-10646 +- The autocompleter `ch` configuration property has been removed. Use the `trigger` property instead. #TINY-8929 +- Deprecated `template` plugin. #TINY-10654 + +### Fixed +- When deleting the last row in a table, the cursor would jump to the first cell (top left), instead of moving to the next adjacent cell in some cases. #TINY-6309 +- Heading formatting would be partially applied to the content within the `summary` element when the caret was positioned between words. #TINY-10312 +- Moving focus to the outside of the editor after having clicked a menu would not fire a `blur` event as expected. #TINY-10310 +- Autocomplete would sometimes cause corrupt data when starting during text composition. #TINY-10317 +- Inline mode with persisted toolbar would show regardless of the skin being loaded, causing css issues. #TINY-10482 +- Table classes couldn't be removed via setting an empty value in `table_class_list`. Also fixed being forced to pick the first class option. #TINY-6653 +- Directly right clicking on a ol's li in FireFox didn't enable the button `List Properties...` in the context menu. #TINY-10490 +- The `link_default_target` option wasn't considered when inserting a link via `quicklink` toolbar. #TINY-10439 +- When inline editor toolbar wrapped to multiple lines the top wasn't always calculated correctly. #TINY-10580 +- Removed manually dispatching dragend event on drop in Firefox. #TINY-10389 +- Slovenian help dialog content had a dot in the wrong place. #TINY-10601 +- Pressing Backspace at the start of an empty `summary` element within a `details` element nested in a list item no longer removes the `summary` element. #TINY-10303 +- The toolbar width was miscalculated for the inline editor positioned inside a scrollable container. #TINY-10581 +- Fixed incorrect object processor for `event_root` option. #TINY-10433 +- Adding newline after using `selection.setContent` to insert a block element would throw an unhandled exception. #TINY-10560 +- Floating toolbar buttons in inline editor incorrectly wrapped into multiple rows on window resizing or zooming. #TINY-10570 +- When setting table border width and `table_style_by_css` is true, only the border attribute is set to 0 and border-width styling is no longer used. #TINY-10308 +- Clicking to the left or right of a non-editable div in Firefox would show two cursors. #TINY-10314 + +## 6.8.3 - 2024-02-08 + +### Changed +- Update outbound TinyMCE website links. #TINY-10491 + +### Fixed +- The floating toolbar would not be fully visible when the editor was placed inside a scrollable container. #TINY-10335 +- ShadowDOM skin was not loaded properly when used with js bundling feature. #TINY-10451 + +## 6.8.2 - 2023-12-11 + +### Fixed +- Bespoke select toolbar buttons including `fontfamily`, `fontsize`, `blocks`, and `styles` incorrectly used plural words in their accessible names. #TINY-10426 +- The `align` bespoke select toolbar button had an accessible name that was misleading and grammatically incorrect in certain cases. #TINY-10435 +- Accessible names of bespoke select toolbar buttons including `align`, `fontfamily`, `fontsize`, `blocks`, and `styles` were incorrectly translated. #TINY-10426 #TINY-10435 +- Clicking inside table cells with heavily nested content could cause the browser to hang. #TINY-10380 +- Toggling a list that contains an LI element having another list as its first child would remove the remaining content within that LI element. #TINY-10414 + +## 6.8.1 - 2023-11-29 + +### Improved +- Colorpicker now includes the Brightness/Saturation selector and hue slider in the keyboard navigable items. #TINY-9287 + +### Fixed +- Translation syntax for announcement text in the table grid was incorrectly formatted. #TINY-10141 +- The functions `schema.isWrapper` and `schema.isInline` did not exclude node names that started with `#` which should not be considered as elements. #TINY-10385 + +## 6.8.0 - 2023-11-22 + +### Added +- CSS files are now also generated as separate JS files to improve bundling of all resources. #TINY-10352 +- Added new `StylesheetLoader.loadRawCss` API that can be used to load CSS into a style element. #TINY-10352 +- Added new `StylesheetLoader.unloadRawCss` API that can be used to unload CSS that was loaded into a style element. #TINY-10352 +- Added `force_hex_color` editor option. Option `'always'` converts all RGB & RGBA colours to hex, `'rgb_only'` will only convert RGB and *not* RGBA colours to hex, `'off'` won't convert any colours to hex. #TINY-9819 +- Added `default_font_stack` editor option that makes it possible to define what is considered a system font stack. #TINY-10290 +- New `sandbox_iframes` option that controls whether iframe elements will be added a `sandbox=""` attribute to mitigate malicious intent. #TINY-10348 +- New `convert_unsafe_embeds` option that controls whether `` and `` elements will be converted to more restrictive alternatives, namely `` for image MIME types, `