From 2a9df13fe38a684f4a06da0cf18ba68c114d400a Mon Sep 17 00:00:00 2001 From: Vincent CLAVIEN Date: Wed, 15 Jan 2025 00:01:52 +0100 Subject: [PATCH] wip --- dev/public/js/init.js | 30 + dev/public/js/libs.js | 36 + dev/public/js/main.js | 1513 ----------------- dev/public/js/stores.js | 888 ++++++++++ dev/public/js/strings.js | 46 +- dev/public/js/utils.js | 678 ++++++++ dev/src/components/MobileMenu.astro | 6 +- dev/src/components/PagingBottom.astro | 16 +- dev/src/components/PagingOptions.astro | 10 +- dev/src/components/PagingTop.astro | 20 +- dev/src/components/SvgSprites.astro | 29 +- dev/src/components/panels/HeaderPanel.astro | 14 +- dev/src/components/panels/ToolsPanel.astro | 60 +- .../panels/actor/LikesBookmarks.astro | 2 +- dev/src/components/panels/tools/About.astro | 7 + dev/src/components/panels/tools/AppLog.astro | 13 + .../components/panels/tools/AppSettings.astro | 39 + .../components/panels/tools/ManageFiles.astro | 44 + .../components/screens/WelcomeScreen.astro | 9 +- dev/src/css/_actor.scss | 10 +- dev/src/css/_colors-mixins.scss | 12 + dev/src/css/_colors.scss | 6 +- dev/src/css/_global.scss | 64 +- dev/src/css/_layout.scss | 12 +- dev/src/css/_menu.scss | 71 +- dev/src/css/_mixins.scss | 5 +- dev/src/css/_tags.scss | 9 +- dev/src/css/_tools.scss | 200 ++- dev/src/css/_toot.scss | 7 +- dev/src/css/_welcome.scss | 4 - dev/src/pages/index.astro | 5 +- dist/_astro/index.33CKCK6j.css | 1 + dist/_astro/index.uwcihdcd.css | 1 - dist/index.html | 18 +- dist/js/init.js | 30 + dist/js/libs.js | 36 + dist/js/main.js | 1507 ---------------- dist/js/stores.js | 888 ++++++++++ dist/js/strings.js | 208 ++- dist/js/utils.js | 678 ++++++++ readme.md | 3 + 41 files changed, 3932 insertions(+), 3303 deletions(-) create mode 100644 dev/public/js/init.js create mode 100644 dev/public/js/libs.js delete mode 100644 dev/public/js/main.js create mode 100644 dev/public/js/stores.js create mode 100644 dev/public/js/utils.js create mode 100644 dev/src/components/panels/tools/About.astro create mode 100644 dev/src/components/panels/tools/AppLog.astro create mode 100644 dev/src/components/panels/tools/AppSettings.astro create mode 100644 dev/src/components/panels/tools/ManageFiles.astro create mode 100644 dist/_astro/index.33CKCK6j.css delete mode 100644 dist/_astro/index.uwcihdcd.css create mode 100644 dist/js/init.js create mode 100644 dist/js/libs.js delete mode 100644 dist/js/main.js create mode 100644 dist/js/stores.js create mode 100644 dist/js/utils.js diff --git a/dev/public/js/init.js b/dev/public/js/init.js new file mode 100644 index 0000000..8539d5a --- /dev/null +++ b/dev/public/js/init.js @@ -0,0 +1,30 @@ +drag.init("app"); + +document.addEventListener("alpine:init", () => { + // create and init stores + Alpine.store("files", filesStore); + Alpine.store("lightbox", lightboxStore); + Alpine.store("ui", uiStore); + Alpine.store("userPrefs", userPrefsStore); + + const salutations = [ + "Hi!", + "Hiya!", + "Hello there!", + "Good day!", + "Hullo!", + "Buongiorno!", + "Guten Tag!", + "Bonjour!", + "Oh hey!", + ]; + Alpine.store("ui").logMsg(`MARL loaded. ${salutations[Math.floor(Math.random() * salutations.length)]} 😊`); + + resetStores(); +}); + +document.addEventListener("alpine-i18n:ready", function () { + AlpineI18n.create("en", appStrings); + AlpineI18n.fallbackLocale = "en"; + setLang(); +}); diff --git a/dev/public/js/libs.js b/dev/public/js/libs.js new file mode 100644 index 0000000..b8db65e --- /dev/null +++ b/dev/public/js/libs.js @@ -0,0 +1,36 @@ +const isFileProtocol = window.location.protocol === "file:"; + +const scripts = [ + { + src: "js/jszip.min.js", + integrity: "sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==", + crossorigin: "anonymous", + defer: false, + }, + // Note: Alpine plug-ins must be inserted BEFORE alpinejs + { + src: "js/alpinejs-i18n.min.js", + integrity: "sha256-o204NcFyHPFzboSC51fufMqFe2KJdQfSCl8AlvSZO/E=", + crossorigin: "anonymous", + defer: true, + }, + { + src: "js/alpinejs.min.js", + integrity: "sha512-FUaEyIgi9bspXaH6hUadCwBLxKwdH7CW24riiOqA5p8hTNR/RCLv9UpAILKwqs2AN5WtKB52CqbiePBei3qjKg==", + crossorigin: "anonymous", + defer: true, + }, +]; + +scripts.forEach(({ src, integrity, crossorigin, defer }) => { + const script = document.createElement("script"); + script.src = src; + if (!isFileProtocol) { + script.integrity = integrity; + script.crossOrigin = crossorigin; + } + if (defer) { + script.defer = true; + } + document.head.appendChild(script); +}); diff --git a/dev/public/js/main.js b/dev/public/js/main.js deleted file mode 100644 index 60e8600..0000000 --- a/dev/public/js/main.js +++ /dev/null @@ -1,1513 +0,0 @@ -// stores definitions - -const userPrefsStore = { - prefix: "marl_", - - save(pref, value) { - console.info("Saving user preference:", pref, value); - localStorage.setItem(this.prefix + pref, value); - }, - load(pref) { - const value = localStorage.getItem(this.prefix + pref); - if (value !== null) { - this.set(pref, value); - } - }, - set(pref, value) { - switch (pref) { - case "sortAsc": - value = +value === 1 ? true : false; - if (value !== Alpine.store("files").sortAsc) { - Alpine.store("files").sortAsc = value; - } - break; - case "pageSize": - value = +value; - if (typeof value == "number" && !isNaN(value) && value > 0 && value !== Alpine.store("files").pageSize) { - Alpine.store("files").pageSize = value; - } - break; - case "lang": - if (!value || !Alpine.store("ui").appLangs[value]) { - if (value) { - console.warn("Unrecognized language in user preferences:", value); - } - value = "en"; - this.save("lang", value); - } - Alpine.store("ui").lang = value; - break; - } - }, -}; - -const filesStore = { - resetState() { - this.sources = []; - this.toots = []; - this.toc = []; - this.duplicates = false; - - this.sortAsc = true; // -> userPrefs - this.pageSize = 10; // -> userPrefs - this.currentPage = 1; - - this.loading = false; - this.someFilesLoaded = false; - - this.languages = {}; - this.boostsAuthors = []; - - this.filters = {}; - this.filtersDefault = { - fullText: "", - hashtagText: "", - mentionText: "", - externalLink: "", - summary: "", - isEdited: false, - isDuplicate: false, - noStartingAt: false, - hasExternalLink: false, - hasHashtags: false, - hasMentions: false, - hasSummary: false, - isSensitive: false, - visibilityPublic: true, - visibilityUnlisted: true, - visibilityFollowers: true, - visibilityMentioned: true, - typeOriginal: true, - typeBoost: true, - attachmentAny: false, - attachmentImage: false, - attachmentVideo: false, - attachmentSound: false, - attachmentNoAltText: false, - attachmentWithAltText: false, - - // automatically generated (see loadJsonFile()): - // lang_en: true, - // lang_fr: true, - // lang_de: true, - // etc - // actor_0: true, - // actor_1: true, - // actor_2: true, - // etc - }; - this.filtersActive = false; - - this.tagsFilters = { - hashtags: "", - mentions: "", - boostsAuthors: "", - }; - - Alpine.store("userPrefs").load("sortAsc"); - Alpine.store("userPrefs").load("pageSize"); - }, - - setFilter() { - this.checkPagingValue(); - scrollTootsToTop(); - pagingUpdated(); - if (JSON.stringify(this.filters) === JSON.stringify(this.filtersDefault)) { - this.filtersActive = false; - } else { - this.filtersActive = true; - } - - const self = this; - setTimeout(() => { - self.checkPagingValue(); - }, 50); - }, - filterByTag(filter, value, id) { - if (value) { - if (value === this.filters[filter]) { - this.filters[filter] = ""; - } else { - this.filters[filter] = value; - } - } - - // "boosted users" group - // in this case let's also (un)check the 'boost type' filters - if (filter == "fullText") { - if (this.filters[filter] === "") { - this.filters.typeBoost = true; - this.filters.typeOriginal = true; - } else { - this.filters.typeBoost = true; - this.filters.typeOriginal = false; - } - } - - this.setFilter(); - - // keyboard focus may be lost when tags list changes - setTimeout(() => { - document.getElementById(id).focus(); - }, 100); - }, - resetFilters(userAction) { - this.filters = JSON.parse(JSON.stringify(this.filtersDefault)); - if (userAction) { - this.currentPage = 1; - this.filtersActive = false; - scrollTootsToTop(); - pagingUpdated(); - } - }, - - get filteredToots() { - const f = this.filters; - const fa = this.filtersActive; - return this.toots.filter((t) => { - if (!fa) { - return true; - } - - if (f.fullText) { - let show = false; - if (t._marl.textContent) { - const filterValue = f.fullText.toLowerCase(); - - if (filterValue && t._marl.textContent && t._marl.textContent.indexOf(filterValue) >= 0) { - show = true; - } - } - if (!show) { - return show; - } - } - - if (f.hashtagText) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - const filterValue = f.hashtagText.toLowerCase(); - if ( - !t.object.tag.some((t) => { - return t.type === "Hashtag" && t.name.toLowerCase().indexOf(filterValue) > -1; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.mentionText) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - const filterValue = f.mentionText.toLowerCase(); - if ( - !t.object.tag.some((t) => { - return t.type === "Mention" && t.name.toLowerCase().indexOf(filterValue) > -1; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.summary) { - if (t._marl.summary) { - const filterValue = f.summary.toLowerCase(); - if (t._marl.summary.indexOf(filterValue) === -1) { - return false; - } - } else { - return false; - } - } - - if (f.isEdited) { - if (!(typeof t.object === "object" && t.object !== null && t.object.updated)) { - return false; - } - } - - if (f.isDuplicate) { - if (!t._marl.duplicate) { - return false; - } - } - - if (f.noStartingAt) { - if (!t._marl.textContent || t._marl.textContent.indexOf("@") === 0) { - return false; - } - } - - if (f.hasExternalLink) { - if (!t._marl.externalLinks || !t._marl.externalLinks.length) { - return false; - } - } - - if (f.hasHashtags) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - if ( - !t.object.tag.some((t) => { - return t.type === "Hashtag"; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.hasMentions) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - if ( - !t.object.tag.some((t) => { - return t.type === "Mention"; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.hasSummary) { - if (typeof t.object === "object" && t.object !== null) { - if (!t.object.summary) { - return false; - } - } else { - return false; - } - } - - if (f.isSensitive) { - if (typeof t.object === "object" && t.object !== null) { - if (!t.object.sensitive) { - return false; - } - } else { - return false; - } - } - - if (f.externalLink) { - let show = false; - if (t._marl.externalLinks && t._marl.externalLinks.length) { - const filterValue = f.externalLink.toLowerCase(); - show = t._marl.externalLinks.some((link) => { - return link.href.indexOf(filterValue) > -1 || link.text.indexOf(filterValue) > -1; - }); - } - if (!show) { - return false; - } - } - - if (!f.visibilityPublic && t._marl.visibility[0] === "public") { - return false; - } - if (!f.visibilityUnlisted && t._marl.visibility[0] === "unlisted") { - return false; - } - if (!f.visibilityFollowers && t._marl.visibility[0] === "followers") { - return false; - } - if (!f.visibilityMentioned && t._marl.visibility[0] === "mentioned") { - return false; - } - - if (!f.typeOriginal && t.type === "Create") { - return false; - } - if (!f.typeBoost && t.type === "Announce") { - return false; - } - - if (f.attachmentAny) { - if (!t._marl.hasAttachments) { - return false; - } - } - if (f.attachmentImage) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return attachmentIsImage(att); - }) - ) { - return false; - } - } else { - return false; - } - } - if (f.attachmentVideo) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return attachmentIsVideo(att); - }) - ) { - return false; - } - } else { - return false; - } - } - if (f.attachmentSound) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return attachmentIsSound(att); - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.attachmentNoAltText) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return att.name === null; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.attachmentWithAltText) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return att.name; - }) - ) { - return false; - } - } else { - return false; - } - } - - for (const lang in this.languages) { - if (f.hasOwnProperty("lang_" + lang) && f["lang_" + lang] === false) { - if (t._marl.langs.includes(lang) || t._marl.langs.length === 0) { - return false; - } - } - } - - for (const source of this.sources) { - const id = source.id; - if (f.hasOwnProperty("actor_" + id) && f["actor_" + id] === false) { - if (t._marl.source === id) { - return false; - } - } - } - - return true; - }); - }, - - get listHashtags() { - return this.listTags("Hashtag"); - }, - get listMentions() { - return this.listTags("Mention"); - }, - listTags(type) { - let filterSource = ""; - switch (type) { - case "Mention": - filterSource = "mentions"; - break; - case "Hashtag": - filterSource = "hashtags"; - break; - } - let h = this.filteredToots.reduce((accu, toot) => { - if (tootHasTags(toot)) { - for (const key in toot.object.tag) { - const tag = toot.object.tag[key]; - if ( - tag.type && - tag.type === type && - tag.name && - tag.name.toLowerCase().indexOf(this.tagsFilters[filterSource].toLowerCase()) >= 0 - ) { - if ( - accu.some((item) => { - return item.name === tag.name; - }) - ) { - accu.map((item) => { - if (item.name === tag.name) { - item.nb++; - } - }); - } else { - accu.push({ - name: tag.name, - href: tag.href, - nb: 1, - }); - } - } - } - } - return accu; - }, []); - - h.sort((a, b) => { - if (a.nb === b.nb) { - return a.name.localeCompare(b.name); - } else { - return b.nb - a.nb; - } - }); - - return h; - }, - get listBoostsAuthors() { - let r = this.boostsAuthors.reduce((accu, item) => { - if (item.name.toLowerCase().indexOf(this.tagsFilters.boostsAuthors.toLowerCase()) >= 0) { - accu.push(item); - } - return accu; - }, []); - r.sort((a, b) => { - if (a.nb === b.nb) { - let aHasNoName = a.name.indexOf("? ") === 0; - let bHasNoName = b.name.indexOf("? ") === 0; - if (aHasNoName && bHasNoName) { - return a.name.localeCompare(b.name); - } else if (aHasNoName) { - return 1; - } else if (bHasNoName) { - return -1; - } else { - return a.name.localeCompare(b.name); - } - } else { - if (a.nb === b.nb) { - return a.name.localeCompare(b.name); - } else { - return b.nb - a.nb; - } - } - }); - return r; - }, - - get sortedLanguages() { - let langs = []; - for (const lang in this.languages) { - langs.push([lang, this.languages[lang]]); - } - langs.sort((a, b) => { - if (a[0] === "undefined") { - return 1; - } - if (b[0] === "undefined") { - return -1; - } - if (a[1] === b[1]) { - return a[0].localeCompare(b[0]); - } - return b[1] - a[1]; - }); - return langs; - }, - - get appReady() { - if (this.sources.length === 0) { - return false; - } - - let r = true; - for (let i = 0; i < this.sources.length; i++) { - const source = this.sources[i]; - if ( - !source.loaded.actor || - !source.loaded.avatar || - !source.loaded.header || - !source.loaded.outbox || - !source.loaded.likes || - !source.loaded.bookmarks - ) { - r = false; - } - } - return r; - }, - - get totalPages() { - return Math.ceil(this.filteredToots.length / this.pageSize); - }, - get pagedToots() { - if (this.filteredToots) { - return this.filteredToots.filter((_, index) => { - let start = (this.currentPage - 1) * this.pageSize; - let end = this.currentPage * this.pageSize; - if (index >= start && index < end) return true; - }); - } else { - return []; - } - }, - - sortToots() { - this.toots.sort((a, b) => { - if (this.sortAsc) { - return a.published.localeCompare(b.published); - } else { - return b.published.localeCompare(a.published); - } - }); - }, - toggleTootsOrder() { - this.sortAsc = !this.sortAsc; - this.sortToots(); - scrollTootsToTop(); - pagingUpdated(); - }, - - checkPagingValue() { - if (this.currentPage < 1) { - this.currentPage = 1; - } else if (this.currentPage > this.totalPages) { - this.currentPage = this.totalPages; - } - }, - nextPage(setFocusTo) { - if (this.currentPage * this.pageSize < this.filteredToots.length) { - this.currentPage++; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - } - }, - prevPage(setFocusTo) { - if (this.currentPage > 1) { - this.currentPage--; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - } - }, - firstPage(setFocusTo) { - this.currentPage = 1; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - }, - lastPage(setFocusTo) { - this.currentPage = this.totalPages; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - }, -}; - -const lightboxStore = { - resetState() { - this.show = false; - this.data = []; - this.source = 0; - this.index = 0; - this.origin = ""; - }, - - open(toot, index, origin) { - this.data = toot.object.attachment; - this.source = toot._marl.source; - this.show = true; - this.index = index; - this.origin = origin; - document.getElementById("main-section-inner").setAttribute("inert", true); - setTimeout(() => { - document.getElementById("lightbox").focus(); - }, 50); - }, - openProfileImg(name, origin, source) { - const data = { - object: { - attachment: [ - { - name: name, - url: name, - mediaType: Alpine.store("files").sources[source][name].type, - }, - ], - }, - _marl: { - source: source, - }, - }; - this.open(data, 0, origin); - }, - close() { - const origin = this.origin; - this.data = []; - this.index = 0; - this.show = false; - this.origin = ""; - document.getElementById("main-section-inner").removeAttribute("inert"); - document.getElementById(origin).focus(); - }, - showNext() { - this.index++; - if (this.index >= this.data.length) { - this.index = 0; - } - if (!attachmentIsImage(this.data[this.index])) { - this.showNext(); - } - }, - showPrev() { - this.index--; - if (this.index < 0) { - this.index = this.data.length - 1; - } - if (!attachmentIsImage(this.data[this.index])) { - this.showPrev(); - } - }, -}; - -const uiStore = { - resetState() { - this.pagingOptionsVisible = false; - this.openMenu = ""; - this.actorPanel = 0; - this.menuIsActive = false; - this.lang = "en"; - this.appLangs = appLangs ?? [["en", "English"]]; - - Alpine.store("userPrefs").load("lang"); - }, - - togglePagingOptions() { - this.pagingOptionsVisible = !this.pagingOptionsVisible; - - if (this.pagingOptionsVisible) { - setTimeout(() => { - document.getElementById("paging-options").focus(); - }, 100); - } - }, - get pagingOptionsClass() { - return this.pagingOptionsVisible ? "open" : ""; - }, - - openActorPanel(id) { - this.actorPanel = id; - }, - switchActorPanel(dir) { - let id = this.actorPanel; - if (dir === "up") { - id++; - if (id >= Alpine.store("files").sources.length) { - id = 0; - } - } else { - id--; - if (id < 0) { - id = Alpine.store("files").sources.length - 1; - } - } - this.actorPanel = id; - document.getElementById("actortab-" + id).focus(); - }, - - menuClose() { - const name = this.openMenu; - this.openMenu = ""; - this.setInert(); - - // bring focus back to where it was before the panel was opened - document.querySelector("#main-section-inner .mobile-menu .menu-" + name).focus(); - }, - menuOpen(name) { - this.openMenu = name; - this.resetPanels(); - this.setInert(); - - setTimeout(() => { - document.getElementById("panel-" + name).focus(); - }, 100); - }, - menuToggle(name) { - switch (name) { - case "actor": - case "filters": - case "tags": - if (this.openMenu === name) { - this.menuClose(); - } else { - this.menuOpen(name); - } - break; - } - }, - resetPanels() { - const name = this.openMenu; - document.querySelectorAll(`#panel-${name} details[open]`).forEach((e) => { - e.removeAttribute("open"); - }); - setTimeout(() => { - document.getElementById("panel-" + name).scrollTop = 0; - }, 250); - }, - checkMenuState() { - const menu = document.getElementById("mobile-menu"); - if (window.getComputedStyle(menu, null).display === "none") { - this.menuIsActive = false; - } else { - this.menuIsActive = true; - } - - this.setInert(); - }, - setInert() { - // set the 'inert' state on the side panels (actor, filters, tags) - // depending on whether they are hidden or not, AND whether the - // mobile menu is active - - document.querySelectorAll("#main-section-inner > *").forEach((e) => { - e.removeAttribute("inert"); - }); - - if (this.menuIsActive) { - if (this.openMenu) { - document - .querySelectorAll("#main-section-inner > *:not(.mobile-menu, .panel-backdrop, #panel-" + this.openMenu) - .forEach((e) => { - e.setAttribute("inert", true); - }); - } else { - document.querySelectorAll("#panel-actor, #panel-filters, #panel-tags").forEach((e) => { - e.setAttribute("inert", true); - }); - } - } - }, - - get appClasses() { - let classes = []; - if (this.openMenu) { - classes.push("menu-open menu-open-" + this.openMenu); - } else { - classes.push("menu-closed"); - } - return classes; - }, -}; - -// utils - -function resetStores() { - Alpine.store("files").resetState(); - Alpine.store("lightbox").resetState(); - Alpine.store("ui").resetState(); -} - -function unZip(files) { - const firstLoad = Alpine.store("files").sources.length === 0; - if (firstLoad) { - resetStores(); - } - Alpine.store("files").loading = true; - - for (let i = 0; i < files.length; i++) { - const file = files[i]; - - if ( - Alpine.store("files").sources.some((source) => { - return ( - source.fileInfos.name === file.name && - source.fileInfos.size === file.size && - source.fileInfos.lastModified === file.lastModified - ); - }) - ) { - console.warn("File already loaded:", file.name); - continue; - } - - JSZip.loadAsync(file).then( - (content) => { - const index = Alpine.store("files").sources.length; - const fileInfos = { - name: file.name, - size: file.size, - lastModified: file.lastModified, - }; - - Alpine.store("files").sources[index] = { - id: index, - fileInfos: { - name: file.name, - size: file.size, - lastModified: file.lastModified, - }, - nbToots: 0, - - actor: {}, - outbox: {}, - likes: [], - bookmarks: [], - avatar: {}, - header: {}, - - loaded: { - actor: false, - avatar: false, - header: false, - outbox: false, - likes: false, - bookmarks: false, - }, - }; - - Alpine.store("files").sources[index]._raw = content.files; - - loadJsonFile("actor", index, fileInfos); - loadJsonFile("outbox", index, fileInfos); - loadJsonFile("likes", index, fileInfos); - loadJsonFile("bookmarks", index, fileInfos); - }, - (error) => { - console.error(`Error loading ${file.name}:`, error.message); - } - ); - } -} - -function loadJsonFile(name, index, fileInfos) { - const content = Alpine.store("files").sources[index]._raw; - - if (content[name + ".json"] === undefined) { - if (name === "likes" || name === "bookmarks") { - // we can still run the app without those files - console.warn(`${fileInfos.name}: File ${name}.json not found in archive.`); - Alpine.store("files").sources[index].loaded[name] = true; - } else { - // this should NOT happen and will prevent the app from running - console.error(`${fileInfos.name}: File ${name}.json not found in archive.`); - } - return; - } - - content[name + ".json"].async("text").then(function (txt) { - if (name === "actor") { - Alpine.store("files").sources[index].actor = JSON.parse(txt); - loadActorImages(index); - Alpine.store("files").sources[index].loaded.actor = true; - } // actor.json - - if (name === "outbox") { - let data = JSON.parse(txt); - - let toots = data.orderedItems.reduce((accu, t) => { - let t2 = preprocessToots(t, index); - if (t2) { - accu.push(t2); - } - return accu; - }, []); - - Alpine.store("files").toots = Alpine.store("files").toots.concat(toots); - Alpine.store("files").sources[index].nbToots = toots.length; - delete data.orderedItems; - Alpine.store("files").sources[index].outbox = data; - Alpine.store("files").sources[index].loaded.outbox = true; - } // outbox.json - - if (name === "likes" || name === "bookmarks") { - const tmp = JSON.parse(txt); - Alpine.store("files").sources[index][name] = tmp.orderedItems; - Alpine.store("files").sources[index].loaded[name] = true; - } // likes.json || bookmarks.json - }); -} - -function buildTootsInfos() { - let langs = {}; - let boosts = []; - - if (Alpine.store("files").toots.length > 0) { - let infos = Alpine.store("files").toots.reduce( - (accu, toot) => { - for (let lang in toot._marl.langs) { - const l = toot._marl.langs[lang]; - if (!accu.langs[l]) { - accu.langs[l] = 1; - } else { - accu.langs[l]++; - } - } - - if (toot.type === "Announce") { - // since Mastodon doesn't allow (yet?) cross-origin requests to - // retrieve post data (for boosts), we try to at least extract the - // user names for all the boosts contained in the archive - - // [ISSUE] "object" value is a string most of the times, but - // sometimes it's a complex object similar to type "Create" - if (typeof toot.object === "object" && toot.object !== null) { - // let's ignore this case for now... - // [TODO], but not clear how it should be handled - } else if (toot.object) { - // if it's not an object and it has a value, then it's simply a - // url (string) pointing to the original (boosted) post. - // [ISSUE] URL format not always consistent... (esp. in the case - // of non-Mastodon instances) - e.g: - // https://craftopi.art/objects/[...] - // https://firefish.city/notes/[...] - // https://bsky.brid.gy/convert/ap/at://did:plc:[...]/app.bsky.feed.post/[...] - // -> the user name is not always present in URL - const url = toot.object.split("/"); - let name; - let user; - let domain; - if (url.length > 2) { - domain = url[2]; - - if (url[0] === "https:" && url[3] === "users" && url[5] === "statuses") { - // Mastodon URL format -> user name - name = url[4]; - user = `https://${url[2]}/users/${url[4]}/`; - } else { - // other URL format -> domain name - name = `? ${url[2]}`; - user = `https://${url[2]}/`; - } - - if (!accu.boosts[name]) { - accu.boosts[name] = { - nb: 1, - name: name, - url: user, - domain: domain, - }; - } else { - accu.boosts[name].nb++; - } - } - } - } - return accu; - }, - { langs: {}, boosts: {} } - ); - - langs = infos.langs; - - boosts = []; - for (var key in infos.boosts) { - boosts.push(infos.boosts[key]); - } - } - - Alpine.store("files").languages = langs; - Alpine.store("files").boostsAuthors = boosts; -} - -function buildDynamicFilters() { - for (const lang in Alpine.store("files").languages) { - Alpine.store("files").filtersDefault["lang_" + lang] = true; - } - - for (const source of Alpine.store("files").sources) { - Alpine.store("files").filtersDefault["actor_" + source.id] = true; - } - - Alpine.store("files").resetFilters(false); -} - -function preprocessToots(t, index) { - // build the '_marl' prop for each toot - let marl = { - langs: [], - source: index, - }; - - // check for duplicates (in case of multiple archive files) - if (Alpine.store("files").toc.includes(t.id)) { - const alts = Alpine.store("files").toots.filter((t2) => t2.id === t.id); - - let identical = false; - const flat1 = JSON.stringify(t); - - alts.forEach((alt) => { - let alt2 = JSON.parse(JSON.stringify(alt)); - delete alt2._marl; - const flat2 = JSON.stringify(alt2); - - if (flat1 === flat2) { - identical = true; - } else { - alt._marl.duplicate = true; - marl.duplicate = true; - Alpine.store("files").duplicates = true; - } - }); - if (identical) { - return false; - } - } else { - Alpine.store("files").toc.push(t.id); - } - - if (t.type === "Create") { - if (typeof t.object === "object" && t.object !== null && t.object.contentMap) { - let langs = []; - for (let lang in t.object.contentMap) { - langs.push(lang); - } - marl.langs = langs; - } else { - marl.langs = ["undefined"]; - } - } - - if (typeof t.object === "object" && t.object !== null) { - if (t.object.content) { - const content = t.object.content.toLowerCase(); - marl.textContent = stripHTML(content); - marl.externalLinks = extractExternalLinks(content); - } - if (t.object.summary) { - marl.summary = t.object.summary.toLowerCase(); - } - - if (t.object.attachment && t.object.attachment.length) { - marl.hasAttachments = true; - } - } else if (t.object) { - marl.textContent = t.object.toLowerCase(); - } - - marl.visibility = tootVisibility(t); - - const id = t.id.split("/"); - marl.id = id[id.length - 2]; - - t._marl = marl; - return t; -} - -function loadActorImages(index) { - const actor = Alpine.store("files").sources[index].actor; - const content = Alpine.store("files").sources[index]._raw; - - if (actor.icon && actor.icon.type === "Image" && actor.icon.url && content[actor.icon.url]) { - const image = actor.icon; - content[image.url].async("base64").then(function (content) { - Alpine.store("files").sources[index].avatar = { - type: image.mediaType, - content: content, - noImg: false, - }; - Alpine.store("files").sources[index].loaded.avatar = true; - }); - } else { - Alpine.store("files").sources[index].avatar = { noImg: true }; - Alpine.store("files").sources[index].loaded.avatar = true; - } - - if (actor.image && actor.image.type === "Image" && actor.image.url && content[actor.image.url]) { - const image = actor.image; - content[image.url].async("base64").then(function (content) { - Alpine.store("files").sources[index].header = { - type: image.mediaType, - content: content, - noImg: false, - }; - Alpine.store("files").sources[index].loaded.header = true; - }); - } else { - Alpine.store("files").sources[index].header = { noImg: true }; - Alpine.store("files").sources[index].loaded.header = true; - } -} - -function setHueForSources() { - const nbSources = Alpine.store("files").sources.length; - const hueStart = Math.round(Math.random() * 360); // MARL accent: 59.17 - const hueSpacing = Math.round(360 / nbSources); - - for (let i = 0; i < nbSources; i++) { - Alpine.store("files").sources[i].hue = hueStart + hueSpacing * i; - } -} - -function checkAppReady(ok) { - if (ok) { - buildTootsInfos(); - buildDynamicFilters(); - cleanUpRaw(); - setHueForSources(); - document.getElementById("main-section").focus(); - Alpine.store("ui").checkMenuState(); - Alpine.store("files").sortToots(); - Alpine.store("files").loading = false; - Alpine.store("files").someFilesLoaded = true; - } -} - -function cleanUpRaw() { - for (let i = 0; i < Alpine.store("files").sources.length; i++) { - const content = Alpine.store("files").sources[i]._raw; - if (content.cleanedUp) { - continue; - } - - const actor = Alpine.store("files").sources[i].actor; - if (actor.image && actor.image.url) { - delete content[actor.image.url]; - } - if (actor.icon && actor.icon.url) { - delete content[actor.icon.url]; - } - delete content["actor.json"]; - delete content["outbox.json"]; - delete content["likes.json"]; - delete content["bookmarks.json"]; - content.cleanedUp = true; - - Alpine.store("files").sources[i]._raw = content; - } -} - -function loadAttachedMedia(att, index) { - if (attachmentIsImage(att) || attachmentIsVideo(att) || attachmentIsSound(att)) { - const data = Alpine.store("files").sources[index]._raw; - let url = att.url; - // ?! some instances seem to add their own name in front of the path, - // resulting in an invalid path with relation to the archive - // structure (e.g. "/framapiaf/media_attachments/...", but in the - // archive there is only a folder "/media_attachments") - // => So we remove everything that comes before "media_attachments/", - // hoping it doesn't break something else... :/ - const prefix = url.indexOf("media_attachments/"); - if (prefix > 0) { - url = url.slice(prefix); - } - if (!data[url]) { - return; - } - data[url].async("base64").then((content) => { - Alpine.store("files").sources[index][att.url] = { - type: att.mediaType, - content: content, - }; - }); - } -} - -function pagingUpdated() { - document.querySelectorAll(`#toots details[open]`).forEach((e) => { - e.removeAttribute("open"); - }); -} - -function scrollTootsToTop(setFocusTo) { - setTimeout(() => { - document.getElementById("toots").scrollTop = 0; - if (setFocusTo) { - // for keyboard users: we transfer the focus to the corresponding button - // in the upper paging module; or, in the cases where said button is - // disabled, we set the focus on the list of posts. - document.getElementById(setFocusTo).focus(); - } - }, 50); -} - -function contentType(data) { - let r = ""; - switch (data) { - case "Create": - r = "Post"; - break; - case "Announce": - r = "Boost"; - break; - } - return r; -} - -function tootVisibility(data) { - if (data.to.includes("https://www.w3.org/ns/activitystreams#Public")) { - return ["public", AlpineI18n.t("filters.visibilityPublic")]; - } - if ( - data.to.some((x) => x.indexOf("/followers") > -1) && - !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && - data.cc.includes("https://www.w3.org/ns/activitystreams#Public") - ) { - return ["unlisted", AlpineI18n.t("filters.visibilityUnlisted")]; - } - if ( - data.to.some((x) => x.indexOf("/followers") > -1) && - !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && - !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") - ) { - return ["followers", AlpineI18n.t("filters.visibilityFollowers")]; - } - if ( - !data.to.some((x) => x.indexOf("/followers") > -1) && - !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && - !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") - ) { - return ["mentioned", AlpineI18n.t("filters.visibilityMentioned")]; - } -} - -function tootHasTags(toot) { - return typeof toot.object === "object" && toot.object !== null && toot.object.tag && toot.object.tag.length; -} - -function formatJson(data) { - let r = data; - if (r._marl) { - // not a part of the source data; let's hide it to avoid confusion - r = JSON.parse(JSON.stringify(data)); - delete r._marl; - } - return JSON.stringify(r, null, 4); -} - -function formatAuthor(author, plainText) { - if (plainText) { - return author.split("/").pop(); - } else { - return `${author.split("/").pop()}`; - } -} - -function formatDateTime(data) { - let date = new Date(data); - const dateOptions = { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - hour: "numeric", - minute: "2-digit", - second: "2-digit", - }; - return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); -} - -function formatDate(data) { - let date = new Date(data); - const dateOptions = { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - }; - return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); -} - -function formatNumber(nb) { - return nb.toLocaleString(); -} - -function formatLikesBookmarks(url) { - const u = url.split("/"); - u.splice(0, 2); - - // 0 [domain] - // 1 "users" - // 2 [username] - // 3 "statuses" - // 4 [post id] - - let text = `${u[0]}`; - if (u[1] === "users" && u[3] === "statuses") { - text += `${u[2]}${u[4]}`; - } else { - u.splice(0, 1); - text += `${u.join("/")}`; - } - return text; -} - -function stripHTML(str) { - let doc = new DOMParser().parseFromString(str, "text/html"); - return doc.body.textContent || ""; -} - -function extractExternalLinks(str) { - const doc = new DOMParser().parseFromString(str, "text/html"); - const nodes = doc.querySelectorAll("a[href]:not(.mention)"); - let links = []; - nodes.forEach((link) => { - links.push({ - href: link.href, - text: link.textContent, - }); - }); - return links; -} - -function attachmentIsImage(att) { - return att.mediaType === "image/jpeg" || att.mediaType === "image/png"; -} - -function attachmentIsVideo(att) { - return att.mediaType === "video/mp4"; -} - -function attachmentIsSound(att) { - return att.mediaType === "audio/mpeg"; -} - -function attachmentWrapperClass(att) { - let r = []; - if (attachmentIsImage(att)) { - r.push("att-img"); - } else if (attachmentIsSound(att)) { - r.push("att-sound"); - } else if (attachmentIsVideo(att)) { - r.push("att-video"); - } - - if (!att.name) { - r.push("no-alt-text"); - } - - return r; -} - -function isFilterActive(name) { - return Alpine.store("files").filters[name] !== Alpine.store("files").filtersDefault[name]; -} - -function startOver() { - const txt = AlpineI18n.t("menu.newFileConfirm"); - if (confirm(txt)) { - location.reload(); - } -} - -function setLang() { - AlpineI18n.locale = Alpine.store("ui").lang; - Alpine.store("userPrefs").save("lang", AlpineI18n.locale); -} - -// drag'n'drop over entire page - -const drag = { - el: null, - - init(el) { - this.dropArea = document.getElementById(el); - - ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { - this.dropArea.addEventListener(eventName, (e) => this.preventDragDefaults(e), false); - }); - ["dragenter", "dragover"].forEach((eventName) => { - this.dropArea.addEventListener(eventName, () => this.highlightDrag(), false); - }); - ["dragleave", "drop"].forEach((eventName) => { - this.dropArea.addEventListener(eventName, () => this.unhighlightDrag(), false); - }); - this.dropArea.addEventListener("drop", (e) => this.handleDrop(e), false); - }, - - preventDragDefaults(e) { - e.preventDefault(); - e.stopPropagation(); - }, - highlightDrag() { - this.dropArea.classList.add("highlight-drag"); - }, - unhighlightDrag() { - this.dropArea.classList.remove("highlight-drag"); - }, - handleDrop(e) { - const dt = e.dataTransfer; - const files = dt.files; - unZip(files); - }, -}; - -// initialization - -drag.init("app"); - -const isFileProtocol = window.location.protocol === "file:"; -const scripts = [ - { - src: "js/jszip.min.js", - integrity: "sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==", - crossorigin: "anonymous", - defer: false, - }, - // Note: Alpine plug-ins must be inserted BEFORE alpinejs - { - src: "js/alpinejs-i18n.min.js", - integrity: "sha256-o204NcFyHPFzboSC51fufMqFe2KJdQfSCl8AlvSZO/E=", - crossorigin: "anonymous", - defer: true, - }, - { - src: "js/alpinejs.min.js", - integrity: "sha512-FUaEyIgi9bspXaH6hUadCwBLxKwdH7CW24riiOqA5p8hTNR/RCLv9UpAILKwqs2AN5WtKB52CqbiePBei3qjKg==", - crossorigin: "anonymous", - defer: true, - }, -]; - -scripts.forEach(({ src, integrity, crossorigin, defer }) => { - const script = document.createElement("script"); - script.src = src; - if (!isFileProtocol) { - script.integrity = integrity; - script.crossOrigin = crossorigin; - } - if (defer) { - script.defer = true; - } - document.head.appendChild(script); -}); - -document.addEventListener("alpine:init", () => { - // create and init stores - Alpine.store("files", filesStore); - Alpine.store("lightbox", lightboxStore); - Alpine.store("ui", uiStore); - Alpine.store("userPrefs", userPrefsStore); - - resetStores(); - - // watch user preference values for changes - Alpine.effect(() => { - const pageSize = Alpine.store("files").pageSize; - const sortAsc = Alpine.store("files").sortAsc; - - Alpine.store("userPrefs").save("pageSize", pageSize); - Alpine.store("userPrefs").save("sortAsc", sortAsc ? 1 : 0); - }); -}); - -document.addEventListener("alpine-i18n:ready", function () { - AlpineI18n.create("en", appStrings); - AlpineI18n.fallbackLocale = "en"; - AlpineI18n.locale = Alpine.store("ui").lang; - console.info(`App language set to '${AlpineI18n.locale}' (${Alpine.store("ui").appLangs[AlpineI18n.locale]})`); -}); diff --git a/dev/public/js/stores.js b/dev/public/js/stores.js new file mode 100644 index 0000000..da227b1 --- /dev/null +++ b/dev/public/js/stores.js @@ -0,0 +1,888 @@ +// stores definitions + +const userPrefsStore = { + prefix: "marl_", + + save(pref, value) { + const msg = `Saving user preference (${pref}: ${value})`; + Alpine.store("ui").logMsg(msg, "info"); + localStorage.setItem(this.prefix + pref, value); + }, + load(pref) { + const value = localStorage.getItem(this.prefix + pref); + if (value !== null) { + this.set(pref, value); + } else if (pref === "lang") { + this.set(pref, value); + } + }, + set(pref, value) { + switch (pref) { + case "sortAsc": + value = +value === 1 ? true : false; + if (value !== Alpine.store("files").sortAsc) { + Alpine.store("files").sortAsc = value; + } + break; + + case "pageSize": + value = +value; + if (typeof value == "number" && !isNaN(value) && value > 0 && value !== Alpine.store("files").pageSize) { + Alpine.store("files").pageSize = value; + } + break; + + case "lang": + if (!value) { + value = detectLangFromBrowser(); + if (value) { + this.save("lang", value); + } + } + if (!value || !Alpine.store("ui").appLangs[value]) { + if (value) { + const msg = `Unrecognized language in user preferences: ${value}`; + console.warn(msg); + Alpine.store("ui").logMsg(msg, "warn"); + } + value = "en"; + this.save("lang", value); + } + Alpine.store("ui").lang = value; + break; + + case "theme": + if (!(value === "dark" || value === "light")) { + value = "light"; + this.save("theme", value); + } + Alpine.store("ui").theme = value; + setTheme(value); + break; + } + }, +}; + +const filesStore = { + resetState() { + this.sources = []; + this.toots = []; + this.toc = []; + this.duplicates = false; + + this.sortAsc = true; // -> userPrefs + this.pageSize = 10; // -> userPrefs + this.currentPage = 1; + + this.loading = false; + this.someFilesLoaded = false; + + this.languages = {}; + this.boostsAuthors = []; + + this.filters = {}; + this.filtersDefault = { + fullText: "", + hashtagText: "", + mentionText: "", + externalLink: "", + summary: "", + isEdited: false, + isDuplicate: false, + noStartingAt: false, + hasExternalLink: false, + hasHashtags: false, + hasMentions: false, + hasSummary: false, + isSensitive: false, + visibilityPublic: true, + visibilityUnlisted: true, + visibilityFollowers: true, + visibilityMentioned: true, + typeOriginal: true, + typeBoost: true, + attachmentAny: false, + attachmentImage: false, + attachmentVideo: false, + attachmentSound: false, + attachmentNoAltText: false, + attachmentWithAltText: false, + + // automatically generated (see loadJsonFile()): + // lang_en: true, + // lang_fr: true, + // lang_de: true, + // etc + // actor_0: true, + // actor_1: true, + // actor_2: true, + // etc + }; + this.filtersActive = false; + + this.tagsFilters = { + hashtags: "", + mentions: "", + boostsAuthors: "", + }; + + Alpine.store("userPrefs").load("sortAsc"); + Alpine.store("userPrefs").load("pageSize"); + }, + + setFilter() { + this.checkPagingValue(); + scrollTootsToTop(); + pagingUpdated(); + if (JSON.stringify(this.filters) === JSON.stringify(this.filtersDefault)) { + this.filtersActive = false; + } else { + this.filtersActive = true; + } + + const self = this; + setTimeout(() => { + self.checkPagingValue(); + }, 50); + }, + filterByTag(filter, value, id) { + if (value) { + if (value === this.filters[filter]) { + this.filters[filter] = ""; + } else { + this.filters[filter] = value; + } + } + + // "boosted users" group + // in this case let's also (un)check the 'boost type' filters + if (filter == "fullText") { + if (this.filters[filter] === "") { + this.filters.typeBoost = true; + this.filters.typeOriginal = true; + } else { + this.filters.typeBoost = true; + this.filters.typeOriginal = false; + } + } + + this.setFilter(); + + // keyboard focus may be lost when tags list changes + setTimeout(() => { + document.getElementById(id).focus(); + }, 100); + }, + resetFilters(userAction) { + this.filters = JSON.parse(JSON.stringify(this.filtersDefault)); + if (userAction) { + this.currentPage = 1; + this.filtersActive = false; + scrollTootsToTop(); + pagingUpdated(); + } + }, + + get filteredToots() { + const f = this.filters; + const fa = this.filtersActive; + return this.toots.filter((t) => { + if (!fa) { + return true; + } + + if (f.fullText) { + let show = false; + if (t._marl.textContent) { + const filterValue = f.fullText.toLowerCase(); + + if (filterValue && t._marl.textContent && t._marl.textContent.indexOf(filterValue) >= 0) { + show = true; + } + } + if (!show) { + return show; + } + } + + if (f.hashtagText) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + const filterValue = f.hashtagText.toLowerCase(); + if ( + !t.object.tag.some((t) => { + return t.type === "Hashtag" && t.name.toLowerCase().indexOf(filterValue) > -1; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.mentionText) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + const filterValue = f.mentionText.toLowerCase(); + if ( + !t.object.tag.some((t) => { + return t.type === "Mention" && t.name.toLowerCase().indexOf(filterValue) > -1; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.summary) { + if (t._marl.summary) { + const filterValue = f.summary.toLowerCase(); + if (t._marl.summary.indexOf(filterValue) === -1) { + return false; + } + } else { + return false; + } + } + + if (f.isEdited) { + if (!(typeof t.object === "object" && t.object !== null && t.object.updated)) { + return false; + } + } + + if (f.isDuplicate) { + if (!t._marl.duplicate) { + return false; + } + } + + if (f.noStartingAt) { + if (!t._marl.textContent || t._marl.textContent.indexOf("@") === 0) { + return false; + } + } + + if (f.hasExternalLink) { + if (!t._marl.externalLinks || !t._marl.externalLinks.length) { + return false; + } + } + + if (f.hasHashtags) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + if ( + !t.object.tag.some((t) => { + return t.type === "Hashtag"; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.hasMentions) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + if ( + !t.object.tag.some((t) => { + return t.type === "Mention"; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.hasSummary) { + if (typeof t.object === "object" && t.object !== null) { + if (!t.object.summary) { + return false; + } + } else { + return false; + } + } + + if (f.isSensitive) { + if (typeof t.object === "object" && t.object !== null) { + if (!t.object.sensitive) { + return false; + } + } else { + return false; + } + } + + if (f.externalLink) { + let show = false; + if (t._marl.externalLinks && t._marl.externalLinks.length) { + const filterValue = f.externalLink.toLowerCase(); + show = t._marl.externalLinks.some((link) => { + return link.href.indexOf(filterValue) > -1 || link.text.indexOf(filterValue) > -1; + }); + } + if (!show) { + return false; + } + } + + if (!f.visibilityPublic && t._marl.visibility[0] === "public") { + return false; + } + if (!f.visibilityUnlisted && t._marl.visibility[0] === "unlisted") { + return false; + } + if (!f.visibilityFollowers && t._marl.visibility[0] === "followers") { + return false; + } + if (!f.visibilityMentioned && t._marl.visibility[0] === "mentioned") { + return false; + } + + if (!f.typeOriginal && t.type === "Create") { + return false; + } + if (!f.typeBoost && t.type === "Announce") { + return false; + } + + if (f.attachmentAny) { + if (!t._marl.hasAttachments) { + return false; + } + } + if (f.attachmentImage) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return attachmentIsImage(att); + }) + ) { + return false; + } + } else { + return false; + } + } + if (f.attachmentVideo) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return attachmentIsVideo(att); + }) + ) { + return false; + } + } else { + return false; + } + } + if (f.attachmentSound) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return attachmentIsSound(att); + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.attachmentNoAltText) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return att.name === null; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.attachmentWithAltText) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return att.name; + }) + ) { + return false; + } + } else { + return false; + } + } + + for (const lang in this.languages) { + if (f.hasOwnProperty("lang_" + lang) && f["lang_" + lang] === false) { + if (t._marl.langs.includes(lang) || t._marl.langs.length === 0) { + return false; + } + } + } + + for (const source of this.sources) { + const id = source.id; + if (f.hasOwnProperty("actor_" + id) && f["actor_" + id] === false) { + if (t._marl.source === id) { + return false; + } + } + } + + return true; + }); + }, + + get listHashtags() { + return this.listTags("Hashtag"); + }, + get listMentions() { + return this.listTags("Mention"); + }, + listTags(type) { + let filterSource = ""; + switch (type) { + case "Mention": + filterSource = "mentions"; + break; + case "Hashtag": + filterSource = "hashtags"; + break; + } + let h = this.filteredToots.reduce((accu, toot) => { + if (tootHasTags(toot)) { + for (const key in toot.object.tag) { + const tag = toot.object.tag[key]; + if ( + tag.type && + tag.type === type && + tag.name && + tag.name.toLowerCase().indexOf(this.tagsFilters[filterSource].toLowerCase()) >= 0 + ) { + if ( + accu.some((item) => { + return item.name === tag.name; + }) + ) { + accu.map((item) => { + if (item.name === tag.name) { + item.nb++; + } + }); + } else { + accu.push({ + name: tag.name, + href: tag.href, + nb: 1, + }); + } + } + } + } + return accu; + }, []); + + h.sort((a, b) => { + if (a.nb === b.nb) { + return a.name.localeCompare(b.name); + } else { + return b.nb - a.nb; + } + }); + + return h; + }, + get listBoostsAuthors() { + let r = this.boostsAuthors.reduce((accu, item) => { + if (item.name.toLowerCase().indexOf(this.tagsFilters.boostsAuthors.toLowerCase()) >= 0) { + accu.push(item); + } + return accu; + }, []); + r.sort((a, b) => { + if (a.nb === b.nb) { + let aHasNoName = a.name.indexOf("? ") === 0; + let bHasNoName = b.name.indexOf("? ") === 0; + if (aHasNoName && bHasNoName) { + return a.name.localeCompare(b.name); + } else if (aHasNoName) { + return 1; + } else if (bHasNoName) { + return -1; + } else { + return a.name.localeCompare(b.name); + } + } else { + if (a.nb === b.nb) { + return a.name.localeCompare(b.name); + } else { + return b.nb - a.nb; + } + } + }); + return r; + }, + + get sortedLanguages() { + let langs = []; + for (const lang in this.languages) { + langs.push([lang, this.languages[lang]]); + } + langs.sort((a, b) => { + if (a[0] === "undefined") { + return 1; + } + if (b[0] === "undefined") { + return -1; + } + if (a[1] === b[1]) { + return a[0].localeCompare(b[0]); + } + return b[1] - a[1]; + }); + return langs; + }, + + get appReady() { + if (this.sources.length === 0) { + return false; + } + + let r = true; + for (let i = 0; i < this.sources.length; i++) { + const source = this.sources[i]; + if ( + !source.loaded.actor || + !source.loaded.avatar || + !source.loaded.header || + !source.loaded.outbox || + !source.loaded.likes || + !source.loaded.bookmarks + ) { + r = false; + } + } + return r; + }, + + get totalPages() { + return Math.ceil(this.filteredToots.length / this.pageSize); + }, + get pagedToots() { + if (this.filteredToots) { + return this.filteredToots.filter((_, index) => { + let start = (this.currentPage - 1) * this.pageSize; + let end = this.currentPage * this.pageSize; + if (index >= start && index < end) return true; + }); + } else { + return []; + } + }, + + sortToots() { + this.toots.sort((a, b) => { + if (this.sortAsc) { + return a.published.localeCompare(b.published); + } else { + return b.published.localeCompare(a.published); + } + }); + }, + toggleTootsOrder() { + this.sortAsc = !this.sortAsc; + Alpine.store("userPrefs").save("sortAsc", this.sortAsc ? 1 : 0); + this.sortToots(); + scrollTootsToTop(); + pagingUpdated(); + }, + + setPostsPerPage() { + this.checkPagingValue(); + Alpine.store("userPrefs").save("pageSize", this.pageSize); + }, + checkPagingValue() { + if (this.currentPage < 1) { + this.currentPage = 1; + } else if (this.currentPage > this.totalPages) { + this.currentPage = this.totalPages; + } + }, + nextPage(setFocusTo) { + if (this.currentPage * this.pageSize < this.filteredToots.length) { + this.currentPage++; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + } + }, + prevPage(setFocusTo) { + if (this.currentPage > 1) { + this.currentPage--; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + } + }, + firstPage(setFocusTo) { + this.currentPage = 1; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + }, + lastPage(setFocusTo) { + this.currentPage = this.totalPages; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + }, +}; + +const lightboxStore = { + resetState() { + this.show = false; + this.data = []; + this.source = 0; + this.index = 0; + this.origin = ""; + }, + + open(toot, index, origin) { + this.data = toot.object.attachment; + this.source = toot._marl.source; + this.show = true; + this.index = index; + this.origin = origin; + document.getElementById("main-section-inner").setAttribute("inert", true); + setTimeout(() => { + document.getElementById("lightbox").focus(); + }, 50); + }, + openProfileImg(name, origin, source) { + const data = { + object: { + attachment: [ + { + name: name, + url: name, + mediaType: Alpine.store("files").sources[source][name].type, + }, + ], + }, + _marl: { + source: source, + }, + }; + this.open(data, 0, origin); + }, + close() { + const origin = this.origin; + this.data = []; + this.index = 0; + this.show = false; + this.origin = ""; + document.getElementById("main-section-inner").removeAttribute("inert"); + document.getElementById(origin).focus(); + }, + showNext() { + this.index++; + if (this.index >= this.data.length) { + this.index = 0; + } + if (!attachmentIsImage(this.data[this.index])) { + this.showNext(); + } + }, + showPrev() { + this.index--; + if (this.index < 0) { + this.index = this.data.length - 1; + } + if (!attachmentIsImage(this.data[this.index])) { + this.showPrev(); + } + }, +}; + +const uiStore = { + log: [], + resetState() { + this.pagingOptionsVisible = false; + this.openMenu = ""; + this.actorPanel = 0; + this.menuIsActive = false; + this.lang = "en"; + this.appLangs = appLangs ?? { en: "English" }; + this.theme = "light"; + this.log = this.log ?? []; + + Alpine.store("userPrefs").load("lang"); + Alpine.store("userPrefs").load("theme"); + }, + + logMsg(msg, type) { + type = type ?? "info"; + const dateOptions = { + hour12: false, + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + const time = new Date().toLocaleTimeString(Alpine.store("ui").lang, dateOptions); + let m = { + msg: msg, + type: type, + time: time, + }; + this.log.unshift(m); + }, + + toggleTheme() { + this.theme = this.theme === "light" ? "dark" : "light"; + Alpine.store("userPrefs").save("theme", this.theme); + setTheme(this.theme); + }, + + togglePagingOptions() { + this.pagingOptionsVisible = !this.pagingOptionsVisible; + + if (this.pagingOptionsVisible) { + setTimeout(() => { + document.getElementById("paging-options").focus(); + }, 100); + } + }, + get pagingOptionsClass() { + return this.pagingOptionsVisible ? "open" : ""; + }, + + openActorPanel(id) { + this.actorPanel = id; + }, + switchActorPanel(dir) { + let id = this.actorPanel; + if (dir === "up") { + id++; + if (id >= Alpine.store("files").sources.length) { + id = 0; + } + } else { + id--; + if (id < 0) { + id = Alpine.store("files").sources.length - 1; + } + } + this.actorPanel = id; + document.getElementById("actortab-" + id).focus(); + }, + + menuClose() { + const name = this.openMenu; + this.openMenu = ""; + this.setInert(); + + // bring focus back to where it was before the panel was opened + document.querySelector("#main-section-inner .mobile-menu .menu-" + name).focus(); + }, + menuOpen(name) { + this.openMenu = name; + this.resetPanels(); + this.setInert(); + + setTimeout(() => { + document.getElementById("panel-" + name).focus(); + }, 100); + }, + menuToggle(name) { + switch (name) { + case "actor": + case "filters": + case "tags": + case "tools": + if (this.openMenu === name) { + this.menuClose(); + } else { + this.menuOpen(name); + } + break; + } + }, + resetPanels() { + const name = this.openMenu; + document.querySelectorAll(`#panel-${name} details[open]`).forEach((e) => { + e.removeAttribute("open"); + }); + setTimeout(() => { + document.getElementById("panel-" + name).scrollTop = 0; + }, 250); + }, + checkMenuState() { + const menu = document.getElementById("mobile-menu"); + if (window.getComputedStyle(menu, null).display === "none") { + this.menuIsActive = false; + } else { + this.menuIsActive = true; + } + + this.setInert(); + }, + + setInertMain() { + document + .querySelectorAll("#main-section-inner > *:not(.mobile-menu, .panel-backdrop, #panel-" + this.openMenu) + .forEach((e) => { + e.setAttribute("inert", true); + }); + }, + setInertPanels() { + document.querySelectorAll("#panel-actor, #panel-filters, #panel-tags, #panel-tools").forEach((e) => { + e.setAttribute("inert", true); + }); + }, + setInertTools() { + document.querySelectorAll("#panel-tools").forEach((e) => { + e.setAttribute("inert", true); + }); + }, + setInert() { + // set the 'inert' state on the side panels or the main part of the app + // depending on whether they are hidden or not, AND whether the mobile + // menu is active + + document.querySelectorAll("#main-section-inner > *").forEach((e) => { + e.removeAttribute("inert"); + }); + + if (this.menuIsActive) { + if (this.openMenu) { + this.setInertMain(); + } else { + this.setInertPanels(); + } + } else { + if (this.openMenu === "tools") { + this.setInertMain(); + } else { + this.setInertTools(); + } + } + }, + + get appClasses() { + let classes = []; + if (this.openMenu) { + classes.push("menu-open menu-open-" + this.openMenu); + } else { + classes.push("menu-closed"); + } + return classes; + }, +}; diff --git a/dev/public/js/strings.js b/dev/public/js/strings.js index b0a6f35..1a31ea5 100644 --- a/dev/public/js/strings.js +++ b/dev/public/js/strings.js @@ -16,7 +16,6 @@ const appStrings = { p3: `Start by opening your archive file with MARL.
You can drag and drop it anywhere on this page, or {labelStart}click here to select it{labelEnd}.`, - projectPage: `Project page (github)`, }, misc: { loading: "Loading", @@ -27,8 +26,7 @@ const appStrings = { filters: "Filters", filtersActive: "some filters are active", tags: "Tags", - newFile: "New File", - newFileConfirm: "Discard current data and load a new archive file?", + tools: "Tools", }, lightbox: { next: "Next image", @@ -50,8 +48,8 @@ const appStrings = { cache. Posts that are not in your instance cache any more are not included in your archive. This affects boosts, likes, and bookmarks.`, rawData: "Raw data {fileName}", - favorites: "Favorites", - favoritesEmpty: "no favorites", + likes: "Favorites", + likesEmpty: "no favorites", bookmarks: "Bookmarks", bookmarksEmpty: "no bookmarks", }, @@ -140,6 +138,21 @@ const appStrings = { mentionsFilter: "Filter mentions", boostsFilter: "Filter boosted users", }, + tools: { + panelTitle: "Tools", + appSettings: "App settings", + selectLanguage: "Select language", + useDarkTheme: "Use dark theme", + useLightTheme: "Use light theme", + loadedFiles: "Loaded files", + addAnother: "Add another archive", + addAnotherTip: + "Tip: You can open multiple archives at once.
You can also drag and drop your archive files anywhere on this window.", + startOver: "Start over", + startOverConfirm: "Discard current data and load a new archive file?", + appLog: "App log", + projectPage: `Project page (github)`, + }, }, fr: { @@ -154,7 +167,6 @@ const appStrings = { p3: `Commencez par ouvrir votre archive avec MARL.
Vous pouvez la glisser-déposer n'importe où sur cette page, ou {labelStart}cliquer ici pour la sélectionner{labelEnd}.`, - projectPage: `Page du project (github)`, }, misc: { loading: "Chargement", @@ -165,8 +177,7 @@ const appStrings = { filters: "Filtres", filtersActive: "certains filtres sont actifs", tags: "Tags", - newFile: "Nouveau fichier", - newFileConfirm: "Repartir de zéro et charger un nouveau fichier ?", + tools: "Outils", }, lightbox: { next: "Image suivante", @@ -188,8 +199,8 @@ const appStrings = { de ce cache. Les posts qui ne sont plus présents dans le cache de votre instance ne sont pas inclus dans votre archive. Cela concerne les partages, les favoris et les marque-pages.`, rawData: "Données brutes {fileName}", - favorites: "Favoris", - favoritesEmpty: "aucun favori", + likes: "Favoris", + likesEmpty: "aucun favori", bookmarks: "Marque-pages", bookmarksEmpty: "aucun marque-page", }, @@ -278,5 +289,20 @@ const appStrings = { mentionsFilter: "Filtrer les mentions", boostsFilter: "Filter utilisateurs partagés", }, + tools: { + panelTitle: "Outils", + appSettings: "Réglages de l'app", + selectLanguage: "Choisir la langue", + useDarkTheme: "Utiliser le thème sombre", + useLightTheme: "Utiliser le thème clair", + loadedFiles: "Fichiers chargés", + addAnother: "Ajouter une autre archive", + addAnotherTip: + "Astuce: Vous pouvez ouvrir plusieurs archives en même temps.
Vous pouvez aussi glisser-déposer vos fichiers d'archive n'importe où dans cette fenêtre.", + startOver: "Recommencer", + startOverConfirm: "Repartir de zéro et charger un nouveau fichier ?", + appLog: "Journal", + projectPage: `Page du project (github)`, + }, }, }; diff --git a/dev/public/js/utils.js b/dev/public/js/utils.js new file mode 100644 index 0000000..a8448cf --- /dev/null +++ b/dev/public/js/utils.js @@ -0,0 +1,678 @@ +function resetStores() { + Alpine.store("files").resetState(); + Alpine.store("lightbox").resetState(); + Alpine.store("ui").resetState(); +} + +function unZip(files) { + const firstLoad = Alpine.store("files").sources.length === 0; + if (firstLoad) { + resetStores(); + } + Alpine.store("files").loading = true; + + for (let i = 0; i < files.length; i++) { + const file = files[i]; + + if ( + Alpine.store("files").sources.some((source) => { + return ( + source.fileInfos.name === file.name && + source.fileInfos.size === file.size && + source.fileInfos.lastModified === file.lastModified + ); + }) + ) { + const msg = `File already loaded: ${file.name}`; + console.warn(msg); + Alpine.store("ui").logMsg(msg, "warn"); + continue; + } + + Alpine.store("ui").logMsg(`Loading file: ${file.name}`, "info"); + + JSZip.loadAsync(file).then( + (content) => { + const index = Alpine.store("files").sources.length; + const fileInfos = { + name: file.name, + size: file.size, + lastModified: file.lastModified, + }; + + Alpine.store("files").sources[index] = { + id: index, + fileInfos: fileInfos, + nbToots: 0, + + actor: {}, + outbox: {}, + likes: [], + bookmarks: [], + avatar: {}, + header: {}, + + loaded: { + actor: false, + avatar: false, + header: false, + outbox: false, + likes: false, + bookmarks: false, + }, + }; + + Alpine.store("files").sources[index]._raw = content.files; + + loadJsonFile("actor", index, fileInfos); + loadJsonFile("outbox", index, fileInfos); + loadJsonFile("likes", index, fileInfos); + loadJsonFile("bookmarks", index, fileInfos); + }, + (error) => { + const msg = `Error loading ${file.name}: ${error.message}`; + console.error(msg); + Alpine.store("ui").logMsg(msg, "error"); + } + ); + } +} + +function loadJsonFile(name, index, fileInfos) { + const content = Alpine.store("files").sources[index]._raw; + + if (content[name + ".json"] === undefined) { + if (name === "likes" || name === "bookmarks") { + // we can still run the app without those files + const msg = `${fileInfos.name}: File ${name}.json not found in archive.`; + console.warn(msg); + Alpine.store("ui").logMsg(msg, "warn"); + Alpine.store("files").sources[index].loaded[name] = true; + } else { + // this should NOT happen and will prevent the app from running + const msg = `Critical error - ${fileInfos.name}: File ${name}.json not found in archive.`; + console.error(msg); + Alpine.store("ui").logMsg(msg, "error"); + } + return; + } + + content[name + ".json"].async("text").then(function (txt) { + if (name === "actor") { + Alpine.store("files").sources[index].actor = JSON.parse(txt); + loadActorImages(index); + Alpine.store("files").sources[index].loaded.actor = true; + } // actor.json + + if (name === "outbox") { + let data = JSON.parse(txt); + + let toots = data.orderedItems.reduce((accu, t) => { + let t2 = preprocessToots(t, index); + if (t2) { + accu.push(t2); + } + return accu; + }, []); + + Alpine.store("files").toots = Alpine.store("files").toots.concat(toots); + Alpine.store("files").sources[index].nbToots = toots.length; + delete data.orderedItems; + Alpine.store("files").sources[index].outbox = data; + Alpine.store("files").sources[index].loaded.outbox = true; + } // outbox.json + + if (name === "likes" || name === "bookmarks") { + const tmp = JSON.parse(txt); + Alpine.store("files").sources[index][name] = tmp.orderedItems; + Alpine.store("files").sources[index].loaded[name] = true; + } // likes.json || bookmarks.json + }); +} + +function buildTootsInfos() { + let langs = {}; + let boosts = []; + + if (Alpine.store("files").toots.length > 0) { + let infos = Alpine.store("files").toots.reduce( + (accu, toot) => { + for (let lang in toot._marl.langs) { + const l = toot._marl.langs[lang]; + if (!accu.langs[l]) { + accu.langs[l] = 1; + } else { + accu.langs[l]++; + } + } + + if (toot.type === "Announce") { + // since Mastodon doesn't allow (yet?) cross-origin requests to + // retrieve post data (for boosts), we try to at least extract the + // user names for all the boosts contained in the archive + + // [ISSUE] "object" value is a string most of the times, but + // sometimes it's a complex object similar to type "Create" + if (typeof toot.object === "object" && toot.object !== null) { + // let's ignore this case for now... + // [TODO], but not clear how it should be handled + } else if (toot.object) { + // if it's not an object and it has a value, then it's simply a + // url (string) pointing to the original (boosted) post. + // [ISSUE] URL format not always consistent... (esp. in the case + // of non-Mastodon instances) - e.g: + // https://craftopi.art/objects/[...] + // https://firefish.city/notes/[...] + // https://bsky.brid.gy/convert/ap/at://did:plc:[...]/app.bsky.feed.post/[...] + // -> the user name is not always present in URL + const url = toot.object.split("/"); + let name; + let user; + let domain; + if (url.length > 2) { + domain = url[2]; + + if (url[0] === "https:" && url[3] === "users" && url[5] === "statuses") { + // Mastodon URL format -> user name + name = url[4]; + user = `https://${url[2]}/users/${url[4]}/`; + } else { + // other URL format -> domain name + name = `? ${url[2]}`; + user = `https://${url[2]}/`; + } + + if (!accu.boosts[name]) { + accu.boosts[name] = { + nb: 1, + name: name, + url: user, + domain: domain, + }; + } else { + accu.boosts[name].nb++; + } + } + } + } + return accu; + }, + { langs: {}, boosts: {} } + ); + + langs = infos.langs; + + boosts = []; + for (var key in infos.boosts) { + boosts.push(infos.boosts[key]); + } + } + + Alpine.store("files").languages = langs; + Alpine.store("files").boostsAuthors = boosts; +} + +function buildDynamicFilters() { + for (const lang in Alpine.store("files").languages) { + Alpine.store("files").filtersDefault["lang_" + lang] = true; + } + + for (const source of Alpine.store("files").sources) { + Alpine.store("files").filtersDefault["actor_" + source.id] = true; + } + + Alpine.store("files").resetFilters(false); +} + +function preprocessToots(t, index) { + // build the '_marl' prop for each toot + let marl = { + langs: [], + source: index, + }; + + // check for duplicates (in case of multiple archive files) + if (Alpine.store("files").toc.includes(t.id)) { + const alts = Alpine.store("files").toots.filter((t2) => t2.id === t.id); + + let identical = false; + const flat1 = JSON.stringify(t); + + alts.forEach((alt) => { + let alt2 = JSON.parse(JSON.stringify(alt)); + delete alt2._marl; + const flat2 = JSON.stringify(alt2); + + if (flat1 === flat2) { + identical = true; + } else { + alt._marl.duplicate = true; + marl.duplicate = true; + Alpine.store("files").duplicates = true; + } + }); + if (identical) { + return false; + } + } else { + Alpine.store("files").toc.push(t.id); + } + + if (t.type === "Create") { + if (typeof t.object === "object" && t.object !== null && t.object.contentMap) { + let langs = []; + for (let lang in t.object.contentMap) { + langs.push(lang); + } + marl.langs = langs; + } else { + marl.langs = ["undefined"]; + } + } + + if (typeof t.object === "object" && t.object !== null) { + if (t.object.content) { + const content = t.object.content.toLowerCase(); + marl.textContent = stripHTML(content); + marl.externalLinks = extractExternalLinks(content); + } + if (t.object.summary) { + marl.summary = t.object.summary.toLowerCase(); + } + + if (t.object.attachment && t.object.attachment.length) { + marl.hasAttachments = true; + } + } else if (t.object) { + marl.textContent = t.object.toLowerCase(); + } + + marl.visibility = tootVisibility(t); + + const id = t.id.split("/"); + marl.id = id[id.length - 2]; + + t._marl = marl; + return t; +} + +function loadActorImages(index) { + const actor = Alpine.store("files").sources[index].actor; + const content = Alpine.store("files").sources[index]._raw; + + if (actor.icon && actor.icon.type === "Image" && actor.icon.url && content[actor.icon.url]) { + const image = actor.icon; + content[image.url].async("base64").then(function (content) { + Alpine.store("files").sources[index].avatar = { + type: image.mediaType, + content: content, + noImg: false, + }; + Alpine.store("files").sources[index].loaded.avatar = true; + }); + } else { + Alpine.store("files").sources[index].avatar = { noImg: true }; + Alpine.store("files").sources[index].loaded.avatar = true; + } + + if (actor.image && actor.image.type === "Image" && actor.image.url && content[actor.image.url]) { + const image = actor.image; + content[image.url].async("base64").then(function (content) { + Alpine.store("files").sources[index].header = { + type: image.mediaType, + content: content, + noImg: false, + }; + Alpine.store("files").sources[index].loaded.header = true; + }); + } else { + Alpine.store("files").sources[index].header = { noImg: true }; + Alpine.store("files").sources[index].loaded.header = true; + } +} + +function setHueForSources() { + const nbSources = Alpine.store("files").sources.length; + const hueStart = Math.round(Math.random() * 360); // MARL accent: 59.17 + const hueSpacing = Math.round(360 / nbSources); + + for (let i = 0; i < nbSources; i++) { + Alpine.store("files").sources[i].hue = hueStart + hueSpacing * i; + } +} + +function checkAppReady(ok) { + if (ok) { + buildTootsInfos(); + buildDynamicFilters(); + cleanUpRaw(); + setHueForSources(); + document.getElementById("main-section").focus(); + Alpine.store("ui").checkMenuState(); + Alpine.store("files").sortToots(); + Alpine.store("files").loading = false; + Alpine.store("files").someFilesLoaded = true; + } +} + +function cleanUpRaw() { + for (let i = 0; i < Alpine.store("files").sources.length; i++) { + const content = Alpine.store("files").sources[i]._raw; + if (content.cleanedUp) { + continue; + } + + const actor = Alpine.store("files").sources[i].actor; + if (actor.image && actor.image.url) { + delete content[actor.image.url]; + } + if (actor.icon && actor.icon.url) { + delete content[actor.icon.url]; + } + delete content["actor.json"]; + delete content["outbox.json"]; + delete content["likes.json"]; + delete content["bookmarks.json"]; + content.cleanedUp = true; + + Alpine.store("files").sources[i]._raw = content; + } +} + +function loadAttachedMedia(att, index) { + if (attachmentIsImage(att) || attachmentIsVideo(att) || attachmentIsSound(att)) { + const data = Alpine.store("files").sources[index]._raw; + let url = att.url; + // ?! some instances seem to add their own name in front of the path, + // resulting in an invalid path with relation to the archive + // structure (e.g. "/framapiaf/media_attachments/...", but in the + // archive there is only a folder "/media_attachments") + // => So we remove everything that comes before "media_attachments/", + // hoping it doesn't break something else... :/ + const prefix = url.indexOf("media_attachments/"); + if (prefix > 0) { + url = url.slice(prefix); + } + if (!data[url]) { + return; + } + data[url].async("base64").then((content) => { + Alpine.store("files").sources[index][att.url] = { + type: att.mediaType, + content: content, + }; + }); + } +} + +function pagingUpdated() { + document.querySelectorAll(`#toots details[open]`).forEach((e) => { + e.removeAttribute("open"); + }); +} + +function scrollTootsToTop(setFocusTo) { + setTimeout(() => { + document.getElementById("toots").scrollTop = 0; + if (setFocusTo) { + // for keyboard users: we transfer the focus to the corresponding button + // in the upper paging module; or, in the cases where said button is + // disabled, we set the focus on the list of posts. + document.getElementById(setFocusTo).focus(); + } + }, 50); +} + +function contentType(data) { + let r = ""; + switch (data) { + case "Create": + r = "Post"; + break; + case "Announce": + r = "Boost"; + break; + } + return r; +} + +function tootVisibility(data) { + if (data.to.includes("https://www.w3.org/ns/activitystreams#Public")) { + return ["public", AlpineI18n.t("filters.visibilityPublic")]; + } + if ( + data.to.some((x) => x.indexOf("/followers") > -1) && + !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && + data.cc.includes("https://www.w3.org/ns/activitystreams#Public") + ) { + return ["unlisted", AlpineI18n.t("filters.visibilityUnlisted")]; + } + if ( + data.to.some((x) => x.indexOf("/followers") > -1) && + !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && + !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") + ) { + return ["followers", AlpineI18n.t("filters.visibilityFollowers")]; + } + if ( + !data.to.some((x) => x.indexOf("/followers") > -1) && + !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && + !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") + ) { + return ["mentioned", AlpineI18n.t("filters.visibilityMentioned")]; + } +} + +function tootHasTags(toot) { + return typeof toot.object === "object" && toot.object !== null && toot.object.tag && toot.object.tag.length; +} + +function formatJson(data) { + let r = data; + if (r._marl) { + // not a part of the source data; let's hide it to avoid confusion + r = JSON.parse(JSON.stringify(data)); + delete r._marl; + } + return JSON.stringify(r, null, 4); +} + +function formatAuthor(author, plainText) { + if (plainText) { + return author.split("/").pop(); + } else { + return `${author.split("/").pop()}`; + } +} + +function formatDateTime(data) { + let date = new Date(data); + const dateOptions = { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + }; + return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); +} + +function formatFileDateTime(data) { + let date = new Date(data); + const dateOptions = { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour12: false, + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); +} +function formatFileSize(size) { + var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)); + return +(size / Math.pow(1024, i)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][i]; // ### i18n +} + +function formatDate(data) { + let date = new Date(data); + const dateOptions = { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }; + return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); +} + +function formatNumber(nb) { + return nb.toLocaleString(); +} + +function formatLikesBookmarks(url) { + const u = url.split("/"); + u.splice(0, 2); + + // 0 [domain] + // 1 "users" + // 2 [username] + // 3 "statuses" + // 4 [post id] + + let text = `${u[0]}`; + if (u[1] === "users" && u[3] === "statuses") { + text += `${u[2]}${u[4]}`; + } else { + u.splice(0, 1); + text += `${u.join("/")}`; + } + return text; +} + +function stripHTML(str) { + let doc = new DOMParser().parseFromString(str, "text/html"); + return doc.body.textContent || ""; +} + +function extractExternalLinks(str) { + const doc = new DOMParser().parseFromString(str, "text/html"); + const nodes = doc.querySelectorAll("a[href]:not(.mention)"); + let links = []; + nodes.forEach((link) => { + links.push({ + href: link.href, + text: link.textContent, + }); + }); + return links; +} + +function attachmentIsImage(att) { + return att.mediaType === "image/jpeg" || att.mediaType === "image/png"; +} + +function attachmentIsVideo(att) { + return att.mediaType === "video/mp4"; +} + +function attachmentIsSound(att) { + return att.mediaType === "audio/mpeg"; +} + +function attachmentWrapperClass(att) { + let r = []; + if (attachmentIsImage(att)) { + r.push("att-img"); + } else if (attachmentIsSound(att)) { + r.push("att-sound"); + } else if (attachmentIsVideo(att)) { + r.push("att-video"); + } + + if (!att.name) { + r.push("no-alt-text"); + } + + return r; +} + +function isFilterActive(name) { + return Alpine.store("files").filters[name] !== Alpine.store("files").filtersDefault[name]; +} + +function startOver() { + const txt = AlpineI18n.t("tools.startOverConfirm"); + if (confirm(txt)) { + location.reload(); + } +} + +function detectLangFromBrowser() { + const langs = navigator.languages; + if (langs && langs.length) { + for (let i = 0; i < langs.length; i++) { + let lang = langs[i].split("-")[0]; + if (Alpine.store("ui").appLangs[lang]) { + const msg = `Setting language based on browser preference: '${lang}' (${ + Alpine.store("ui").appLangs[lang] + })`; + Alpine.store("ui").logMsg(msg, "info"); + return lang; + } + } + } + return false; +} + +function setLang() { + const lang = Alpine.store("ui").lang; + AlpineI18n.locale = lang; + Alpine.store("userPrefs").save("lang", lang); + + const msg = `App language set to '${lang}' (${Alpine.store("ui").appLangs[lang]})`; + Alpine.store("ui").logMsg(msg); +} + +function setTheme(theme) { + document.getElementsByTagName("html")[0].setAttribute("class", theme); +} + +// drag'n'drop over entire page + +const drag = { + el: null, + + init(el) { + this.dropArea = document.getElementById(el); + + ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { + this.dropArea.addEventListener(eventName, (e) => this.preventDragDefaults(e), false); + }); + ["dragenter", "dragover"].forEach((eventName) => { + this.dropArea.addEventListener(eventName, () => this.highlightDrag(), false); + }); + ["dragleave", "drop"].forEach((eventName) => { + this.dropArea.addEventListener(eventName, () => this.unhighlightDrag(), false); + }); + this.dropArea.addEventListener("drop", (e) => this.handleDrop(e), false); + }, + + preventDragDefaults(e) { + e.preventDefault(); + e.stopPropagation(); + }, + highlightDrag() { + this.dropArea.classList.add("highlight-drag"); + }, + unhighlightDrag() { + this.dropArea.classList.remove("highlight-drag"); + }, + handleDrop(e) { + const dt = e.dataTransfer; + const files = dt.files; + unZip(files); + }, +}; diff --git a/dev/src/components/MobileMenu.astro b/dev/src/components/MobileMenu.astro index 5b1efeb..7ee919d 100644 --- a/dev/src/components/MobileMenu.astro +++ b/dev/src/components/MobileMenu.astro @@ -29,9 +29,9 @@
  • -
  • diff --git a/dev/src/components/PagingBottom.astro b/dev/src/components/PagingBottom.astro index 54afd2b..9b47c3a 100644 --- a/dev/src/components/PagingBottom.astro +++ b/dev/src/components/PagingBottom.astro @@ -6,8 +6,8 @@ > - +
    @@ -24,8 +24,8 @@ @click="$store.files.nextPage('paging-btn-next')" :disabled="$store.files.currentPage >= $store.files.totalPages" > - - @@ -33,8 +33,8 @@ @click="$store.files.lastPage('toots')" :disabled="$store.files.currentPage >= $store.files.totalPages" > - - diff --git a/dev/src/components/PagingOptions.astro b/dev/src/components/PagingOptions.astro index 7e89410..9f02888 100644 --- a/dev/src/components/PagingOptions.astro +++ b/dev/src/components/PagingOptions.astro @@ -8,11 +8,10 @@ min="1" max="$store.files.totalPages" x-model="$store.files.currentPage" - @keyup="$store.files.checkPagingValue()" @change="$store.files.checkPagingValue()" onclick="this.select()" - /> - / + /> + / ( + /> )
    diff --git a/dev/src/components/PagingTop.astro b/dev/src/components/PagingTop.astro index 0491402..e480e6a 100644 --- a/dev/src/components/PagingTop.astro +++ b/dev/src/components/PagingTop.astro @@ -11,8 +11,8 @@ import PagingOptions from "./PagingOptions.astro"; > - +
    @@ -30,8 +30,8 @@ import PagingOptions from "./PagingOptions.astro"; @@ -43,8 +43,8 @@ import PagingOptions from "./PagingOptions.astro"; @click="$store.files.nextPage()" :disabled="$store.files.currentPage >= $store.files.totalPages" > - - @@ -53,8 +53,8 @@ import PagingOptions from "./PagingOptions.astro"; @click="$store.files.lastPage()" :disabled="$store.files.currentPage >= $store.files.totalPages" > - - diff --git a/dev/src/components/SvgSprites.astro b/dev/src/components/SvgSprites.astro index 7269049..3cf21ec 100644 --- a/dev/src/components/SvgSprites.astro +++ b/dev/src/components/SvgSprites.astro @@ -89,14 +89,39 @@ d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" /> - + - + + + + + + + + + + + + + + + + diff --git a/dev/src/components/panels/HeaderPanel.astro b/dev/src/components/panels/HeaderPanel.astro index 5c90f82..8206837 100644 --- a/dev/src/components/panels/HeaderPanel.astro +++ b/dev/src/components/panels/HeaderPanel.astro @@ -5,24 +5,24 @@ import PagingTop from '../PagingTop.astro';

    - + - - + -

    diff --git a/dev/src/components/panels/ToolsPanel.astro b/dev/src/components/panels/ToolsPanel.astro index ec5bfd6..5158016 100644 --- a/dev/src/components/panels/ToolsPanel.astro +++ b/dev/src/components/panels/ToolsPanel.astro @@ -1,59 +1,29 @@ --- import CloseBtn from './CloseBtn.astro' +import AppSettings from './tools/AppSettings.astro' +import ManageFiles from './tools/ManageFiles.astro' +import AppLog from './tools/AppLog.astro' +import About from './tools/About.astro' --- diff --git a/dev/src/components/panels/actor/LikesBookmarks.astro b/dev/src/components/panels/actor/LikesBookmarks.astro index 27df6ed..afa1d87 100644 --- a/dev/src/components/panels/actor/LikesBookmarks.astro +++ b/dev/src/components/panels/actor/LikesBookmarks.astro @@ -7,7 +7,7 @@ const { type } = Astro.props;

    diff --git a/dev/src/components/panels/tools/About.astro b/dev/src/components/panels/tools/About.astro new file mode 100644 index 0000000..b821aab --- /dev/null +++ b/dev/src/components/panels/tools/About.astro @@ -0,0 +1,7 @@ +
    +

    + MARL v. 2.1.0 — + + +

    +
    diff --git a/dev/src/components/panels/tools/AppLog.astro b/dev/src/components/panels/tools/AppLog.astro new file mode 100644 index 0000000..0536b0a --- /dev/null +++ b/dev/src/components/panels/tools/AppLog.astro @@ -0,0 +1,13 @@ +
    +

    +
      + +
    +
    diff --git a/dev/src/components/panels/tools/AppSettings.astro b/dev/src/components/panels/tools/AppSettings.astro new file mode 100644 index 0000000..742e34f --- /dev/null +++ b/dev/src/components/panels/tools/AppSettings.astro @@ -0,0 +1,39 @@ +
    +

    + +
    + + +
    + +
    + +
    +
    + diff --git a/dev/src/components/panels/tools/ManageFiles.astro b/dev/src/components/panels/tools/ManageFiles.astro new file mode 100644 index 0000000..b41d8d1 --- /dev/null +++ b/dev/src/components/panels/tools/ManageFiles.astro @@ -0,0 +1,44 @@ +
    +
    +

    +
      + +
    +
    + +
    + + + + +
    +
    +
    diff --git a/dev/src/components/screens/WelcomeScreen.astro b/dev/src/components/screens/WelcomeScreen.astro index f7f6549..1ad3b39 100644 --- a/dev/src/components/screens/WelcomeScreen.astro +++ b/dev/src/components/screens/WelcomeScreen.astro @@ -1,5 +1,6 @@ --- -import ToolsPanel from '../panels/ToolsPanel.astro'; +import AppSettings from '../panels/tools/AppSettings.astro'; +import About from '../panels/tools/About.astro'; ---
    @@ -19,6 +20,8 @@ import ToolsPanel from '../panels/ToolsPanel.astro'; /> - - +
    + + +
    diff --git a/dev/src/css/_actor.scss b/dev/src/css/_actor.scss index 08cf008..06f07ff 100644 --- a/dev/src/css/_actor.scss +++ b/dev/src/css/_actor.scss @@ -1,3 +1,5 @@ +@use "mixins"; + .actor { position: relative; &::before { @@ -144,13 +146,9 @@ color: var(--actor-fg0); line-height: 1.3; background: var(--actor-bg4); - background: radial-gradient( - circle at 200% 150%, - var(--actor-bg3), - var(--actor-bg4) - ); + background: radial-gradient(circle at 200% 150%, var(--actor-bg3), var(--actor-bg4)); border-radius: 0.5rem; - box-shadow: 0 0.3rem 0.4rem -0.2rem rgba(0, 0, 0, 0.3); + @include mixins.box-shadow-post(); } .actor-infos { diff --git a/dev/src/css/_colors-mixins.scss b/dev/src/css/_colors-mixins.scss index d6ab449..8eec9b6 100644 --- a/dev/src/css/_colors-mixins.scss +++ b/dev/src/css/_colors-mixins.scss @@ -7,6 +7,8 @@ --bg2: #{colors.$dark-bg2}; --bg3: #{colors.$dark-bg3}; --bg4: #{colors.$dark-bg4}; + --bg5: #{colors.$dark-bg5}; + --bg6: #{colors.$dark-bg6}; --fg0: #{colors.$dark-fg0}; --fg1: #{colors.$dark-fg1}; @@ -84,6 +86,10 @@ --actor-accent-ok: #{colors.$dark-actor-accent-ok}; --actor-accent2-ok: #{colors.$dark-actor-accent2-ok}; } + .loaded-files-list li { + --actor-accent-ok: #{colors.$dark-actor-accent-ok}; + --actor-accent2-ok: #{colors.$dark-actor-accent2-ok}; + } } @else { @@ -92,6 +98,8 @@ --bg2: #{colors.$bg2}; --bg3: #{colors.$bg3}; --bg4: #{colors.$bg4}; + --bg5: #{colors.$bg5}; + --bg6: #{colors.$bg6}; --fg0: #{colors.$fg0}; --fg1: #{colors.$fg1}; @@ -168,5 +176,9 @@ --actor-accent-ok: #{colors.$actor-accent-ok}; --actor-accent2-ok: #{colors.$actor-accent2-ok}; } + .loaded-files-list li { + --actor-accent-ok: #{colors.$actor-accent-ok}; + --actor-accent2-ok: #{colors.$actor-accent2-ok}; + } } } diff --git a/dev/src/css/_colors.scss b/dev/src/css/_colors.scss index bb146a7..0f190e8 100644 --- a/dev/src/css/_colors.scss +++ b/dev/src/css/_colors.scss @@ -6,10 +6,12 @@ $hue: 30; // === 59.17 in oklch /*********************************/ $bg0: #fff; // post content -$bg1: hsl($hue, 10%, 95%); // post +$bg1: hsl($hue, 12%, 95%); // post $bg2: hsl($hue, 12%, 90%); // post attachment/meta/raw $bg3: hsl($hue, 15%, 87%); // posts header $bg4: hsl($hue, 17.5%, 84%); // filters/tags panels +$bg5: hsl($hue, 25%, 95%); // settings +$bg6: #fff; // settings > ul $fg0: #000; // post content $fg1: hsl($hue, 10%, 20%); // body text; some SVG icons (panel close) @@ -22,6 +24,8 @@ $dark-bg1: hsl($hue, 8%, 13%); $dark-bg2: hsl($hue, 10%, 10%); $dark-bg3: hsl($hue, 12%, 8%); $dark-bg4: hsl($hue, 12%, 8%); +$dark-bg5: $dark-bg2; +$dark-bg6: rgba(255, 255, 255, 0.07); $dark-fg0: hsl($hue, 5%, 95%); $dark-fg1: hsl($hue, 10%, 80%); $dark-fg2: hsl($hue, 15%, 50%); diff --git a/dev/src/css/_global.scss b/dev/src/css/_global.scss index 11f5b6d..51307d2 100644 --- a/dev/src/css/_global.scss +++ b/dev/src/css/_global.scss @@ -1,5 +1,5 @@ -@use 'mixins'; -@use 'colors-mixins'; +@use "mixins"; +@use "colors-mixins"; /* mini-reset */ *, @@ -36,7 +36,7 @@ html { @media (prefers-color-scheme: dark) { color-scheme: dark; - @include colors-mixins.theme-vars('dark'); + @include colors-mixins.theme-vars("dark"); } } @@ -47,7 +47,7 @@ html.light { html.dark { color-scheme: dark; - @include colors-mixins.theme-vars('dark'); + @include colors-mixins.theme-vars("dark"); } /* global rules */ @@ -81,21 +81,33 @@ a, color: var(--accent); } -button { - position: relative; - top: 0; +input, +button, +label.btn, +select { padding: 0.3em 0.5em; - line-height: 24px; font-family: inherit; - vertical-align: middle; - background: var(--bg-button); border: none; - border-radius: 5px; - box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.35); - @media (forced-colors: active) { border: 1px solid ButtonText; } +} + +button, +label.btn, +select { + border-radius: 5px; +} + +button, +label.btn { + position: relative; + top: 0; + line-height: 24px; + vertical-align: middle; + background: var(--bg-button); + border-radius: 5px; + box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.35); &:not(:has(svg)) { padding: 0.7em 1em; @@ -163,16 +175,11 @@ button { } } -input { - padding: 0.3em 0.5em; +input, +select { color: inherit; - border: none; background-color: var(--bg-input); - @media (forced-colors: active) { - border: 1px solid ButtonText; - } - &:hover { background-color: var(--bg-input-hover); } @@ -182,14 +189,14 @@ input { } } -input[type='number'] { +input[type="number"] { width: 6ch; text-align: center; -moz-appearance: textfield; appearance: textfield; } -input[type='number']::-webkit-inner-spin-button, -input[type='number']::-webkit-outer-spin-button { +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } @@ -243,19 +250,28 @@ details { // focusable elements *:focus-visible, .tags-group button:focus-visible div, -.toots-filter:has([type='checkbox']:focus-visible) { +.toots-filter:has([type="checkbox"]:focus-visible) { text-decoration: none; outline: 2px solid var(--accent-light); outline-offset: 2px; border-radius: 2px; position: relative; z-index: 1; + + &.mobile-menu-panel { + outline-offset: -4px; + border-radius: 6px; + } } .visually-hidden:not(:focus):not(:active) { @include mixins.visually-hidden(); } +[inert] > * { + opacity: 0.5; +} + .nojs { margin: 5dvh 10dvw; padding: 2em; diff --git a/dev/src/css/_layout.scss b/dev/src/css/_layout.scss index 8911b35..3d31d8f 100644 --- a/dev/src/css/_layout.scss +++ b/dev/src/css/_layout.scss @@ -11,11 +11,12 @@ html { z-index: 1; height: 100dvh; max-width: 100dvw; + overflow: hidden; display: grid; grid-template-areas: - "tools actor filters header tags" - "tools actor filters toots tags"; - grid-template-columns: 150px min(25%, 600px) min(20%, 400px) 1fr min(20%, 400px); + "actor filters header tags" + "actor filters toots tags"; + grid-template-columns: min(25%, 600px) min(20%, 400px) 1fr min(20%, 400px); grid-template-rows: min-content 1fr; } @@ -34,9 +35,6 @@ html { .toots-tags { grid-area: tags; } -.panel-tools { - grid-area: tools; -} .mobile-menu { display: none; } @@ -57,7 +55,7 @@ html { .toggle-order { margin-bottom: 5px; } - .load-new { + .open-tools { float: right; margin-left: 5px; } diff --git a/dev/src/css/_menu.scss b/dev/src/css/_menu.scss index 0a64301..ba6abc5 100644 --- a/dev/src/css/_menu.scss +++ b/dev/src/css/_menu.scss @@ -116,6 +116,36 @@ $panel-width: 400px; bottom: 0; z-index: 3; background-color: var(--menu-backdrop); + + .main-page.menu-open & { + display: block; + } +} + +.panel-close { + position: absolute; + right: 6px; + top: 6px; + z-index: 1; + color: var(--panel-close); + background: transparent; + box-shadow: none; + .btn-icon { + fill: currentColor; + } + &:hover, + &:focus, + &:focus-visible { + color: var(--panel-close-hover); + } + + @media (forced-colors: active) { + color: buttonText; + } + + .actor & { + color: #fff; + } } @media screen and (width < 1200px) { @@ -124,7 +154,7 @@ $panel-width: 400px; .toots-header { z-index: 2; box-shadow: 0 -10px 10px 10px rgba(0, 0, 0, 0.5); - .load-new { + .open-tools { display: none; } } @@ -147,6 +177,7 @@ $panel-width: 400px; .mobile-menu-panel { position: fixed; left: 0 - ($menu-width + $panel-width); + right: auto; top: 0; bottom: 0; z-index: 3; @@ -163,31 +194,6 @@ $panel-width: 400px; scroll-behavior: smooth; } } - .panel-close { - position: absolute; - right: 6px; - top: 6px; - z-index: 1; - color: var(--panel-close); - background: transparent; - box-shadow: none; - .btn-icon { - fill: currentColor; - } - &:hover, - &:focus, - &:focus-visible { - color: var(--panel-close-hover); - } - - @media (forced-colors: active) { - color: buttonText; - } - - .actor & { - color: #fff; - } - } .mobile-menu { display: block; @@ -196,13 +202,10 @@ $panel-width: 400px; } .main-page.menu-open { - .panel-backdrop { - display: block; - } - &.menu-open-actor .actor, &.menu-open-filters .toots-filters, - &.menu-open-tags .toots-tags { + &.menu-open-tags .toots-tags, + &.menu-open-tools .tools { left: $menu-width; box-shadow: -1rem 0 1rem 1rem rgba(0, 0, 0, 0.5); @media (prefers-reduced-motion: no-preference) { @@ -212,7 +215,8 @@ $panel-width: 400px; &.menu-open-actor .menu-actor, &.menu-open-filters .menu-filters, - &.menu-open-tags .menu-tags { + &.menu-open-tags .menu-tags, + &.menu-open-tools .menu-tools { color: var(--menu-fg-active); background-color: rgba(255, 255, 255, 0.2); } @@ -258,7 +262,8 @@ $panel-width: 400px; .main-page.menu-open { &.menu-open-actor .actor, &.menu-open-filters .toots-filters, - &.menu-open-tags .toots-tags { + &.menu-open-tags .toots-tags, + &.menu-open-tools .tools { left: 0; } } diff --git a/dev/src/css/_mixins.scss b/dev/src/css/_mixins.scss index cffbc19..dbcc49f 100644 --- a/dev/src/css/_mixins.scss +++ b/dev/src/css/_mixins.scss @@ -9,5 +9,8 @@ } @mixin box-shadow-inner() { - box-shadow: 0 0.1rem 0.2rem -0.2rem rgba(0, 0, 0, 0.5) inset; + box-shadow: 0 0.15rem 0.3rem -0.2rem rgba(0, 0, 0, 0.35) inset; +} +@mixin box-shadow-post() { + box-shadow: 0 0.3rem 0.4rem -0.2rem rgba(0, 0, 0, 0.3); } diff --git a/dev/src/css/_tags.scss b/dev/src/css/_tags.scss index 2dab937..12c5b10 100644 --- a/dev/src/css/_tags.scss +++ b/dev/src/css/_tags.scss @@ -18,7 +18,7 @@ $display-wide: 340px; height: 100%; overflow: hidden; display: grid; - padding: 1rem 1rem 0; + padding: 0.5rem 1rem; grid-template-rows: min-content 1fr; ul { @@ -92,15 +92,14 @@ $display-wide: 340px; } .tags-group-header { - white-space: nowrap; - overflow: hidden; - @container (width >= #{$display-wide}) { display: flex; justify-content: space-between; gap: 0.25rem 1rem; } h3 { + white-space: nowrap; + overflow: hidden; .count { color: var(--accent-dark); font-weight: normal; @@ -121,7 +120,7 @@ $display-wide: 340px; .tags-group-scroll { overflow: auto; - padding-top: 0.5rem; + margin-top: 0.5rem; padding-bottom: 2rem; } diff --git a/dev/src/css/_tools.scss b/dev/src/css/_tools.scss index 1ab7ede..30c0f12 100644 --- a/dev/src/css/_tools.scss +++ b/dev/src/css/_tools.scss @@ -1,3 +1,199 @@ -.panel-tools { - padding: 1rem; +@use "mixins"; + +.tools-section { + h3 { + margin: 0 0 1rem; + } + ul { + list-style: none; + font-size: 0.85em; + margin: 1rem 0; + padding: 0.5rem 1rem; + background-color: var(--bg6); + border-radius: 0.5rem; + word-break: break-all; + } + li { + list-style: none; + margin: 0.5rem 0; + } +} + +.panel-tools { + position: absolute; + left: auto; + right: calc(0px - min(25%, 600px)); + top: 0; + bottom: 0; + z-index: 4; + + width: min(25%, 600px); + height: 100dvh; + overflow-y: scroll; + padding: 0; + + box-shadow: 0.75rem 0 1rem 0.75rem rgba(0, 0, 0, 0.35); + background-color: var(--bg5); + backdrop-filter: blur(20px); + + display: flex; + flex-direction: column; + justify-content: space-between; + + @media (prefers-reduced-motion: no-preference) { + transition: right 0.2s ease-out; + transition-property: right, box-shadow; + scroll-behavior: smooth; + } + + .menu-open-tools & { + right: 0; + } + + .panel-close { + display: block !important; + } + .tools-section { + padding: 1rem 1rem 2rem; + &.app-log { + padding-bottom: 0; + } + } +} + +.app-settings { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1rem; + h3 { + margin: 0; + flex: 0 0 100%; + user-select: none; + } +} + +.switch-lang { + display: flex; + gap: 1rem; + line-height: 24px; + label { + display: block; + width: 24px; + height: 24px; + padding: 0.3rem 0.5rem; + svg { + width: 24px; + height: 24px; + fill: var(--button-svg); + @media (forced-colors: active) { + fill: ButtonText; + } + } + } +} + +.switch-theme { + &.theme-dark { + .btn-label.dark { + display: none; + } + } + &.theme-light { + .btn-label.light { + display: none; + } + } +} + +.loaded-files-list { + @include mixins.box-shadow-inner(); + + li { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + } + li + li { + margin-top: 1rem; + } + .name { + flex: 0 0 100%; + color: var(--accent); + } + + @media (forced-colors: none) { + .multiple-files & li { + position: relative; + padding-left: 1.25rem; + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + z-index: 1; + width: 0.25rem; + border-radius: 0.25rem; + background-color: var(--actor-accent-ok, transparent); + } + } + } +} + +.manage-files-actions { + display: flex; + flex-wrap: wrap; + gap: 1rem; + + .file-loader { + @include mixins.visually-hidden(); + } + + .tip { + flex: 0 0 100%; + font-size: 0.8em; + text-align: center; + font-style: italic; + opacity: 0.85; + } +} + +.app-log { + ul { + height: 20dvh; + overflow-y: auto; + @include mixins.box-shadow-inner(); + } + .msg { + b { + font-weight: normal; + color: var(--accent); + } + } + .time { + opacity: 0.65; + } +} + +.about { + font-size: 0.85em; + text-align: center; +} + +.welcome { + .tools { + margin-top: auto; + + h3, + .app-log { + display: none; + } + } + .manage-files { + display: none; + } + .about { + margin-top: 4rem; + } } diff --git a/dev/src/css/_toot.scss b/dev/src/css/_toot.scss index fe22d82..3936728 100644 --- a/dev/src/css/_toot.scss +++ b/dev/src/css/_toot.scss @@ -62,7 +62,7 @@ $meta-visible: 100ch; overflow-wrap: break-word; background-color: var(--bg0); border-radius: 0.5rem; - box-shadow: 0 0.3rem 0.4rem -0.2rem rgba(0, 0, 0, 0.3); + @include mixins.box-shadow-post(); @media (forced-colors: active) { border: 1px solid CanvasText; @@ -113,7 +113,6 @@ $meta-visible: 100ch; z-index: 1; width: 0.25rem; border-radius: 0.25rem; - background-color: red; background-color: var(--actor-accent-ok, transparent); } } @@ -193,7 +192,7 @@ $meta-visible: 100ch; object-fit: cover; font-size: 0.6em; border-radius: 0.5rem; - box-shadow: 0 0.3rem 0.4rem -0.2rem rgba(0, 0, 0, 0.3); + @include mixins.box-shadow-post(); mix-blend-mode: multiply; @media (prefers-reduced-motion: no-preference) { transition: filter 0.2s ease-out; @@ -253,7 +252,7 @@ $meta-visible: 100ch; max-width: 100%; max-height: 75dvh; border-radius: 0.5rem; - box-shadow: 0 0.3rem 0.4rem -0.2rem rgba(0, 0, 0, 0.3); + @include mixins.box-shadow-post(); } .att-description { padding: 1rem; diff --git a/dev/src/css/_welcome.scss b/dev/src/css/_welcome.scss index 6e549f1..71aedb7 100644 --- a/dev/src/css/_welcome.scss +++ b/dev/src/css/_welcome.scss @@ -18,10 +18,6 @@ line-height: 1.4; position: relative; z-index: 1; - - .load-new { - display: none; - } } .intro { diff --git a/dev/src/pages/index.astro b/dev/src/pages/index.astro index 995f740..dd1c643 100644 --- a/dev/src/pages/index.astro +++ b/dev/src/pages/index.astro @@ -22,6 +22,9 @@ import SvgSprites from '../components/SvgSprites.astro'; - + + + + diff --git a/dist/_astro/index.33CKCK6j.css b/dist/_astro/index.33CKCK6j.css new file mode 100644 index 0000000..b80fc7a --- /dev/null +++ b/dist/_astro/index.33CKCK6j.css @@ -0,0 +1 @@ +@charset "UTF-8";*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}.actor-summary *,.toot-content *{all:revert}.actor-summary>*:first-child,.toot-content>*:first-child{margin-top:0}.actor-summary>*:last-child,.toot-content>*:last-child{margin-bottom:0}.actor-summary a,.toot-content a{color:var(--accent)}html{--bg0: #fff;--bg1: hsl(30, 12%, 95%);--bg2: hsl(30, 12%, 90%);--bg3: hsl(30, 15%, 87%);--bg4: hsl(30, 17.5%, 84%);--bg5: hsl(30, 25%, 95%);--bg6: #fff;--fg0: #000;--fg1: hsl(30, 10%, 20%);--fg2: hsl(30, 10%, 30%);--fg-inv: #fff;--menu-bg: hsl(30, 25%, 20%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 10%, 20%);--panel-close-hover: hsl(30, 75%, 38%);--accent: hsl(30, 100%, 30%);--accent-dark: hsl(30, 100%, 26%);--accent-light: hsl(30, 75%, 38%);--accent-light2: hsl(30, 75%, 50%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .75);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .35);--bg-input-hover: rgba(255, 255, 255, .65);--bg-input-focus: rgb(255, 255, 255);--bg-button: rgb(255, 255, 255);--bg-button-hover: hsl(30, 50%, 95%);--button-svg: hsl(30, 10%, 20%);--button-svg-hover: hsl(30, 75%, 50%);--button-svg-focus: hsl(30, 75%, 50%);--button-svg-active: hsl(30, 75%, 50%);--fg-button-focus: hsl(30, 100%, 30%);--fg-button-active: hsl(30, 100%, 30%);--posts-count: hsl(30, 75%, 38%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 38%);--stripe1: hsl(30, 100%, 30%);--stripe2: hsl(30, 100%, 26%);--stripe-fg: #fff;--private-post-bg: hsl(30, 50%, 96%);--private-post-border: hsl(30, 75%, 50%)}html .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 37.5%, 11%);--actor-bg0-ok: oklch(21% 2% var(--actor-hue));--actor-bg1: hsl(30, 32.5%, 15%);--actor-bg1-ok: oklch(24% 2% var(--actor-hue));--actor-bg2: hsl(30, 25%, 20%);--actor-bg2-ok: oklch(27% 2% var(--actor-hue));--actor-bg3: hsl(30, 25%, 25%);--actor-bg3-ok: oklch(30% 2% var(--actor-hue));--actor-bg4: hsl(30, 22.5%, 40%);--actor-bg4-ok: oklch(40% 10% var(--actor-hue));--actor-fg0: #fff;--actor-fg1: hsl(30, 40%, 80%);--actor-fg1-ok: oklch(92% 8% var(--actor-hue));--actor-tabs-bg: hsl(30, 12%, 17%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html .actors-tabs button,html .toot-content,html .loaded-files-list li{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}@media (prefers-color-scheme: dark){html{color-scheme:dark;--bg0: hsl(30, 6%, 20%);--bg1: hsl(30, 8%, 13%);--bg2: hsl(30, 10%, 10%);--bg3: hsl(30, 12%, 8%);--bg4: hsl(30, 12%, 8%);--bg5: hsl(30, 10%, 10%);--bg6: rgba(255, 255, 255, .07);--fg0: hsl(30, 5%, 95%);--fg1: hsl(30, 10%, 80%);--fg2: hsl(30, 15%, 50%);--fg-inv: #000;--menu-bg: hsl(30, 25%, 5%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 5%, 90%);--panel-close-hover: hsl(30, 0%, 100%);--accent: hsl(30, 70%, 65%);--accent-dark: hsl(30, 55%, 50%);--accent-light: hsl(30, 75%, 60%);--accent-light2: hsl(30, 75%, 68%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .9);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .1);--bg-input-hover: rgba(255, 255, 255, .15);--bg-input-focus: rgba(255, 255, 255, .25);--bg-button: hsl(30, 25%, 20%);--bg-button-hover: hsl(30, 25%, 30%);--button-svg: hsl(30, 10%, 80%);--button-svg-hover: hsl(30, 65%, 65%);--button-svg-focus: hsl(30, 65%, 65%);--button-svg-active: hsl(30, 65%, 65%);--fg-button-focus: hsl(30, 70%, 65%);--fg-button-active: hsl(30, 70%, 65%);--posts-count: hsl(30, 5%, 95%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 40%);--stripe1: hsl(30, 75%, 25%);--stripe2: hsl(30, 75%, 30%);--stripe-fg: hsl(30, 5%, 95%);--private-post-bg: hsl(30, 10%, 10%);--private-post-border: hsl(30, 55%, 50%)}html .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 32%, 2%);--actor-bg0-ok: oklch(11% 1% var(--actor-hue));--actor-bg1: hsl(30, 30%, 4%);--actor-bg1-ok: oklch(14% 1% var(--actor-hue));--actor-bg2: hsl(30, 27%, 6%);--actor-bg2-ok: oklch(17% 1% var(--actor-hue));--actor-bg3: hsl(30, 25%, 8%);--actor-bg3-ok: oklch(20% 1% var(--actor-hue));--actor-bg4: hsl(30, 20%, 15%);--actor-bg4-ok: oklch(30% 6% var(--actor-hue));--actor-fg0: hsl(30, 5%, 95%);--actor-fg1: hsl(30, 65%, 65%);--actor-fg1-ok: oklch(85% 25% var(--actor-hue));--actor-tabs-bg: hsl(30, 20%, 8%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html .actors-tabs button,html .toot-content,html .loaded-files-list li{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}}html.light{color-scheme:light;--bg0: #fff;--bg1: hsl(30, 12%, 95%);--bg2: hsl(30, 12%, 90%);--bg3: hsl(30, 15%, 87%);--bg4: hsl(30, 17.5%, 84%);--bg5: hsl(30, 25%, 95%);--bg6: #fff;--fg0: #000;--fg1: hsl(30, 10%, 20%);--fg2: hsl(30, 10%, 30%);--fg-inv: #fff;--menu-bg: hsl(30, 25%, 20%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 10%, 20%);--panel-close-hover: hsl(30, 75%, 38%);--accent: hsl(30, 100%, 30%);--accent-dark: hsl(30, 100%, 26%);--accent-light: hsl(30, 75%, 38%);--accent-light2: hsl(30, 75%, 50%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .75);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .35);--bg-input-hover: rgba(255, 255, 255, .65);--bg-input-focus: rgb(255, 255, 255);--bg-button: rgb(255, 255, 255);--bg-button-hover: hsl(30, 50%, 95%);--button-svg: hsl(30, 10%, 20%);--button-svg-hover: hsl(30, 75%, 50%);--button-svg-focus: hsl(30, 75%, 50%);--button-svg-active: hsl(30, 75%, 50%);--fg-button-focus: hsl(30, 100%, 30%);--fg-button-active: hsl(30, 100%, 30%);--posts-count: hsl(30, 75%, 38%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 38%);--stripe1: hsl(30, 100%, 30%);--stripe2: hsl(30, 100%, 26%);--stripe-fg: #fff;--private-post-bg: hsl(30, 50%, 96%);--private-post-border: hsl(30, 75%, 50%)}html.light .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 37.5%, 11%);--actor-bg0-ok: oklch(21% 2% var(--actor-hue));--actor-bg1: hsl(30, 32.5%, 15%);--actor-bg1-ok: oklch(24% 2% var(--actor-hue));--actor-bg2: hsl(30, 25%, 20%);--actor-bg2-ok: oklch(27% 2% var(--actor-hue));--actor-bg3: hsl(30, 25%, 25%);--actor-bg3-ok: oklch(30% 2% var(--actor-hue));--actor-bg4: hsl(30, 22.5%, 40%);--actor-bg4-ok: oklch(40% 10% var(--actor-hue));--actor-fg0: #fff;--actor-fg1: hsl(30, 40%, 80%);--actor-fg1-ok: oklch(92% 8% var(--actor-hue));--actor-tabs-bg: hsl(30, 12%, 17%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html.light .actors-tabs button,html.light .toot-content,html.light .loaded-files-list li{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html.dark{color-scheme:dark;--bg0: hsl(30, 6%, 20%);--bg1: hsl(30, 8%, 13%);--bg2: hsl(30, 10%, 10%);--bg3: hsl(30, 12%, 8%);--bg4: hsl(30, 12%, 8%);--bg5: hsl(30, 10%, 10%);--bg6: rgba(255, 255, 255, .07);--fg0: hsl(30, 5%, 95%);--fg1: hsl(30, 10%, 80%);--fg2: hsl(30, 15%, 50%);--fg-inv: #000;--menu-bg: hsl(30, 25%, 5%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 5%, 90%);--panel-close-hover: hsl(30, 0%, 100%);--accent: hsl(30, 70%, 65%);--accent-dark: hsl(30, 55%, 50%);--accent-light: hsl(30, 75%, 60%);--accent-light2: hsl(30, 75%, 68%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .9);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .1);--bg-input-hover: rgba(255, 255, 255, .15);--bg-input-focus: rgba(255, 255, 255, .25);--bg-button: hsl(30, 25%, 20%);--bg-button-hover: hsl(30, 25%, 30%);--button-svg: hsl(30, 10%, 80%);--button-svg-hover: hsl(30, 65%, 65%);--button-svg-focus: hsl(30, 65%, 65%);--button-svg-active: hsl(30, 65%, 65%);--fg-button-focus: hsl(30, 70%, 65%);--fg-button-active: hsl(30, 70%, 65%);--posts-count: hsl(30, 5%, 95%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 40%);--stripe1: hsl(30, 75%, 25%);--stripe2: hsl(30, 75%, 30%);--stripe-fg: hsl(30, 5%, 95%);--private-post-bg: hsl(30, 10%, 10%);--private-post-border: hsl(30, 55%, 50%)}html.dark .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 32%, 2%);--actor-bg0-ok: oklch(11% 1% var(--actor-hue));--actor-bg1: hsl(30, 30%, 4%);--actor-bg1-ok: oklch(14% 1% var(--actor-hue));--actor-bg2: hsl(30, 27%, 6%);--actor-bg2-ok: oklch(17% 1% var(--actor-hue));--actor-bg3: hsl(30, 25%, 8%);--actor-bg3-ok: oklch(20% 1% var(--actor-hue));--actor-bg4: hsl(30, 20%, 15%);--actor-bg4-ok: oklch(30% 6% var(--actor-hue));--actor-fg0: hsl(30, 5%, 95%);--actor-fg1: hsl(30, 65%, 65%);--actor-fg1-ok: oklch(85% 25% var(--actor-hue));--actor-tabs-bg: hsl(30, 20%, 8%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html.dark .actors-tabs button,html.dark .toot-content,html.dark .loaded-files-list li{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}body{margin:0;padding:0;color:var(--fg1);font-size:16px;background:var(--bg2);font-family:sans-serif}h1,h2,h3,h4,h5,h6{color:var(--fg2)}img{display:block;height:auto;max-width:100%}a,.main-welcome p label{color:var(--accent)}input,button,label.btn,select{padding:.3em .5em;font-family:inherit;border:none}@media (forced-colors: active){input,button,label.btn,select{border:1px solid ButtonText}}button,label.btn,select{border-radius:5px}button,label.btn{position:relative;top:0;line-height:24px;vertical-align:middle;background:var(--bg-button);border-radius:5px;box-shadow:0 .15em #00000059}button:not(:has(svg)),label.btn:not(:has(svg)){padding:.7em 1em}button .btn-icon,button .btn-label,label.btn .btn-icon,label.btn .btn-label{display:inline-block;line-height:24px;vertical-align:top}button .btn-label,label.btn .btn-label{padding:0 .5em}button .btn-icon,label.btn .btn-icon{width:24px;height:24px;fill:var(--button-svg)}@media (forced-colors: active){button .btn-icon,label.btn .btn-icon{fill:ButtonText}}button:hover,label.btn:hover{background:var(--bg-button-hover)}button:hover .btn-icon,label.btn:hover .btn-icon{fill:var(--button-svg-hover)}@media (forced-colors: active){button:hover .btn-icon,label.btn:hover .btn-icon{fill:ButtonText}}button:focus,label.btn:focus{color:var(--fg-button-focus)}button:focus .btn-icon,label.btn:focus .btn-icon{fill:var(--button-svg-focus)}@media (forced-colors: active){button:focus .btn-icon,label.btn:focus .btn-icon{fill:ButtonText}}button:active,label.btn:active{top:.15em;color:var(--fg-button-active);box-shadow:0 0 #0000}button:active .btn-icon,label.btn:active .btn-icon{fill:var(--button-svg-active)}@media (forced-colors: active){button:active .btn-icon,label.btn:active .btn-icon{fill:ButtonText}}button:disabled,label.btn:disabled{top:0;box-shadow:none;color:inherit;opacity:.35;cursor:not-allowed}button:disabled .btn-icon,label.btn:disabled .btn-icon{fill:var(--button-svg)}@media (forced-colors: active){button:disabled .btn-icon,label.btn:disabled .btn-icon{fill:GrayText}}input,select{color:inherit;background-color:var(--bg-input)}input:hover,select:hover{background-color:var(--bg-input-hover)}input:focus,input:focus-visible,select:focus,select:focus-visible{background-color:var(--bg-input-focus)}input[type=number]{width:6ch;text-align:center;-moz-appearance:textfield;appearance:textfield}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}details summary{cursor:pointer;user-select:none;display:flex;gap:.75rem;align-items:center}details summary .summary-icon{min-width:1.3rem;text-align:center}details summary .summary-icon svg{height:1.3em;aspect-ratio:1}details pre,details textarea{white-space:pre-wrap;word-break:break-all;font-size:.8em;color:inherit}details textarea{display:block;width:100%;resize:vertical;border:none;background:transparent;min-height:20em;min-height:15.5lh}@supports (field-sizing: content){details textarea{min-height:0;field-sizing:content}}::selection{color:var(--selection-text);background-color:var(--selection-bg)}*:focus-visible,.tags-group button:focus-visible div,.toots-filter:has([type=checkbox]:focus-visible){text-decoration:none;outline:2px solid var(--accent-light);outline-offset:2px;border-radius:2px;position:relative;z-index:1}*:focus-visible.mobile-menu-panel,.tags-group button:focus-visible div.mobile-menu-panel,.toots-filter:has([type=checkbox]:focus-visible).mobile-menu-panel{outline-offset:-4px;border-radius:6px}.visually-hidden:not(:focus):not(:active){width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}[inert]>*{opacity:.5}.nojs{margin:5dvh 10dvw;padding:2em;color:#fff;text-align:center;font-weight:700;border-radius:.5rem;background-color:#600}@media (forced-colors: active){details summary{color:ButtonText!important}details summary svg{fill:ButtonText!important}.tags-group button *{color:ButtonText!important}}html{width:100dvw;height:100dvh;overflow:hidden}.main-section-inner{position:relative;z-index:1;height:100dvh;max-width:100dvw;overflow:hidden;display:grid;grid-template-areas:"actor filters header tags" "actor filters toots tags";grid-template-columns:min(25%,600px) min(20%,400px) 1fr min(20%,400px);grid-template-rows:min-content 1fr}.actor{grid-area:actor}.toots-header{grid-area:header}.toots-filters{grid-area:filters}.toots{grid-area:toots}.toots-tags{grid-area:tags}.mobile-menu{display:none}.toots-header{white-space:nowrap;overflow:hidden;background-color:var(--bg3)}.toots-header h2{padding:1rem 1rem 0}.toots-header h2 .order{font-weight:400}.toots-header .nb{color:var(--posts-count)}.toots-header .toggle-order{margin-bottom:5px}.toots-header .open-tools{float:right;margin-left:5px}.toots-header .paging-options-toggle{display:none}.toots{background:var(--bg3);overflow:auto;container-type:inline-size}@media (prefers-reduced-motion: no-preference){.toots{scroll-behavior:smooth}}.paging{display:flex;justify-content:space-between;align-items:center;gap:.5rem;padding:1rem}.paging .direction-back,.paging .direction-fwd{display:flex;flex-wrap:wrap;gap:.25rem}.paging .direction-back{justify-content:flex-start}.paging .direction-fwd{justify-content:flex-end}.toots .paging{padding-bottom:2rem}@media (width < 400px){.toots .paging .direction-back .btn-label,.toots .paging .direction-fwd .btn-label,.toots .paging .paging-options-toggle .btn-label{width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}}.toots-header{container-type:inline-size}.toots-header input{font-size:inherit}.toots-header .paging{clear:both;flex-wrap:wrap}.toots-header .paging .paging-options,.toots-header .paging .paging-options-toggle{order:2;user-select:none}.toots-header .paging .direction-back{order:1}.toots-header .paging .direction-fwd{order:3}.toots-header .paging-options-reverse{display:none}@container (width < 675px){.toots-header .toggle-order{display:none}.toots-header .paging-options-toggle{display:block}.toots-header .paging-options-reverse{display:block;padding:0 1rem 1rem}.toots-header .paging .paging-options{font-size:.85em;display:none;order:4;flex-basis:calc(100% + 2rem);position:relative;margin:.5rem -1rem -1rem;text-align:center;background:var(--bg4);box-shadow:0 .75rem .75rem -1rem #000 inset}.toots-header .paging .paging-options:before{content:"";position:absolute;left:calc(50% - .5rem);top:0;z-index:2;width:0;height:0;border:.5rem solid transparent;border-top:.4rem solid var(--bg3)}@media (forced-colors: active){.toots-header .paging .paging-options:before{content:none;display:none}}.toots-header .paging .paging-options.open{display:block}.toots-header .paging-options-inner{display:inline-block;padding:1rem}}@container (width < 550px){.toots-header .paging .direction-back .btn-label,.toots-header .paging .direction-fwd .btn-label,.toots-header .paging .paging-options-toggle .btn-label{width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}}@keyframes movingStripes{0%{background-position:0 0}to{background-position:-2.8284271247rem 0}}.welcome{max-width:80ch;height:100dvh;margin:0 auto;padding:10dvh 2rem;display:flex;flex-direction:column;overflow:auto;line-height:1.4;position:relative;z-index:1}.intro h1{margin-bottom:7dvh;text-align:center;line-height:1.1}.intro h1 .accronym{display:block;font-size:.8em;opacity:.7}.intro p{margin:1em 0}.intro p label{color:var(--accent);text-decoration:underline;cursor:pointer}@media (forced-colors: active){.intro p label{color:ButtonText}}.file-loader{display:block;width:100%;height:10rem;max-height:70dvh;margin:7dvh 0;padding:1rem;border-radius:1rem;background:var(--bg0)}.file-loader:hover,.file-loader:focus{background:var(--bg0);outline:2px solid var(--accent-light);outline-offset:2px}.loading .file-loader{display:flex;justify-content:center;align-items:center;font-size:2em;background-color:var(--accent);color:var(--stripe-fg);background:repeating-linear-gradient(45deg,var(--stripe1),var(--stripe1) 1rem,var(--stripe2) 1rem,var(--stripe2) 2rem);background-size:200% auto}.loading .file-loader:hover,.loading .file-loader:focus{outline:none}@media (prefers-reduced-motion: no-preference){.loading .file-loader{animation:movingStripes 2s linear infinite}}.main-page.highlight-drag:before{content:"";position:absolute;inset:0;z-index:-1;background:repeating-linear-gradient(45deg,var(--stripe1),var(--stripe1) 1rem,var(--stripe2) 1rem,var(--stripe2) 2rem);background-size:200% auto}@media (prefers-reduced-motion: no-preference){.main-page.highlight-drag:before{animation:movingStripes 2s linear infinite}}.main-page.highlight-drag:after{content:"";position:absolute;inset:1rem;z-index:0;background:var(--bg2)}.loading{justify-content:center}.loading .file-loader{margin:0}.actor{position:relative}.actor:before{content:"";position:absolute;right:0;top:0;bottom:0;z-index:1;width:2rem;box-shadow:-1.5rem 0 2rem -2rem #000 inset}.actor a{color:var(--actor-fg0)}.actor h1,.actor h2,.actor h3,.actor h4{color:inherit}.actor summary{padding:1rem}.actor summary svg{fill:var(--actor-fg1)}.actor summary:hover,.actor summary:focus{color:var(--actor-fg0)}.actor summary:hover svg,.actor summary:focus svg{fill:var(--actor-fg0)}.actor details:not(.comment) summary:focus-visible{outline-offset:-6px;border-radius:10px}.actors-wrapper{height:100%;display:flex;flex-direction:column;justify-content:flex-start}.actor-panel{flex-grow:1;height:100%;container-type:inline-size;display:flex;flex-direction:column;justify-content:flex-start;overflow-y:auto;overflow-x:hidden;color:var(--actor-fg1);background:var(--actor-bg3)}.actor-pretty{flex-grow:1;flex-shrink:0;padding:1rem 1rem 2rem}.actor-banner button,.actor-id button{padding:0;background:transparent;box-shadow:none;border-radius:0;cursor:pointer}.actor-banner button:active,.actor-id button:active{top:0}.actor-banner{margin:-1rem -1rem 0}.actor-banner button:focus-visible{outline-offset:-4px;border-radius:8px;overflow:visible}.actor-banner button:focus-visible img{border-radius:0}.actor-id{position:relative;z-index:1;display:grid;grid-template-areas:"img name" "img url";grid-template-columns:100px 1fr;grid-template-rows:min-content 1fr}.actor-id button{border-radius:.5rem}.actor-id>*{position:relative;top:-2rem!important}.actor-avatar{grid-area:img;width:100px;border-radius:.5rem;cursor:pointer}.actor-avatar img{width:100px;height:auto;border-radius:.5rem}.actor-avatar.no-avatar{cursor:default}.actor-name{grid-area:name;margin:2.4rem 0 0;padding-left:1rem;font-weight:400}.actor-url{grid-area:url;padding-left:1rem}.actor-summary{margin:2rem 0;padding:1rem;color:var(--actor-fg0);line-height:1.3;background:var(--actor-bg4);background:radial-gradient(circle at 200% 150%,var(--actor-bg3),var(--actor-bg4));border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d}.actor-infos{display:grid;grid-template-columns:minmax(0,max-content) minmax(50%,1fr);margin:2rem 0}.actor-infos dl{grid-column:span 2;display:grid;grid-template-columns:subgrid}.actor-infos dt{padding:.5rem 1rem;font-weight:400}.actor-infos dd{padding:.5rem 1rem;color:var(--actor-fg0);border-left:1px solid var(--actor-bg4)}@container (width < 340px){.actor-infos{display:block}.actor-infos dl{display:block;padding:.5rem 1rem;border-bottom:1px solid var(--actor-bg4)}.actor-infos dl:last-child{border-bottom:none}.actor-infos dt{padding:0;margin-bottom:.5rem}.actor-infos dd{padding:0;border-left:none}}.actor-posts-count{display:flex;flex-wrap:wrap;justify-content:space-around;margin:2rem 0 0;gap:1rem}.actor-posts-count .count{display:block;color:var(--actor-fg0);font-size:1.6em;font-weight:700}.actor-posts-count .total,.actor-posts-count .archive{flex:0 0 calc(50% - .5rem);padding:1rem;text-align:center;border-radius:.5rem;background-color:#00000040}@media (forced-colors: active){.actor-posts-count .total,.actor-posts-count .archive{border:1px dotted CanvasText}}.actor-posts-count .comment{flex:0 0 100%;font-size:.8em}.actor-posts-count .comment summary{padding:0;font-weight:700}.actor-posts-count .comment summary:hover,.actor-posts-count .comment summary:focus-visible{color:var(--actor-fg0)}.actor-posts-count .comment[open] summary{color:var(--actor-fg0)}.actor-posts-count .comment p{margin-top:.5rem;padding-left:2.05rem}.actor-raw{background:var(--actor-bg2);box-shadow:0 .75rem .75rem -1rem #000 inset}.actor-raw .details-content{padding:1rem 1rem 2rem;overflow:auto;overflow-wrap:break-word}.actor-raw textarea{color:var(--actor-fg0)}.actor-likes-bookmarks{background:var(--actor-bg0);overflow-wrap:anywhere}.actor-likes-bookmarks h2{display:inline;font-size:inherit;font-weight:400}.actor-likes-bookmarks ul{font-size:.9em;padding:0 1rem 2rem}.actor-likes-bookmarks li{margin:0 -.25rem;padding:.25rem 0;list-style:none}.actor-likes-bookmarks a{display:inline-block;padding:.25rem;line-height:1.1;text-decoration:none}.actor-likes-bookmarks a:hover{text-decoration:underline}.actor-likes-bookmarks a .url-instance{padding-right:1ch;color:var(--actor-fg1)}.actor-likes-bookmarks a .url-instance:after{padding-left:1ch;content:"»"}.actor-likes-bookmarks a .url-actor{padding-right:1ch;color:var(--actor-fg0);font-weight:700}.actor-likes-bookmarks a .url-actor:after{padding-left:1ch;content:"»"}.actor-likes-bookmarks a .url-post-id{color:var(--actor-fg1)}.actor-likes-bookmarks .no-content{padding:0 1rem 2rem;text-align:center;font-style:italic}.actor-likes{background:var(--actor-bg1);box-shadow:0 .75rem .75rem -1rem #000 inset}.actor-bookmarks{padding-bottom:1rem;box-shadow:0 .75rem .75rem -1rem #000 inset}.multiple-actors .actors-tabs{flex-grow:0;flex-shrink:0;display:flex;flex-wrap:wrap;gap:.75rem;padding:.75rem 1rem;background-color:var(--actor-tabs-bg);overflow:hidden;user-select:none}.multiple-actors .actors-tabs button{all:unset;padding:.35rem .7rem .4rem;font-size:.85em;font-family:inherit;border-radius:.25rem;color:#fff;background:var(--actor-accent2-ok, transparent);cursor:pointer}.multiple-actors .actors-tabs button span{display:inline-block;padding:0 0 .3rem;border-bottom:.1rem solid transparent}.multiple-actors .actors-tabs button:hover span{border-bottom-color:#ffffff80}.multiple-actors .actors-tabs button[aria-selected=true]{box-shadow:none}.multiple-actors .actors-tabs button[aria-selected=true] span{border-bottom-color:#fff}.multiple-actors .actors-tabs button:focus-visible{outline:2px solid hsl(var(--actor-hue),50%,80%);outline:2px solid var(--actor-accent-ok, var(--actor-accent));outline-offset:2px}@media (forced-colors: active){.multiple-actors .actors-tabs button{border:1px solid ButtonText}.multiple-actors .actors-tabs button span{border-bottom-color:Canvas}.multiple-actors .actors-tabs button[aria-selected=true] span{border-bottom-color:Highlight}}.multiple-actors .actor *:focus-visible{outline-color:var(--actor-accent-ok, var(--accent-light))}.multiple-actors .actor summary svg{fill:var(--actor-fg1-ok, var(--actor-fg1))}.multiple-actors .actor-panel{color:var(--actor-fg1-ok, var(--actor-fg1));background:var(--actor-bg3-ok, var(--actor-bg3))}.multiple-actors .actor-summary{background:var(--actor-bg4-ok, var(--actor-bg4));background:radial-gradient(circle at 200% 150%,var(--actor-bg3-ok, var(--actor-bg3)),var(--actor-bg4-ok, var(--actor-bg4)))}.multiple-actors .actor-infos dd{border-left-color:var(--actor-bg4-ok, var(--actor-bg4))}@container (width < 340px){.multiple-actors .actor-infos dl{border-bottom-color:var(--actor-bg4-ok, var(--actor-bg4))}}.multiple-actors .actor-raw{background:var(--actor-bg2-ok, var(--actor-bg2))}.multiple-actors .actor-likes-bookmarks{background:var(--actor-bg0-ok, var(--actor-bg0))}.multiple-actors .actor-likes{background:var(--actor-bg1-ok, var(--actor-bg1))}.multiple-actors .actor-likes-bookmarks a .url-instance,.multiple-actors .actor-likes-bookmarks a .url-post-id{color:var(--actor-fg1-ok, var(--actor-fg1))}.toots-filters{height:100dvh;max-height:100dvh;padding:1rem 1rem 2em;background-color:var(--bg4);overflow-y:auto;container-type:inline-size;user-select:none}.toots-filters h2{margin:0 0 2rem}.toots-filters-reset{margin-top:1rem;text-align:right}.toots-filters-reset .reset:not(:disabled){box-shadow:0 0 0 .25rem var(--accent)}.toots-filters-group{margin:0 0 1rem}.toots-filters-group-title{margin:0 0 .5rem}.toots-filters-group:has(.text){display:grid;grid-template-columns:min-content 1fr;gap:.35rem}.toots-filters-group:has(.text) .toots-filter{grid-column:span 2}.toots-filters-group:has(.text) .toots-filter.text{display:grid;grid-template-columns:subgrid}@container (width < 260px){.toots-filters-group:has(.text){display:block}.toots-filters-group:has(.text) .toots-filter.text label{margin:.5rem 0 .25rem}}.toots-filter{margin-bottom:2px}.toots-filter label{display:block;padding:.2rem .3rem;border-radius:.2rem}.toots-filter.sep-above{margin-top:1rem}.toots-filter label{font-size:.85em}.toots-filter.text input{display:block;width:100%}.toots-filter.checkbox label{position:relative;padding-left:1.7rem;background:transparent url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e") no-repeat 2px 2px/auto 18.4px scroll}.toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}.toots-filter.checkbox input{position:absolute;left:1px;top:1px;z-index:1;opacity:0}@media (forced-colors: active){.toots-filter.checkbox label{background-image:none!important}.toots-filter.checkbox input{left:3px;top:4px;opacity:1;outline:none!important}}.toots-filter:hover label,.toots-filter:focus-visible label,.toots-filter:focus-within label{background-color:#ffffff59}.toots-filter.active label{color:var(--fg-inv);opacity:1;background-color:var(--accent)}@media (forced-colors: active){.toots-filter.active label{outline:1px solid Highlight}}@media (prefers-color-scheme: light){.toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}.toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}.toots-filter:hover label,.toots-filter:focus-visible label,.toots-filter:focus-within label{background-color:#ffffff59}.toots-filter.active label{background-color:var(--accent)}}html.light .toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}html.light .toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}html.light .toots-filter:hover label,html.light .toots-filter:focus-visible label,html.light .toots-filter:focus-within label{background-color:#ffffff59}html.light .toots-filter.active label{background-color:var(--accent)}@media (prefers-color-scheme: dark){.toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}.toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}.toots-filter:hover label,.toots-filter:focus-visible label,.toots-filter:focus-within label{background-color:#ffffff1a}.toots-filter.active label{background-color:var(--accent-light)}}html.dark .toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}html.dark .toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}html.dark .toots-filter:hover label,html.dark .toots-filter:focus-visible label,html.dark .toots-filter:focus-within label{background-color:#ffffff1a}html.dark .toots-filter.active label{background-color:var(--accent-light)}.toot{background:var(--bg1)}@container (width >= 100ch){.toot{display:grid;grid-template-areas:"header meta" "pretty meta" "infos meta" "raw meta";grid-template-columns:50% 50%;grid-template-rows:min-content min-content 1fr min-content}}.toot:last-child{border-bottom:none}.toot h3,.toot h4{margin:0}.toot-pretty{grid-area:pretty;padding:1rem}.toot-pretty .fetch-boost-data{padding-top:1rem;text-align:right}.toot-summary{padding:.5rem 1rem;margin-bottom:1rem;color:var(--stripe-fg);background:repeating-linear-gradient(45deg,var(--stripe1),var(--stripe1) 10px,var(--stripe2) 10px,var(--stripe2) 20px);box-shadow:0 .15rem .3rem -.2rem #00000059 inset;border-radius:.5rem}@media (forced-colors: active){.toot-summary{outline:1px solid Highlight}}.toot-content{margin:0;padding:1rem;color:var(--fg0);line-height:1.3;overflow-wrap:break-word;background-color:var(--bg0);border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d}@media (forced-colors: active){.toot-content{border:1px solid CanvasText}}.toot-type-boost .toot-content{position:relative;overflow-wrap:break-word;background:radial-gradient(circle at 100% 100%,var(--bg2),var(--bg0))}.toot-type-boost .toot-content:after{content:"";position:absolute;inset:0;z-index:1;background:transparent url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e") no-repeat calc(100% - 1rem) calc(100% - .5rem) /auto 50% scroll}.toot-type-boost .toot-content a,.toot-type-boost .toot-content .toot-content-inner{position:relative;z-index:2}.toot-visibility-mentioned .toot-content{background-color:var(--private-post-bg);border:2px dashed var(--private-post-border)}@media (forced-colors: none){.multiple-actors .toot-content{position:relative;padding-left:1.6rem}.multiple-actors .toot-content:before{content:"";position:absolute;left:.35rem;top:.35rem;bottom:.35rem;z-index:1;width:.25rem;border-radius:.25rem;background-color:var(--actor-accent-ok, transparent)}}.toot-attachments{margin:1rem 0 0}.toot-attachments ul,.toot-attachments li{list-style:none}.toot-attachments .att-description{font-size:.85em;line-height:1.4}.toot-attachments .no-alt-text .att-description{font-style:italic}.toot-attachments .desc-source{display:block;margin-top:.5rem;font-style:normal;word-break:break-all;line-height:1.2}.toot-attachments .desc-source strong{color:var(--accent);font-weight:400}.att-img+.att-img{margin-top:1rem}.att-img .att-wrapper{display:flex;align-items:flex-start}.att-img .att-img-wrapper{width:clamp(75px,7.5dvw,150px);flex:0 0 clamp(75px,7.5dvw,150px);padding:0;background:transparent;border-radius:.5rem;box-shadow:none;cursor:pointer;background:#fff}@media (prefers-reduced-motion: no-preference){.att-img .att-img-wrapper{transition:background-color .35s ease-out}}.att-img .att-img-wrapper:hover,.att-img .att-img-wrapper:focus{box-shadow:none;border-radius:.5rem}.att-img .att-img-wrapper:hover{transition-duration:.2s;background:var(--accent-light3)}.att-img .att-img-wrapper:hover img{filter:grayscale(50%)}@media (forced-colors: active){.att-img .att-img-wrapper:hover img{filter:none}}.att-img img{display:block;width:100%;aspect-ratio:1;object-fit:cover;font-size:.6em;border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d;mix-blend-mode:multiply}@media (prefers-reduced-motion: no-preference){.att-img img{transition:filter .2s ease-out}}@media (forced-colors: active){.att-img img{mix-blend-mode:normal}}.att-img .att-description{padding:0 1rem}.att-img.no-alt-text .att-img-wrapper{position:relative}.att-img.no-alt-text .att-img-wrapper:before{content:"";position:absolute;right:-.5rem;bottom:-.25rem;z-index:2;width:1.5rem;height:1.5rem;border-radius:10rem;background:#c00 url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-120q-33%200-56.5-23.5T400-200q0-33%2023.5-56.5T480-280q33%200%2056.5%2023.5T560-200q0%2033-23.5%2056.5T480-120Zm0-240q-33%200-56.5-23.5T400-440v-320q0-33%2023.5-56.5T480-840q33%200%2056.5%2023.5T560-760v320q0%2033-23.5%2056.5T480-360Z'/%3e%3c/svg%3e") no-repeat 50% 50%/contain scroll}.att-sound{padding:1rem;background-color:var(--bg2);border-radius:.5rem;box-shadow:0 .15rem .3rem -.2rem #00000059 inset}.att-sound+.att-sound{margin-top:1rem}.att-sound audio{display:block;width:100%;margin:0 0 1rem;border-radius:.3rem}.att-video+.att-video{margin-top:1rem}.att-video video{display:block;width:auto;height:auto;max-width:100%;max-height:75dvh;border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d}.att-video .att-description{padding:1rem}.toot-meta{grid-area:meta;display:flex;align-items:flex-start;gap:1rem;padding:1rem;padding-left:0}@container (width < 100ch){.toot-meta{display:none}}.toot-meta dl{margin:0;display:grid;grid-template-columns:min-content 1fr}.toot-meta dt{white-space:nowrap}.toot-meta>div{flex-basis:calc((100% - 2rem) / 3);max-width:calc((100% - 2rem)/2);flex-grow:1;flex-shrink:0;padding:1rem;background-color:var(--bg2);border-radius:.5rem;box-shadow:0 .15rem .3rem -.2rem #00000059 inset}@media (forced-colors: active){.toot-meta>div{border:1px solid CanvasText}}.toot-meta .toot-links{flex-grow:1}.toot-meta .toot-links a{word-break:break-all}.toot-meta h4{margin-bottom:.5rem}.toot-meta li{list-style:none;padding:.35rem 0 0}.toot-meta li a{display:block;padding-left:1.5rem;background-color:transparent;background-repeat:no-repeat;background-position:0 0;background-size:auto 1.2rem;word-break:break-all}@media (forced-colors: active){.toot-meta li a{padding-left:0}}.toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}.toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}.toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}.toot-infos{grid-area:infos;margin:0 1rem;padding:1rem;font-size:.8em;display:flex;flex-wrap:wrap;align-items:flex-start;container-type:inline-size;background:var(--bg2);border-radius:.5rem;box-shadow:0 .15rem .3rem -.2rem #00000059 inset}.toot-infos>span{margin-bottom:.25rem}.toot-infos .type,.toot-infos .visibility,.toot-infos .published,.toot-infos .link a{padding-left:1.6em;background:transparent none no-repeat 0 0/1rem auto scroll}@media (forced-colors: active){.toot-infos .type,.toot-infos .visibility,.toot-infos .published,.toot-infos .link a{padding-left:0}}.toot-infos .published{padding-left:0}.toot-infos .published:before{content:"-";margin:0 1ch}.toot-infos .published,.toot-infos .visibility{margin-right:1rem}.toot-infos .visibility{margin-bottom:0}.toot-infos .link{margin-left:auto}.toot-infos .type{order:1}.toot-infos .author{order:2}.toot-infos .published{order:4}.toot-infos .visibility{order:5}.toot-infos .link{order:3}.toot-infos .published{flex:0 0 100%;margin-right:0;padding-left:1.6em;background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}@media (forced-colors: active){.toot-infos .published{padding-left:0}}.toot-infos .published:before{content:none}.toot-infos .published .updated{display:block}.toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}.toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}.toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}.toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}.toot-infos .link a{padding-left:0;padding-right:1.6em;background-position:100% 50%;background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}@media (forced-colors: active){.toot-infos .link a{padding-right:0;background:none!important}}.toot-raw{grid-area:raw;margin-top:.5rem;padding:0 1rem 2rem}.toot-raw details{border-radius:.5rem}.toot-raw summary{justify-content:flex-end;gap:.3rem;padding:.5rem 1rem;font-size:.8em;text-align:right;opacity:.8;user-select:none}.toot-raw summary:hover,.toot-raw summary:focus-visible{opacity:1;border-radius:.5rem;background-color:var(--bg2)}.toot-raw .summary-icon{display:flex}.toot-raw .summary-icon svg{fill:currentColor}.toot-raw details[open]{background-color:var(--bg2);box-shadow:0 .15rem .3rem -.2rem #00000059 inset}@media (forced-colors: active){.toot-raw details[open]{border:1px solid CanvasText}}.toot-raw details[open] summary{opacity:1;background-color:transparent!important;box-shadow:none}.toot-raw .details-content{padding:1rem;overflow-wrap:break-word}.toots-no-results{padding:4rem 1rem;text-align:center;background:var(--bg1)}.toots-no-results p{margin:1em 0}@media (prefers-color-scheme: light){.toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}.toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}.toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}.toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}.toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}.toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}.toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}.toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}.toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}}html.light .toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}html.light .toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}html.light .toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}html.light .toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}html.light .toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}html.light .toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}html.light .toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}html.light .toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.light .toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.light .toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}html.light .toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}@media (prefers-color-scheme: dark){.toot-type-boost .toot-content:after{opacity:.25}.toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}.toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}.toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}.toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}.toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}.toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}.toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}.toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}.toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}}html.dark .toot-type-boost .toot-content:after{opacity:.25}html.dark .toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}html.dark .toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}html.dark .toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}html.dark .toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}html.dark .toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}html.dark .toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}html.dark .toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}.toots-tags{display:grid;grid-template-rows:min-content repeat(3,1fr);height:100vh;padding-bottom:2rem;overflow:hidden;background-color:var(--bg4);container-type:inline-size}.tags-title{padding:1rem}.tags-group{position:relative;height:100%;overflow:hidden;display:grid;padding:.5rem 1rem;grid-template-rows:min-content 1fr}.tags-group ul{font-size:.85em;list-style:none}.tags-group li{list-style:none;margin:.3rem 4px}.tags-group button{all:unset;display:block;width:100%}.tags-group button div{display:flex;align-items:center;width:100%;gap:.25rem;padding:.1rem .3rem;border-radius:.2rem;border:none;background:transparent;font-family:inherit;font-size:inherit;cursor:pointer}.tags-group button div .count{color:var(--accent-dark);font-size:.85em;font-weight:700}@container (width >= 340px){.tags-group button div .count{display:inline-block;min-width:5ch;text-align:right}}.tags-group button div .name{flex-grow:1;word-break:break-all;display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.tags-group button div .domain{margin-left:auto;padding-left:.5em;font-size:.85em;font-weight:700;letter-spacing:.02em;opacity:.75}.tags-group button:hover .name{text-decoration:underline}.tags-group .active button div{color:var(--fg-inv);background-color:var(--accent)}.tags-group .active button div .count{color:var(--fg-inv)}@container (width >= 340px){.tags-group-header{display:flex;justify-content:space-between;gap:.25rem 1rem}}.tags-group-header h3{white-space:nowrap;overflow:hidden}.tags-group-header h3 .count{color:var(--accent-dark);font-weight:400}.tags-group-filter{flex-grow:1}.tags-group-filter input{display:block;width:100%;margin-top:.25rem}@container (width >= 340px){.tags-group-filter input{margin-top:0}}.tags-group-scroll{overflow:auto;margin-top:.5rem;padding-bottom:2rem}@media (prefers-color-scheme: light){.tags-group .active button div{background-color:var(--accent)}}html.light .tags-group .active button div{background-color:var(--accent)}@media (prefers-color-scheme: dark){.tags-group .active button div{background-color:var(--accent-light)}}html.dark .tags-group .active button div{background-color:var(--accent-light)}.tools-section h3{margin:0 0 1rem}.tools-section ul{list-style:none;font-size:.85em;margin:1rem 0;padding:.5rem 1rem;background-color:var(--bg6);border-radius:.5rem;word-break:break-all}.tools-section li{list-style:none;margin:.5rem 0}.panel-tools{position:absolute;left:auto;right:calc(0px - min(25%,600px));top:0;bottom:0;z-index:4;width:min(25%,600px);height:100dvh;overflow-y:scroll;padding:0;box-shadow:.75rem 0 1rem .75rem #00000059;background-color:var(--bg5);backdrop-filter:blur(20px);display:flex;flex-direction:column;justify-content:space-between}@media (prefers-reduced-motion: no-preference){.panel-tools{transition:right .2s ease-out;transition-property:right,box-shadow;scroll-behavior:smooth}}.menu-open-tools .panel-tools{right:0}.panel-tools .panel-close{display:block!important}.panel-tools .tools-section{padding:1rem 1rem 2rem}.panel-tools .tools-section.app-log{padding-bottom:0}.app-settings{display:flex;flex-wrap:wrap;justify-content:space-between;gap:1rem}.app-settings h3{margin:0;flex:0 0 100%;user-select:none}.switch-lang{display:flex;gap:1rem;line-height:24px}.switch-lang label{display:block;width:24px;height:24px;padding:.3rem .5rem}.switch-lang label svg{width:24px;height:24px;fill:var(--button-svg)}@media (forced-colors: active){.switch-lang label svg{fill:ButtonText}}.switch-theme.theme-dark .btn-label.dark,.switch-theme.theme-light .btn-label.light{display:none}.loaded-files-list{box-shadow:0 .15rem .3rem -.2rem #00000059 inset}.loaded-files-list li{display:flex;flex-wrap:wrap;justify-content:space-between}.loaded-files-list li+li{margin-top:1rem}.loaded-files-list .name{flex:0 0 100%;color:var(--accent)}@media (forced-colors: none){.multiple-files .loaded-files-list li{position:relative;padding-left:1.25rem}.multiple-files .loaded-files-list li:before{content:"";position:absolute;left:0;top:0;bottom:0;z-index:1;width:.25rem;border-radius:.25rem;background-color:var(--actor-accent-ok, transparent)}}.manage-files-actions{display:flex;flex-wrap:wrap;gap:1rem}.manage-files-actions .file-loader{width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}.manage-files-actions .tip{flex:0 0 100%;font-size:.8em;text-align:center;font-style:italic;opacity:.85}.app-log ul{height:20dvh;overflow-y:auto;box-shadow:0 .15rem .3rem -.2rem #00000059 inset}.app-log .msg b{font-weight:400;color:var(--accent)}.app-log .time{opacity:.65}.about{font-size:.85em;text-align:center}.welcome .tools{margin-top:auto}.welcome .tools h3,.welcome .tools .app-log,.welcome .manage-files{display:none}.welcome .about{margin-top:4rem}.mobile-menu{display:none;position:relative;z-index:2;color:var(--menu-fg);background-color:var(--menu-bg)}@media (forced-colors: active){.mobile-menu{color:buttonText}}.mobile-menu ul{list-style:none;height:100dvh;padding:1rem 0;display:flex;flex-direction:column;justify-content:flex-start}.mobile-menu li{display:block;list-style:none}.mobile-menu button{display:flex;flex-direction:column;justify-content:space-between;align-items:center;position:relative;height:75px;aspect-ratio:1;padding:0;color:inherit;font-size:.75em;text-align:center;font-weight:700;line-height:1;letter-spacing:.05em;text-transform:uppercase;background:none;box-shadow:none;border-radius:0}.mobile-menu button:focus-visible{outline-offset:-4px}.mobile-menu button svg{display:block;width:35px;height:auto;margin:5px auto 0;fill:var(--menu-icon)}@media (forced-colors: active){.mobile-menu button svg{fill:buttonText}}.mobile-menu button span{display:block;padding-bottom:10px}.mobile-menu button .visually-hidden{text-transform:initial}.mobile-menu button.menu-filters.filters-active:before{content:"";position:absolute;right:10px;top:5px;z-index:1;width:10px;height:10px;border-radius:10px;background-color:var(--menu-filter-active)}@media (forced-colors: active){.mobile-menu button.menu-filters.filters-active:before{background-color:Highlight}}.mobile-menu button.menu-new{margin-top:auto}.mobile-menu button:hover,.mobile-menu button:focus-visible{background-color:#ffffff1a}@media (forced-colors: active){.mobile-menu button:hover,.mobile-menu button:focus-visible{color:Canvas;background:Highlight}.mobile-menu button:hover span,.mobile-menu button:focus-visible span{color:CanvasText}.mobile-menu button:hover svg,.mobile-menu button:focus-visible svg{fill:Canvas}}.panel-backdrop{display:none;position:fixed;inset:0;z-index:3;background-color:var(--menu-backdrop)}.main-page.menu-open .panel-backdrop{display:block}.panel-close{position:absolute;right:6px;top:6px;z-index:1;color:var(--panel-close);background:transparent;box-shadow:none}.panel-close .btn-icon{fill:currentColor}.panel-close:hover,.panel-close:focus,.panel-close:focus-visible{color:var(--panel-close-hover)}@media (forced-colors: active){.panel-close{color:buttonText}}.actor .panel-close{color:#fff}@media screen and (width < 1200px){.toots-header{z-index:2;box-shadow:0 -10px 10px 10px #00000080}.toots-header .open-tools{display:none}.toots{z-index:1}.main-section-inner{grid-template-areas:"menu header" "menu toots";grid-template-columns:75px 1fr;overflow:hidden}.toots .paging{padding-bottom:1rem}.mobile-menu-panel{position:fixed;inset:0 auto 0 -475px;z-index:3;width:min(400px,100dvw - 75px);box-shadow:0 0 #0000}.mobile-menu-panel.actor:before{content:none}}@media screen and (width < 1200px) and (prefers-reduced-motion: no-preference){.mobile-menu-panel{transition:left .2s ease-out;transition-property:left,box-shadow;scroll-behavior:smooth}}@media screen and (width < 1200px){.mobile-menu{display:block;grid-area:menu;z-index:5}.main-page.menu-open.menu-open-actor .actor,.main-page.menu-open.menu-open-filters .toots-filters,.main-page.menu-open.menu-open-tags .toots-tags,.main-page.menu-open.menu-open-tools .tools{left:75px;box-shadow:-1rem 0 1rem 1rem #00000080}}@media screen and (width < 1200px) and (prefers-reduced-motion: no-preference){.main-page.menu-open.menu-open-actor .actor,.main-page.menu-open.menu-open-filters .toots-filters,.main-page.menu-open.menu-open-tags .toots-tags,.main-page.menu-open.menu-open-tools .tools{transition-duration:.35s}}@media screen and (width < 1200px){.main-page.menu-open.menu-open-actor .menu-actor,.main-page.menu-open.menu-open-filters .menu-filters,.main-page.menu-open.menu-open-tags .menu-tags,.main-page.menu-open.menu-open-tools .menu-tools{color:var(--menu-fg-active);background-color:#fff3}}@media screen and (width < 500px){.main-section-inner{grid-template-areas:"header" "toots" "menu";grid-template-columns:1fr;grid-template-rows:min-content 1fr min-content}.mobile-menu{box-shadow:none}.mobile-menu ul{flex-direction:row;justify-content:space-around;height:auto;padding:0}.mobile-menu li{flex-grow:1}.mobile-menu button{aspect-ratio:unset;width:100%}.mobile-menu-panel{height:auto;bottom:75px;width:100dvw;left:-100dvw;box-shadow:none}.main-page.menu-open.menu-open-actor .actor,.main-page.menu-open.menu-open-filters .toots-filters,.main-page.menu-open.menu-open-tags .toots-tags,.main-page.menu-open.menu-open-tools .tools{left:0}.toots-tags{padding-bottom:1rem}.tags-title{padding-bottom:0}}.overlay{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center}.overlay-content{position:relative;z-index:5}.overlay-content,.overlay-content img{max-width:calc(100dvw - 88px);max-height:calc(100dvh - 44px)}.overlay-content img{box-shadow:0 1rem 2.5rem -1rem #000}.overlay-ui{position:absolute;inset:0;z-index:1}.overlay-ui button{display:block;padding:0;position:absolute;z-index:2;cursor:pointer;border:none;background:transparent;box-shadow:none}.overlay-ui button svg{display:block;width:36px;height:36px;fill:var(--overlay-icon)}.overlay-ui button:hover svg,.overlay-ui button:focus svg,.overlay-ui button:active svg{fill:var(--overlay-icon-hover)}.overlay-ui .viewer-close{top:4px;right:4px}.overlay-ui .viewer-close svg{width:24px;height:24px}.overlay-ui .viewer-next{right:4px;top:calc(50% - 18px)}.overlay-ui .viewer-prev{left:4px;top:calc(50% - 18px)}.overlay-ui .backdrop{position:absolute;inset:0;z-index:1;background-color:var(--overlay-backdrop)} diff --git a/dist/_astro/index.uwcihdcd.css b/dist/_astro/index.uwcihdcd.css deleted file mode 100644 index 1e758ea..0000000 --- a/dist/_astro/index.uwcihdcd.css +++ /dev/null @@ -1 +0,0 @@ -@charset "UTF-8";*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}.actor-summary *,.toot-content *{all:revert}.actor-summary>*:first-child,.toot-content>*:first-child{margin-top:0}.actor-summary>*:last-child,.toot-content>*:last-child{margin-bottom:0}.actor-summary a,.toot-content a{color:var(--accent)}html{--bg0: #fff;--bg1: hsl(30, 10%, 95%);--bg2: hsl(30, 12%, 90%);--bg3: hsl(30, 15%, 87%);--bg4: hsl(30, 17.5%, 84%);--fg0: #000;--fg1: hsl(30, 10%, 20%);--fg2: hsl(30, 10%, 30%);--fg-inv: #fff;--menu-bg: hsl(30, 25%, 20%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 10%, 20%);--panel-close-hover: hsl(30, 75%, 38%);--accent: hsl(30, 100%, 30%);--accent-dark: hsl(30, 100%, 26%);--accent-light: hsl(30, 75%, 38%);--accent-light2: hsl(30, 75%, 50%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .75);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .35);--bg-input-hover: rgba(255, 255, 255, .65);--bg-input-focus: rgb(255, 255, 255);--bg-button: rgb(255, 255, 255);--bg-button-hover: hsl(30, 50%, 95%);--button-svg: hsl(30, 10%, 20%);--button-svg-hover: hsl(30, 75%, 50%);--button-svg-focus: hsl(30, 75%, 50%);--button-svg-active: hsl(30, 75%, 50%);--fg-button-focus: hsl(30, 100%, 30%);--fg-button-active: hsl(30, 100%, 30%);--posts-count: hsl(30, 75%, 38%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 38%);--stripe1: hsl(30, 100%, 30%);--stripe2: hsl(30, 100%, 26%);--stripe-fg: #fff;--private-post-bg: hsl(30, 50%, 96%);--private-post-border: hsl(30, 75%, 50%)}html .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 37.5%, 11%);--actor-bg0-ok: oklch(21% 2% var(--actor-hue));--actor-bg1: hsl(30, 32.5%, 15%);--actor-bg1-ok: oklch(24% 2% var(--actor-hue));--actor-bg2: hsl(30, 25%, 20%);--actor-bg2-ok: oklch(27% 2% var(--actor-hue));--actor-bg3: hsl(30, 25%, 25%);--actor-bg3-ok: oklch(30% 2% var(--actor-hue));--actor-bg4: hsl(30, 22.5%, 40%);--actor-bg4-ok: oklch(40% 10% var(--actor-hue));--actor-fg0: #fff;--actor-fg1: hsl(30, 40%, 80%);--actor-fg1-ok: oklch(92% 8% var(--actor-hue));--actor-tabs-bg: hsl(30, 12%, 17%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html .actors-tabs button,html .toot-content{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}@media (prefers-color-scheme: dark){html{color-scheme:dark;--bg0: hsl(30, 6%, 20%);--bg1: hsl(30, 8%, 13%);--bg2: hsl(30, 10%, 10%);--bg3: hsl(30, 12%, 8%);--bg4: hsl(30, 12%, 8%);--fg0: hsl(30, 5%, 95%);--fg1: hsl(30, 10%, 80%);--fg2: hsl(30, 15%, 50%);--fg-inv: #000;--menu-bg: hsl(30, 25%, 5%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 5%, 90%);--panel-close-hover: hsl(30, 0%, 100%);--accent: hsl(30, 70%, 65%);--accent-dark: hsl(30, 55%, 50%);--accent-light: hsl(30, 75%, 60%);--accent-light2: hsl(30, 75%, 68%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .9);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .1);--bg-input-hover: rgba(255, 255, 255, .15);--bg-input-focus: rgba(255, 255, 255, .25);--bg-button: hsl(30, 25%, 20%);--bg-button-hover: hsl(30, 25%, 30%);--button-svg: hsl(30, 10%, 80%);--button-svg-hover: hsl(30, 65%, 65%);--button-svg-focus: hsl(30, 65%, 65%);--button-svg-active: hsl(30, 65%, 65%);--fg-button-focus: hsl(30, 70%, 65%);--fg-button-active: hsl(30, 70%, 65%);--posts-count: hsl(30, 5%, 95%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 40%);--stripe1: hsl(30, 75%, 25%);--stripe2: hsl(30, 75%, 30%);--stripe-fg: hsl(30, 5%, 95%);--private-post-bg: hsl(30, 10%, 10%);--private-post-border: hsl(30, 55%, 50%)}html .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 32%, 2%);--actor-bg0-ok: oklch(11% 1% var(--actor-hue));--actor-bg1: hsl(30, 30%, 4%);--actor-bg1-ok: oklch(14% 1% var(--actor-hue));--actor-bg2: hsl(30, 27%, 6%);--actor-bg2-ok: oklch(17% 1% var(--actor-hue));--actor-bg3: hsl(30, 25%, 8%);--actor-bg3-ok: oklch(20% 1% var(--actor-hue));--actor-bg4: hsl(30, 20%, 15%);--actor-bg4-ok: oklch(30% 6% var(--actor-hue));--actor-fg0: hsl(30, 5%, 95%);--actor-fg1: hsl(30, 65%, 65%);--actor-fg1-ok: oklch(85% 25% var(--actor-hue));--actor-tabs-bg: hsl(30, 20%, 8%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html .actors-tabs button,html .toot-content{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}}html.light{color-scheme:light;--bg0: #fff;--bg1: hsl(30, 10%, 95%);--bg2: hsl(30, 12%, 90%);--bg3: hsl(30, 15%, 87%);--bg4: hsl(30, 17.5%, 84%);--fg0: #000;--fg1: hsl(30, 10%, 20%);--fg2: hsl(30, 10%, 30%);--fg-inv: #fff;--menu-bg: hsl(30, 25%, 20%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 10%, 20%);--panel-close-hover: hsl(30, 75%, 38%);--accent: hsl(30, 100%, 30%);--accent-dark: hsl(30, 100%, 26%);--accent-light: hsl(30, 75%, 38%);--accent-light2: hsl(30, 75%, 50%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .75);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .35);--bg-input-hover: rgba(255, 255, 255, .65);--bg-input-focus: rgb(255, 255, 255);--bg-button: rgb(255, 255, 255);--bg-button-hover: hsl(30, 50%, 95%);--button-svg: hsl(30, 10%, 20%);--button-svg-hover: hsl(30, 75%, 50%);--button-svg-focus: hsl(30, 75%, 50%);--button-svg-active: hsl(30, 75%, 50%);--fg-button-focus: hsl(30, 100%, 30%);--fg-button-active: hsl(30, 100%, 30%);--posts-count: hsl(30, 75%, 38%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 38%);--stripe1: hsl(30, 100%, 30%);--stripe2: hsl(30, 100%, 26%);--stripe-fg: #fff;--private-post-bg: hsl(30, 50%, 96%);--private-post-border: hsl(30, 75%, 50%)}html.light .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 37.5%, 11%);--actor-bg0-ok: oklch(21% 2% var(--actor-hue));--actor-bg1: hsl(30, 32.5%, 15%);--actor-bg1-ok: oklch(24% 2% var(--actor-hue));--actor-bg2: hsl(30, 25%, 20%);--actor-bg2-ok: oklch(27% 2% var(--actor-hue));--actor-bg3: hsl(30, 25%, 25%);--actor-bg3-ok: oklch(30% 2% var(--actor-hue));--actor-bg4: hsl(30, 22.5%, 40%);--actor-bg4-ok: oklch(40% 10% var(--actor-hue));--actor-fg0: #fff;--actor-fg1: hsl(30, 40%, 80%);--actor-fg1-ok: oklch(92% 8% var(--actor-hue));--actor-tabs-bg: hsl(30, 12%, 17%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html.light .actors-tabs button,html.light .toot-content{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html.dark{color-scheme:dark;--bg0: hsl(30, 6%, 20%);--bg1: hsl(30, 8%, 13%);--bg2: hsl(30, 10%, 10%);--bg3: hsl(30, 12%, 8%);--bg4: hsl(30, 12%, 8%);--fg0: hsl(30, 5%, 95%);--fg1: hsl(30, 10%, 80%);--fg2: hsl(30, 15%, 50%);--fg-inv: #000;--menu-bg: hsl(30, 25%, 5%);--menu-fg: hsl(30, 40%, 80%);--menu-fg-active: #fff;--menu-icon: #fff;--menu-filter-active: hsl(30, 75%, 50%);--panel-close: hsl(30, 5%, 90%);--panel-close-hover: hsl(30, 0%, 100%);--accent: hsl(30, 70%, 65%);--accent-dark: hsl(30, 55%, 50%);--accent-light: hsl(30, 75%, 60%);--accent-light2: hsl(30, 75%, 68%);--accent-light3: hsl(30, 75%, 75%);--overlay-icon: #fff;--overlay-icon-hover: #fff;--overlay-backdrop: rgba(0, 0, 0, .9);--menu-backdrop: rgba(0, 0, 0, .5);--bg-input: rgba(255, 255, 255, .1);--bg-input-hover: rgba(255, 255, 255, .15);--bg-input-focus: rgba(255, 255, 255, .25);--bg-button: hsl(30, 25%, 20%);--bg-button-hover: hsl(30, 25%, 30%);--button-svg: hsl(30, 10%, 80%);--button-svg-hover: hsl(30, 65%, 65%);--button-svg-focus: hsl(30, 65%, 65%);--button-svg-active: hsl(30, 65%, 65%);--fg-button-focus: hsl(30, 70%, 65%);--fg-button-active: hsl(30, 70%, 65%);--posts-count: hsl(30, 5%, 95%);--selection-text: #fff;--selection-bg: hsl(30, 75%, 40%);--stripe1: hsl(30, 75%, 25%);--stripe2: hsl(30, 75%, 30%);--stripe-fg: hsl(30, 5%, 95%);--private-post-bg: hsl(30, 10%, 10%);--private-post-border: hsl(30, 55%, 50%)}html.dark .actors-wrapper{--actor-hue: 0;--actor-bg0: hsl(30, 32%, 2%);--actor-bg0-ok: oklch(11% 1% var(--actor-hue));--actor-bg1: hsl(30, 30%, 4%);--actor-bg1-ok: oklch(14% 1% var(--actor-hue));--actor-bg2: hsl(30, 27%, 6%);--actor-bg2-ok: oklch(17% 1% var(--actor-hue));--actor-bg3: hsl(30, 25%, 8%);--actor-bg3-ok: oklch(20% 1% var(--actor-hue));--actor-bg4: hsl(30, 20%, 15%);--actor-bg4-ok: oklch(30% 6% var(--actor-hue));--actor-fg0: hsl(30, 5%, 95%);--actor-fg1: hsl(30, 65%, 65%);--actor-fg1-ok: oklch(85% 25% var(--actor-hue));--actor-tabs-bg: hsl(30, 20%, 8%);--actor-accent: hsl(30, 75%, 50%);--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}html.dark .actors-tabs button,html.dark .toot-content{--actor-accent-ok: oklch(80% 25% var(--actor-hue));--actor-accent2-ok: oklch(60% 20% var(--actor-hue))}body{margin:0;padding:0;color:var(--fg1);font-size:16px;background:var(--bg2);font-family:sans-serif}h1,h2,h3,h4,h5,h6{color:var(--fg2)}img{display:block;height:auto;max-width:100%}a,.main-welcome p label{color:var(--accent)}button{position:relative;top:0;padding:.3em .5em;line-height:24px;font-family:inherit;vertical-align:middle;background:var(--bg-button);border:none;border-radius:5px;box-shadow:0 .15em #00000059}@media (forced-colors: active){button{border:1px solid ButtonText}}button:not(:has(svg)){padding:.7em 1em}button .btn-icon,button .btn-label{display:inline-block;line-height:24px;vertical-align:top}button .btn-label{padding:0 .5em}button .btn-icon{width:24px;height:24px;fill:var(--button-svg)}@media (forced-colors: active){button .btn-icon{fill:ButtonText}}button:hover{background:var(--bg-button-hover)}button:hover .btn-icon{fill:var(--button-svg-hover)}@media (forced-colors: active){button:hover .btn-icon{fill:ButtonText}}button:focus{color:var(--fg-button-focus)}button:focus .btn-icon{fill:var(--button-svg-focus)}@media (forced-colors: active){button:focus .btn-icon{fill:ButtonText}}button:active{top:.15em;color:var(--fg-button-active);box-shadow:0 0 #0000}button:active .btn-icon{fill:var(--button-svg-active)}@media (forced-colors: active){button:active .btn-icon{fill:ButtonText}}button:disabled{top:0;box-shadow:none;color:inherit;opacity:.35;cursor:not-allowed}button:disabled .btn-icon{fill:var(--button-svg)}@media (forced-colors: active){button:disabled .btn-icon{fill:GrayText}}input{padding:.3em .5em;color:inherit;border:none;background-color:var(--bg-input)}@media (forced-colors: active){input{border:1px solid ButtonText}}input:hover{background-color:var(--bg-input-hover)}input:focus,input:focus-visible{background-color:var(--bg-input-focus)}input[type=number]{width:6ch;text-align:center;-moz-appearance:textfield;appearance:textfield}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}details summary{cursor:pointer;user-select:none;display:flex;gap:.75rem;align-items:center}details summary .summary-icon{min-width:1.3rem;text-align:center}details summary .summary-icon svg{height:1.3em;aspect-ratio:1}details pre,details textarea{white-space:pre-wrap;word-break:break-all;font-size:.8em;color:inherit}details textarea{display:block;width:100%;resize:vertical;border:none;background:transparent;min-height:20em;min-height:15.5lh}@supports (field-sizing: content){details textarea{min-height:0;field-sizing:content}}::selection{color:var(--selection-text);background-color:var(--selection-bg)}*:focus-visible,.tags-group button:focus-visible div,.toots-filter:has([type=checkbox]:focus-visible){text-decoration:none;outline:2px solid var(--accent-light);outline-offset:2px;border-radius:2px;position:relative;z-index:1}.visually-hidden:not(:focus):not(:active){width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}.nojs{margin:5dvh 10dvw;padding:2em;color:#fff;text-align:center;font-weight:700;border-radius:.5rem;background-color:#600}@media (forced-colors: active){details summary{color:ButtonText!important}details summary svg{fill:ButtonText!important}.tags-group button *{color:ButtonText!important}}html{width:100dvw;height:100dvh;overflow:hidden}.main-section-inner{position:relative;z-index:1;height:100dvh;max-width:100dvw;display:grid;grid-template-areas:"actor filters header tags" "actor filters toots tags";grid-template-columns:min(25%,600px) min(20%,400px) 1fr min(20%,400px);grid-template-rows:min-content 1fr}.actor{grid-area:actor}.toots-header{grid-area:header}.toots-filters{grid-area:filters}.toots{grid-area:toots}.toots-tags{grid-area:tags}.mobile-menu{display:none}.toots-header{white-space:nowrap;overflow:hidden;background-color:var(--bg3)}.toots-header h2{padding:1rem 1rem 0}.toots-header h2 .order{font-weight:400}.toots-header .nb{color:var(--posts-count)}.toots-header .toggle-order{margin-bottom:5px}.toots-header .load-new{float:right;margin-left:5px}.toots-header .paging-options-toggle{display:none}.toots{background:var(--bg3);overflow:auto;container-type:inline-size}@media (prefers-reduced-motion: no-preference){.toots{scroll-behavior:smooth}}.paging{display:flex;justify-content:space-between;align-items:center;gap:.5rem;padding:1rem}.paging .direction-back,.paging .direction-fwd{display:flex;flex-wrap:wrap;gap:.25rem}.paging .direction-back{justify-content:flex-start}.paging .direction-fwd{justify-content:flex-end}.toots .paging{padding-bottom:2rem}@media (width < 400px){.toots .paging .direction-back .btn-label,.toots .paging .direction-fwd .btn-label,.toots .paging .paging-options-toggle .btn-label{width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}}.toots-header{container-type:inline-size}.toots-header input{font-size:inherit}.toots-header .paging{clear:both;flex-wrap:wrap}.toots-header .paging .paging-options,.toots-header .paging .paging-options-toggle{order:2;user-select:none}.toots-header .paging .direction-back{order:1}.toots-header .paging .direction-fwd{order:3}.toots-header .paging-options-reverse{display:none}@container (width < 675px){.toots-header .toggle-order{display:none}.toots-header .paging-options-toggle{display:block}.toots-header .paging-options-reverse{display:block;padding:0 1rem 1rem}.toots-header .paging .paging-options{font-size:.85em;display:none;order:4;flex-basis:calc(100% + 2rem);position:relative;margin:.5rem -1rem -1rem;text-align:center;background:var(--bg4);box-shadow:0 .75rem .75rem -1rem #000 inset}.toots-header .paging .paging-options:before{content:"";position:absolute;left:calc(50% - .5rem);top:0;z-index:2;width:0;height:0;border:.5rem solid transparent;border-top:.4rem solid var(--bg3)}@media (forced-colors: active){.toots-header .paging .paging-options:before{content:none;display:none}}.toots-header .paging .paging-options.open{display:block}.toots-header .paging-options-inner{display:inline-block;padding:1rem}}@container (width < 550px){.toots-header .paging .direction-back .btn-label,.toots-header .paging .direction-fwd .btn-label,.toots-header .paging .paging-options-toggle .btn-label{width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);position:absolute;white-space:nowrap}}@keyframes movingStripes{0%{background-position:0 0}to{background-position:-2.8284271247rem 0}}.welcome{max-width:80ch;height:100dvh;margin:0 auto;padding:10dvh 2rem;display:flex;flex-direction:column;overflow:auto;line-height:1.4;position:relative;z-index:1}.intro h1{margin-bottom:7dvh;text-align:center;line-height:1.1}.intro h1 .accronym{display:block;font-size:.8em;opacity:.7}.intro p{margin:1em 0}.intro p label{color:var(--accent);text-decoration:underline;cursor:pointer}@media (forced-colors: active){.intro p label{color:ButtonText}}.file-loader{display:block;width:100%;height:10rem;max-height:70dvh;margin:7dvh 0;padding:1rem;border-radius:1rem;background:var(--bg0)}.file-loader:hover,.file-loader:focus{background:var(--bg0);outline:2px solid var(--accent-light);outline-offset:2px}.file-loader.loading{display:flex;justify-content:center;align-items:center;font-size:2em;background-color:var(--accent);color:var(--stripe-fg);background:repeating-linear-gradient(45deg,var(--stripe1),var(--stripe1) 1rem,var(--stripe2) 1rem,var(--stripe2) 2rem);background-size:200% auto}.file-loader.loading:hover,.file-loader.loading:focus{outline:none}@media (prefers-reduced-motion: no-preference){.file-loader.loading{animation:movingStripes 2s linear infinite}}.about{margin-top:auto;font-size:.85em;text-align:center}.main-page.highlight-drag:before{content:"";position:absolute;inset:0;z-index:-1;background:repeating-linear-gradient(45deg,var(--stripe1),var(--stripe1) 1rem,var(--stripe2) 1rem,var(--stripe2) 2rem);background-size:200% auto}@media (prefers-reduced-motion: no-preference){.main-page.highlight-drag:before{animation:movingStripes 2s linear infinite}}.main-page.highlight-drag:after{content:"";position:absolute;inset:1rem;z-index:0;background:var(--bg2)}.loading-more{justify-content:center}.loading-more .file-loader{margin:0}.actor{position:relative}.actor:before{content:"";position:absolute;right:0;top:0;bottom:0;z-index:1;width:2rem;box-shadow:-1.5rem 0 2rem -2rem #000 inset}.actor a{color:var(--actor-fg0)}.actor h1,.actor h2,.actor h3,.actor h4{color:inherit}.actor summary{padding:1rem}.actor summary svg{fill:var(--actor-fg1)}.actor summary:hover,.actor summary:focus{color:var(--actor-fg0)}.actor summary:hover svg,.actor summary:focus svg{fill:var(--actor-fg0)}.actor details:not(.comment) summary:focus-visible{outline-offset:-6px;border-radius:10px}.actors-wrapper{height:100%;display:flex;flex-direction:column;justify-content:flex-start}.actor-panel{flex-grow:1;height:100%;container-type:inline-size;display:flex;flex-direction:column;justify-content:flex-start;overflow-y:auto;overflow-x:hidden;color:var(--actor-fg1);background:var(--actor-bg3)}.actor-pretty{flex-grow:1;flex-shrink:0;padding:1rem 1rem 2rem}.actor-banner button,.actor-id button{padding:0;background:transparent;box-shadow:none;border-radius:0;cursor:pointer}.actor-banner button:active,.actor-id button:active{top:0}.actor-banner{margin:-1rem -1rem 0}.actor-banner button:focus-visible{outline-offset:-4px;border-radius:8px;overflow:visible}.actor-banner button:focus-visible img{border-radius:0}.actor-id{position:relative;z-index:1;display:grid;grid-template-areas:"img name" "img url";grid-template-columns:100px 1fr;grid-template-rows:min-content 1fr}.actor-id button{border-radius:.5rem}.actor-id>*{position:relative;top:-2rem!important}.actor-avatar{grid-area:img;width:100px;border-radius:.5rem;cursor:pointer}.actor-avatar img{width:100px;height:auto;border-radius:.5rem}.actor-avatar.no-avatar{cursor:default}.actor-name{grid-area:name;margin:2.4rem 0 0;padding-left:1rem;font-weight:400}.actor-url{grid-area:url;padding-left:1rem}.actor-summary{margin:2rem 0;padding:1rem;color:var(--actor-fg0);line-height:1.3;background:var(--actor-bg4);background:radial-gradient(circle at 200% 150%,var(--actor-bg3),var(--actor-bg4));border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d}.actor-infos{display:grid;grid-template-columns:minmax(0,max-content) minmax(50%,1fr);margin:2rem 0}.actor-infos dl{grid-column:span 2;display:grid;grid-template-columns:subgrid}.actor-infos dt{padding:.5rem 1rem;font-weight:400}.actor-infos dd{padding:.5rem 1rem;color:var(--actor-fg0);border-left:1px solid var(--actor-bg4)}@container (width < 340px){.actor-infos{display:block}.actor-infos dl{display:block;padding:.5rem 1rem;border-bottom:1px solid var(--actor-bg4)}.actor-infos dl:last-child{border-bottom:none}.actor-infos dt{padding:0;margin-bottom:.5rem}.actor-infos dd{padding:0;border-left:none}}.actor-posts-count{display:flex;flex-wrap:wrap;justify-content:space-around;margin:2rem 0 0;gap:1rem}.actor-posts-count .count{display:block;color:var(--actor-fg0);font-size:1.6em;font-weight:700}.actor-posts-count .total,.actor-posts-count .archive{flex:0 0 calc(50% - .5rem);padding:1rem;text-align:center;border-radius:.5rem;background-color:#00000040}@media (forced-colors: active){.actor-posts-count .total,.actor-posts-count .archive{border:1px dotted CanvasText}}.actor-posts-count .comment{flex:0 0 100%;font-size:.8em}.actor-posts-count .comment summary{padding:0;font-weight:700}.actor-posts-count .comment summary:hover,.actor-posts-count .comment summary:focus-visible{color:var(--actor-fg0)}.actor-posts-count .comment[open] summary{color:var(--actor-fg0)}.actor-posts-count .comment p{margin-top:.5rem;padding-left:2.05rem}.actor-raw{background:var(--actor-bg2);box-shadow:0 .75rem .75rem -1rem #000 inset}.actor-raw .details-content{padding:1rem 1rem 2rem;overflow:auto;overflow-wrap:break-word}.actor-raw textarea{color:var(--actor-fg0)}.actor-likes-bookmarks{background:var(--actor-bg0);overflow-wrap:anywhere}.actor-likes-bookmarks h2{display:inline;font-size:inherit;font-weight:400}.actor-likes-bookmarks ul{font-size:.9em;padding:0 1rem 2rem}.actor-likes-bookmarks li{margin:0 -.25rem;padding:.25rem 0;list-style:none}.actor-likes-bookmarks a{display:inline-block;padding:.25rem;line-height:1.1;text-decoration:none}.actor-likes-bookmarks a:hover{text-decoration:underline}.actor-likes-bookmarks a .url-instance{padding-right:1ch;color:var(--actor-fg1)}.actor-likes-bookmarks a .url-instance:after{padding-left:1ch;content:"»"}.actor-likes-bookmarks a .url-actor{padding-right:1ch;color:var(--actor-fg0);font-weight:700}.actor-likes-bookmarks a .url-actor:after{padding-left:1ch;content:"»"}.actor-likes-bookmarks a .url-post-id{color:var(--actor-fg1)}.actor-likes-bookmarks .no-content{padding:0 1rem 2rem;text-align:center;font-style:italic}.actor-likes{background:var(--actor-bg1);box-shadow:0 .75rem .75rem -1rem #000 inset}.actor-bookmarks{padding-bottom:1rem;box-shadow:0 .75rem .75rem -1rem #000 inset}.multiple-actors .actors-tabs{flex-grow:0;flex-shrink:0;display:flex;flex-wrap:wrap;gap:.75rem;padding:.75rem 1rem;background-color:var(--actor-tabs-bg);overflow:hidden;user-select:none}.multiple-actors .actors-tabs button{all:unset;padding:.35rem .7rem .4rem;font-size:.85em;font-family:inherit;border-radius:.25rem;color:#fff;background:var(--actor-accent2-ok, transparent);cursor:pointer}.multiple-actors .actors-tabs button span{display:inline-block;padding:0 0 .3rem;border-bottom:.1rem solid transparent}.multiple-actors .actors-tabs button:hover span{border-bottom-color:#ffffff80}.multiple-actors .actors-tabs button[aria-selected=true]{box-shadow:none}.multiple-actors .actors-tabs button[aria-selected=true] span{border-bottom-color:#fff}.multiple-actors .actors-tabs button:focus-visible{outline:2px solid hsl(var(--actor-hue),50%,80%);outline:2px solid var(--actor-accent-ok, var(--actor-accent));outline-offset:2px}@media (forced-colors: active){.multiple-actors .actors-tabs button{border:1px solid ButtonText}.multiple-actors .actors-tabs button span{border-bottom-color:Canvas}.multiple-actors .actors-tabs button[aria-selected=true] span{border-bottom-color:Highlight}}.multiple-actors .actor *:focus-visible{outline-color:var(--actor-accent-ok, var(--accent-light))}.multiple-actors .actor summary svg{fill:var(--actor-fg1-ok, var(--actor-fg1))}.multiple-actors .actor-panel{color:var(--actor-fg1-ok, var(--actor-fg1));background:var(--actor-bg3-ok, var(--actor-bg3))}.multiple-actors .actor-summary{background:var(--actor-bg4-ok, var(--actor-bg4));background:radial-gradient(circle at 200% 150%,var(--actor-bg3-ok, var(--actor-bg3)),var(--actor-bg4-ok, var(--actor-bg4)))}.multiple-actors .actor-infos dd{border-left-color:var(--actor-bg4-ok, var(--actor-bg4))}@container (width < 340px){.multiple-actors .actor-infos dl{border-bottom-color:var(--actor-bg4-ok, var(--actor-bg4))}}.multiple-actors .actor-raw{background:var(--actor-bg2-ok, var(--actor-bg2))}.multiple-actors .actor-likes-bookmarks{background:var(--actor-bg0-ok, var(--actor-bg0))}.multiple-actors .actor-likes{background:var(--actor-bg1-ok, var(--actor-bg1))}.multiple-actors .actor-likes-bookmarks a .url-instance,.multiple-actors .actor-likes-bookmarks a .url-post-id{color:var(--actor-fg1-ok, var(--actor-fg1))}.toots-filters{height:100dvh;max-height:100dvh;padding:1rem 1rem 2em;background-color:var(--bg4);overflow-y:auto;container-type:inline-size;user-select:none}.toots-filters h2{margin:0 0 2rem}.toots-filters-reset{margin-top:1rem;text-align:right}.toots-filters-reset .reset:not(:disabled){box-shadow:0 0 0 .25rem var(--accent)}.toots-filters-group{margin:0 0 1rem}.toots-filters-group-title{margin:0 0 .5rem}.toots-filters-group:has(.text){display:grid;grid-template-columns:min-content 1fr;gap:.35rem}.toots-filters-group:has(.text) .toots-filter{grid-column:span 2}.toots-filters-group:has(.text) .toots-filter.text{display:grid;grid-template-columns:subgrid}@container (width < 260px){.toots-filters-group:has(.text){display:block}.toots-filters-group:has(.text) .toots-filter.text label{margin:.5rem 0 .25rem}}.toots-filter{margin-bottom:2px}.toots-filter label{display:block;padding:.2rem .3rem;border-radius:.2rem}.toots-filter.sep-above{margin-top:1rem}.toots-filter label{font-size:.85em}.toots-filter.text input{display:block;width:100%}.toots-filter.checkbox label{position:relative;padding-left:1.7rem;background:transparent url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e") no-repeat 2px 2px/auto 18.4px scroll}.toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}.toots-filter.checkbox input{position:absolute;left:1px;top:1px;z-index:1;opacity:0}@media (forced-colors: active){.toots-filter.checkbox label{background-image:none!important}.toots-filter.checkbox input{left:3px;top:4px;opacity:1;outline:none!important}}.toots-filter:hover label,.toots-filter:focus-visible label,.toots-filter:focus-within label{background-color:#ffffff59}.toots-filter.active label{color:var(--fg-inv);opacity:1;background-color:var(--accent)}@media (forced-colors: active){.toots-filter.active label{outline:1px solid Highlight}}@media (prefers-color-scheme: light){.toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}.toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}.toots-filter:hover label,.toots-filter:focus-visible label,.toots-filter:focus-within label{background-color:#ffffff59}.toots-filter.active label{background-color:var(--accent)}}html.light .toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}html.light .toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3crect%20style='fill:%23ffffff;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}html.light .toots-filter:hover label,html.light .toots-filter:focus-visible label,html.light .toots-filter:focus-within label{background-color:#ffffff59}html.light .toots-filter.active label{background-color:var(--accent)}@media (prefers-color-scheme: dark){.toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}.toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}.toots-filter:hover label,.toots-filter:focus-visible label,.toots-filter:focus-within label{background-color:#ffffff1a}.toots-filter.active label{background-color:var(--accent-light)}}html.dark .toots-filter.checkbox label{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:0.5;stroke-width:40'%20width='570.43481'%20height='569.61633'%20x='194.78259'%20y='-763.58057'%20/%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Z'%20/%3e%3c/svg%3e")}html.dark .toots-filter.checkbox label:has(input:checked){background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3crect%20style='fill:%23000000;fill-opacity:1;stroke-width:40'%20width='567.97949'%20height='567.97955'%20x='196.41943'%20y='-764.39893'%20/%3e%3cpath%20d='m424-424-86-86q-11-11-28-11t-28%2011q-11%2011-11%2028t11%2028l114%20114q12%2012%2028%2012t28-12l226-226q11-11%2011-28t-11-28q-11-11-28-11t-28%2011L424-424ZM200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h560q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z'%20/%3e%3c/svg%3e")}html.dark .toots-filter:hover label,html.dark .toots-filter:focus-visible label,html.dark .toots-filter:focus-within label{background-color:#ffffff1a}html.dark .toots-filter.active label{background-color:var(--accent-light)}.toot{background:var(--bg1)}@container (width >= 100ch){.toot{display:grid;grid-template-areas:"header meta" "pretty meta" "infos meta" "raw meta";grid-template-columns:50% 50%;grid-template-rows:min-content min-content 1fr min-content}}.toot:last-child{border-bottom:none}.toot h3,.toot h4{margin:0}.toot-pretty{grid-area:pretty;padding:1rem}.toot-pretty .fetch-boost-data{padding-top:1rem;text-align:right}.toot-summary{padding:.5rem 1rem;margin-bottom:1rem;color:var(--stripe-fg);background:repeating-linear-gradient(45deg,var(--stripe1),var(--stripe1) 10px,var(--stripe2) 10px,var(--stripe2) 20px);box-shadow:0 .1rem .2rem -.2rem #00000080 inset;border-radius:.5rem}@media (forced-colors: active){.toot-summary{outline:1px solid Highlight}}.toot-content{margin:0;padding:1rem;color:var(--fg0);line-height:1.3;overflow-wrap:break-word;background-color:var(--bg0);border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d}@media (forced-colors: active){.toot-content{border:1px solid CanvasText}}.toot-type-boost .toot-content{position:relative;overflow-wrap:break-word;background:radial-gradient(circle at 100% 100%,var(--bg2),var(--bg0))}.toot-type-boost .toot-content:after{content:"";position:absolute;inset:0;z-index:1;background:transparent url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e") no-repeat calc(100% - 1rem) calc(100% - .5rem) /auto 50% scroll}.toot-type-boost .toot-content a,.toot-type-boost .toot-content .toot-content-inner{position:relative;z-index:2}.toot-visibility-mentioned .toot-content{background-color:var(--private-post-bg);border:2px dashed var(--private-post-border)}@media (forced-colors: none){.multiple-actors .toot-content{position:relative;padding-left:1.6rem}.multiple-actors .toot-content:before{content:"";position:absolute;left:.35rem;top:.35rem;bottom:.35rem;z-index:1;width:.25rem;border-radius:.25rem;background-color:red;background-color:var(--actor-accent-ok, transparent)}}.toot-attachments{margin:1rem 0 0}.toot-attachments ul,.toot-attachments li{list-style:none}.toot-attachments .att-description{font-size:.85em;line-height:1.4}.toot-attachments .no-alt-text .att-description{font-style:italic}.toot-attachments .desc-source{display:block;margin-top:.5rem;font-style:normal;word-break:break-all;line-height:1.2}.toot-attachments .desc-source strong{color:var(--accent);font-weight:400}.att-img+.att-img{margin-top:1rem}.att-img .att-wrapper{display:flex;align-items:flex-start}.att-img .att-img-wrapper{width:clamp(75px,7.5dvw,150px);flex:0 0 clamp(75px,7.5dvw,150px);padding:0;background:transparent;border-radius:.5rem;box-shadow:none;cursor:pointer;background:#fff}@media (prefers-reduced-motion: no-preference){.att-img .att-img-wrapper{transition:background-color .35s ease-out}}.att-img .att-img-wrapper:hover,.att-img .att-img-wrapper:focus{box-shadow:none;border-radius:.5rem}.att-img .att-img-wrapper:hover{transition-duration:.2s;background:var(--accent-light3)}.att-img .att-img-wrapper:hover img{filter:grayscale(50%)}@media (forced-colors: active){.att-img .att-img-wrapper:hover img{filter:none}}.att-img img{display:block;width:100%;aspect-ratio:1;object-fit:cover;font-size:.6em;border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d;mix-blend-mode:multiply}@media (prefers-reduced-motion: no-preference){.att-img img{transition:filter .2s ease-out}}@media (forced-colors: active){.att-img img{mix-blend-mode:normal}}.att-img .att-description{padding:0 1rem}.att-img.no-alt-text .att-img-wrapper{position:relative}.att-img.no-alt-text .att-img-wrapper:before{content:"";position:absolute;right:-.5rem;bottom:-.25rem;z-index:2;width:1.5rem;height:1.5rem;border-radius:10rem;background:#c00 url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-120q-33%200-56.5-23.5T400-200q0-33%2023.5-56.5T480-280q33%200%2056.5%2023.5T560-200q0%2033-23.5%2056.5T480-120Zm0-240q-33%200-56.5-23.5T400-440v-320q0-33%2023.5-56.5T480-840q33%200%2056.5%2023.5T560-760v320q0%2033-23.5%2056.5T480-360Z'/%3e%3c/svg%3e") no-repeat 50% 50%/contain scroll}.att-sound{padding:1rem;background-color:var(--bg2);border-radius:.5rem;box-shadow:0 .1rem .2rem -.2rem #00000080 inset}.att-sound+.att-sound{margin-top:1rem}.att-sound audio{display:block;width:100%;margin:0 0 1rem;border-radius:.3rem}.att-video+.att-video{margin-top:1rem}.att-video video{display:block;width:auto;height:auto;max-width:100%;max-height:75dvh;border-radius:.5rem;box-shadow:0 .3rem .4rem -.2rem #0000004d}.att-video .att-description{padding:1rem}.toot-meta{grid-area:meta;display:flex;align-items:flex-start;gap:1rem;padding:1rem;padding-left:0}@container (width < 100ch){.toot-meta{display:none}}.toot-meta dl{margin:0;display:grid;grid-template-columns:min-content 1fr}.toot-meta dt{white-space:nowrap}.toot-meta>div{flex-basis:calc((100% - 2rem) / 3);max-width:calc((100% - 2rem)/2);flex-grow:1;flex-shrink:0;padding:1rem;background-color:var(--bg2);border-radius:.5rem;box-shadow:0 .1rem .2rem -.2rem #00000080 inset}@media (forced-colors: active){.toot-meta>div{border:1px solid CanvasText}}.toot-meta .toot-links{flex-grow:1}.toot-meta .toot-links a{word-break:break-all}.toot-meta h4{margin-bottom:.5rem}.toot-meta li{list-style:none;padding:.35rem 0 0}.toot-meta li a{display:block;padding-left:1.5rem;background-color:transparent;background-repeat:no-repeat;background-position:0 0;background-size:auto 1.2rem;word-break:break-all}@media (forced-colors: active){.toot-meta li a{padding-left:0}}.toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}.toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}.toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}.toot-infos{grid-area:infos;margin:0 1rem;padding:1rem;font-size:.8em;display:flex;flex-wrap:wrap;align-items:flex-start;container-type:inline-size;background:var(--bg2);border-radius:.5rem;box-shadow:0 .1rem .2rem -.2rem #00000080 inset}.toot-infos>span{margin-bottom:.25rem}.toot-infos .type,.toot-infos .visibility,.toot-infos .published,.toot-infos .link a{padding-left:1.6em;background:transparent none no-repeat 0 0/1rem auto scroll}@media (forced-colors: active){.toot-infos .type,.toot-infos .visibility,.toot-infos .published,.toot-infos .link a{padding-left:0}}.toot-infos .published{padding-left:0}.toot-infos .published:before{content:"-";margin:0 1ch}.toot-infos .published,.toot-infos .visibility{margin-right:1rem}.toot-infos .visibility{margin-bottom:0}.toot-infos .link{margin-left:auto}.toot-infos .type{order:1}.toot-infos .author{order:2}.toot-infos .published{order:4}.toot-infos .visibility{order:5}.toot-infos .link{order:3}.toot-infos .published{flex:0 0 100%;margin-right:0;padding-left:1.6em;background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}@media (forced-colors: active){.toot-infos .published{padding-left:0}}.toot-infos .published:before{content:none}.toot-infos .published .updated{display:block}.toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}.toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}.toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}.toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}.toot-infos .link a{padding-left:0;padding-right:1.6em;background-position:100% 50%;background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}@media (forced-colors: active){.toot-infos .link a{padding-right:0;background:none!important}}.toot-raw{grid-area:raw;margin-top:.5rem;padding:0 1rem 2rem}.toot-raw details{border-radius:.5rem}.toot-raw summary{justify-content:flex-end;gap:.3rem;padding:.5rem 1rem;font-size:.8em;text-align:right;opacity:.8;user-select:none}.toot-raw summary:hover,.toot-raw summary:focus-visible{opacity:1;border-radius:.5rem;background-color:var(--bg2)}.toot-raw .summary-icon{display:flex}.toot-raw .summary-icon svg{fill:currentColor}.toot-raw details[open]{background-color:var(--bg2);box-shadow:0 .1rem .2rem -.2rem #00000080 inset}@media (forced-colors: active){.toot-raw details[open]{border:1px solid CanvasText}}.toot-raw details[open] summary{opacity:1;background-color:transparent!important;box-shadow:none}.toot-raw .details-content{padding:1rem;overflow-wrap:break-word}.toots-no-results{padding:4rem 1rem;text-align:center;background:var(--bg1)}.toots-no-results p{margin:1em 0}@media (prefers-color-scheme: light){.toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}.toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}.toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}.toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}.toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}.toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}.toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}.toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}.toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}}html.light .toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}html.light .toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}html.light .toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}html.light .toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}html.light .toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}html.light .toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}html.light .toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}html.light .toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.light .toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.light .toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}html.light .toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23000000'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}@media (prefers-color-scheme: dark){.toot-type-boost .toot-content:after{opacity:.25}.toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}.toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}.toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}.toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}.toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}.toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}.toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}.toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}.toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}.toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}}html.dark .toot-type-boost .toot-content:after{opacity:.25}html.dark .toot-meta .toot-people li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-480q-66%200-113-47t-47-113q0-66%2047-113t113-47q66%200%20113%2047t47%20113q0%2066-47%20113t-113%2047ZM160-240v-32q0-34%2017.5-62.5T224-378q62-31%20126-46.5T480-440q66%200%20130%2015.5T736-378q29%2015%2046.5%2043.5T800-272v32q0%2033-23.5%2056.5T720-160H240q-33%200-56.5-23.5T160-240Zm80%200h480v-32q0-11-5.5-20T700-306q-54-27-109-40.5T480-360q-56%200-111%2013.5T260-306q-9%205-14.5%2014t-5.5%2020v32Zm240-320q33%200%2056.5-23.5T560-640q0-33-23.5-56.5T480-720q-33%200-56.5%2023.5T400-640q0%2033%2023.5%2056.5T480-560Zm0-80Zm0%20400Z'/%3e%3c/svg%3e")}html.dark .toot-meta .toot-hashtags li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='m360-320-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H171q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l40-160H231q-20%200-32-15.5t-7-34.5q3-14%2014-22t25-8h129l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h160l33-131q3-13%2013-21t24-8q19%200%2031%2015t7%2033l-28%20112h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H660l-40%20160h109q20%200%2032%2015.5t7%2034.5q-3%2014-14%2022t-25%208H600l-33%20131q-3%2013-13%2021t-24%208q-19%200-31-15t-7-33l28-112H360Zm20-80h160l40-160H420l-40%20160Z'/%3e%3c/svg%3e")}html.dark .toot-meta .toot-links li a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M280-280q-83%200-141.5-58.5T80-480q0-83%2058.5-141.5T280-680h120q17%200%2028.5%2011.5T440-640q0%2017-11.5%2028.5T400-600H280q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035h120q17%200%2028.5%2011.5T440-320q0%2017-11.5%2028.5T400-280H280Zm80-160q-17%200-28.5-11.5T320-480q0-17%2011.5-28.5T360-520h240q17%200%2028.5%2011.5T640-480q0%2017-11.5%2028.5T600-440H360Zm200%20160q-17%200-28.5-11.5T520-320q0-17%2011.5-28.5T560-360h120q50%200%2085-35t35-85q0-50-35-85t-85-35H560q-17%200-28.5-11.5T520-640q0-17%2011.5-28.5T560-680h120q83%200%20141.5%2058.5T880-480q0%2083-58.5%20141.5T680-280H560Z'/%3e%3c/svg%3e")}html.dark .toot-infos .published{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-80q-33%200-56.5-23.5T120-160v-560q0-33%2023.5-56.5T200-800h40v-40q0-17%2011.5-28.5T280-880q17%200%2028.5%2011.5T320-840v40h320v-40q0-17%2011.5-28.5T680-880q17%200%2028.5%2011.5T720-840v40h40q33%200%2056.5%2023.5T840-720v560q0%2033-23.5%2056.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0%200v-80%2080Zm280%20240q-17%200-28.5-11.5T440-440q0-17%2011.5-28.5T480-480q17%200%2028.5%2011.5T520-440q0%2017-11.5%2028.5T480-400Zm-160%200q-17%200-28.5-11.5T280-440q0-17%2011.5-28.5T320-480q17%200%2028.5%2011.5T360-440q0%2017-11.5%2028.5T320-400Zm320%200q-17%200-28.5-11.5T600-440q0-17%2011.5-28.5T640-480q17%200%2028.5%2011.5T680-440q0%2017-11.5%2028.5T640-400ZM480-240q-17%200-28.5-11.5T440-280q0-17%2011.5-28.5T480-320q17%200%2028.5%2011.5T520-280q0%2017-11.5%2028.5T480-240Zm-160%200q-17%200-28.5-11.5T280-280q0-17%2011.5-28.5T320-320q17%200%2028.5%2011.5T360-280q0%2017-11.5%2028.5T320-240Zm320%200q-17%200-28.5-11.5T600-280q0-17%2011.5-28.5T640-320q17%200%2028.5%2011.5T680-280q0%2017-11.5%2028.5T640-240Z'/%3e%3c/svg%3e")}html.dark .toot-type-post .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M560-120v-66q0-8%203-15.5t9-13.5l209-208q9-9%2020-13t22-4q12%200%2023%204.5t20%2013.5l37%2037q8%209%2012.5%2020t4.5%2022q0%2011-4%2022.5T903-300L695-92q-6%206-13.5%209T666-80h-66q-17%200-28.5-11.5T560-120Zm300-223-37-37%2037%2037ZM620-140h38l121-122-37-37-122%20121v38ZM240-80q-33%200-56.5-23.5T160-160v-640q0-33%2023.5-56.5T240-880h287q16%200%2030.5%206t25.5%2017l194%20194q11%2011%2017%2025.5t6%2030.5v57q0%2017-11.5%2028.5T760-510q-17%200-28.5-11.5T720-550v-50H560q-17%200-28.5-11.5T520-640v-160H240v640h200q17%200%2028.5%2011.5T480-120q0%2017-11.5%2028.5T440-80H240Zm0-80v-640%20640Zm521-121-19-18%2037%2037-18-19Z'/%3e%3c/svg%3e")}html.dark .toot-type-boost .toot-infos .type{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M284-506q14-28%2029-54t33-52l-56-11-84%2084%2078%2033Zm482-275q-70%202-149.5%2041T472-636q-42%2042-75%2090t-49%2090l114%20113q42-16%2090-49t90-75q65-65%20104-144t41-149q0-4-1.5-8t-4.5-7q-3-3-7-4.5t-8-1.5ZM546-541q-23-23-23-56.5t23-56.5q23-23%2057-23t57%2023q23%2023%2023%2056.5T660-541q-23%2023-57%2023t-57-23Zm-34%20262%2033%2079%2084-84-11-56q-26%2018-52%2032.5T512-279Zm351-534q8%20110-36%20214.5T688-399l20%2099q4%2020-2%2039t-20%2033L560-102q-15%2015-36%2011.5T495-114l-61-143-171-171-143-61q-20-8-24-29t11-36l126-126q14-14%2033.5-20t39.5-2l99%2020q95-95%20199.5-139T819-857q8%201%2016%204.5t14%209.5q6%206%209.5%2014t4.5%2016ZM157-321q35-35%2085.5-35.5T328-322q35%2035%2034.5%2085.5T327-151q-48%2048-113.5%2057T82-76q9-66%2018-131.5T157-321Zm57%2056q-17%2017-23.5%2041T180-175q25-4%2049-10t41-23q12-12%2013-29t-11-29q-12-12-29-11.5T214-265Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-public .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm-40-82v-78q-33%200-56.5-23.5T360-320v-40L168-552q-3%2018-5.5%2036t-2.5%2036q0%20121%2079.5%20212T440-162Zm276-102q20-22%2036-47.5t26.5-53q10.5-27.5%2016-56.5t5.5-59q0-98-54.5-179T600-776v16q0%2033-23.5%2056.5T520-680h-80v80q0%2017-11.5%2028.5T400-560h-80v80h240q17%200%2028.5%2011.5T600-440v120h40q26%200%2047%2015.5t29%2040.5Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-unlisted .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h360v-80q0-50-35-85t-85-35q-42%200-73.5%2025.5T364-751q-4%2014-16.5%2022.5T320-720q-17%200-28.5-11t-8.5-26q14-69%2069-116t128-47q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-followers .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M240-80q-33%200-56.5-23.5T160-160v-400q0-33%2023.5-56.5T240-640h40v-80q0-83%2058.5-141.5T480-920q83%200%20141.5%2058.5T680-720v80h40q33%200%2056.5%2023.5T800-560v400q0%2033-23.5%2056.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33%200%2056.5-23.5T560-360q0-33-23.5-56.5T480-440q-33%200-56.5%2023.5T400-360q0%2033%2023.5%2056.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50%200-85%2035t-35%2085v80ZM240-160v-400%20400Z'/%3e%3c/svg%3e")}html.dark .toot-visibility-mentioned .toot-infos .visibility{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M480-80q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480v58q0%2059-40.5%20100.5T740-280q-35%200-66-15t-52-43q-29%2029-65.5%2043.5T480-280q-83%200-141.5-58.5T280-480q0-83%2058.5-141.5T480-680q83%200%20141.5%2058.5T680-480v58q0%2026%2017%2044t43%2018q26%200%2043-18t17-44v-58q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093h160q17%200%2028.5%2011.5T680-120q0%2017-11.5%2028.5T640-80H480Zm0-280q50%200%2085-35t35-85q0-50-35-85t-85-35q-50%200-85%2035t-35%2085q0%2050%2035%2085t85%2035Z'/%3e%3c/svg%3e")}html.dark .toot-infos .link a{background-image:url("data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20height='24px'%20viewBox='0%20-960%20960%20960'%20width='24px'%20fill='%23ffffff'%3e%3cpath%20d='M200-120q-33%200-56.5-23.5T120-200v-560q0-33%2023.5-56.5T200-840h240q17%200%2028.5%2011.5T480-800q0%2017-11.5%2028.5T440-760H200v560h560v-240q0-17%2011.5-28.5T800-480q17%200%2028.5%2011.5T840-440v240q0%2033-23.5%2056.5T760-120H200Zm560-584L416-360q-11%2011-28%2011t-28-11q-11-11-11-28t11-28l344-344H600q-17%200-28.5-11.5T560-800q0-17%2011.5-28.5T600-840h200q17%200%2028.5%2011.5T840-800v200q0%2017-11.5%2028.5T800-560q-17%200-28.5-11.5T760-600v-104Z'/%3e%3c/svg%3e")}.toots-tags{display:grid;grid-template-rows:min-content repeat(3,1fr);height:100vh;padding-bottom:2rem;overflow:hidden;background-color:var(--bg4);container-type:inline-size}.tags-title{padding:1rem}.tags-group{position:relative;height:100%;overflow:hidden;display:grid;padding:1rem 1rem 0;grid-template-rows:min-content 1fr}.tags-group ul{font-size:.85em;list-style:none}.tags-group li{list-style:none;margin:.3rem 4px}.tags-group button{all:unset;display:block;width:100%}.tags-group button div{display:flex;align-items:center;width:100%;gap:.25rem;padding:.1rem .3rem;border-radius:.2rem;border:none;background:transparent;font-family:inherit;font-size:inherit;cursor:pointer}.tags-group button div .count{color:var(--accent-dark);font-size:.85em;font-weight:700}@container (width >= 340px){.tags-group button div .count{display:inline-block;min-width:5ch;text-align:right}}.tags-group button div .name{flex-grow:1;word-break:break-all;display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center}.tags-group button div .domain{margin-left:auto;padding-left:.5em;font-size:.85em;font-weight:700;letter-spacing:.02em;opacity:.75}.tags-group button:hover .name{text-decoration:underline}.tags-group .active button div{color:var(--fg-inv);background-color:var(--accent)}.tags-group .active button div .count{color:var(--fg-inv)}.tags-group-header{white-space:nowrap;overflow:hidden}@container (width >= 340px){.tags-group-header{display:flex;justify-content:space-between;gap:.25rem 1rem}}.tags-group-header h3 .count{color:var(--accent-dark);font-weight:400}.tags-group-filter{flex-grow:1}.tags-group-filter input{display:block;width:100%;margin-top:.25rem}@container (width >= 340px){.tags-group-filter input{margin-top:0}}.tags-group-scroll{overflow:auto;padding-top:.5rem;padding-bottom:2rem}@media (prefers-color-scheme: light){.tags-group .active button div{background-color:var(--accent)}}html.light .tags-group .active button div{background-color:var(--accent)}@media (prefers-color-scheme: dark){.tags-group .active button div{background-color:var(--accent-light)}}html.dark .tags-group .active button div{background-color:var(--accent-light)}.mobile-menu{display:none;position:relative;z-index:2;color:var(--menu-fg);background-color:var(--menu-bg)}@media (forced-colors: active){.mobile-menu{color:buttonText}}.mobile-menu ul{list-style:none;height:100dvh;padding:1rem 0;display:flex;flex-direction:column;justify-content:flex-start}.mobile-menu li{display:block;list-style:none}.mobile-menu button{display:flex;flex-direction:column;justify-content:space-between;align-items:center;position:relative;height:75px;aspect-ratio:1;padding:0;color:inherit;font-size:.75em;text-align:center;font-weight:700;line-height:1;letter-spacing:.05em;text-transform:uppercase;background:none;box-shadow:none;border-radius:0}.mobile-menu button:focus-visible{outline-offset:-4px}.mobile-menu button svg{display:block;width:35px;height:auto;margin:5px auto 0;fill:var(--menu-icon)}@media (forced-colors: active){.mobile-menu button svg{fill:buttonText}}.mobile-menu button span{display:block;padding-bottom:10px}.mobile-menu button .visually-hidden{text-transform:initial}.mobile-menu button.menu-filters.filters-active:before{content:"";position:absolute;right:10px;top:5px;z-index:1;width:10px;height:10px;border-radius:10px;background-color:var(--menu-filter-active)}@media (forced-colors: active){.mobile-menu button.menu-filters.filters-active:before{background-color:Highlight}}.mobile-menu button.menu-new{margin-top:auto}.mobile-menu button:hover,.mobile-menu button:focus-visible{background-color:#ffffff1a}@media (forced-colors: active){.mobile-menu button:hover,.mobile-menu button:focus-visible{color:Canvas;background:Highlight}.mobile-menu button:hover span,.mobile-menu button:focus-visible span{color:CanvasText}.mobile-menu button:hover svg,.mobile-menu button:focus-visible svg{fill:Canvas}}.panel-backdrop{display:none;position:fixed;inset:0;z-index:3;background-color:var(--menu-backdrop)}@media screen and (width < 1200px){.toots-header{z-index:2;box-shadow:0 -10px 10px 10px #00000080}.toots-header .load-new{display:none}.toots{z-index:1}.main-section-inner{grid-template-areas:"menu header" "menu toots";grid-template-columns:75px 1fr;overflow:hidden}.toots .paging{padding-bottom:1rem}.mobile-menu-panel{position:fixed;left:-475px;top:0;bottom:0;z-index:3;width:min(400px,100dvw - 75px);box-shadow:0 0 #0000}.mobile-menu-panel.actor:before{content:none}}@media screen and (width < 1200px) and (prefers-reduced-motion: no-preference){.mobile-menu-panel{transition:left .2s ease-out;transition-property:left,box-shadow;scroll-behavior:smooth}}@media screen and (width < 1200px){.panel-close{position:absolute;right:6px;top:6px;z-index:1;color:var(--panel-close);background:transparent;box-shadow:none}.panel-close .btn-icon{fill:currentColor}.panel-close:hover,.panel-close:focus,.panel-close:focus-visible{color:var(--panel-close-hover)}}@media screen and (width < 1200px) and (forced-colors: active){.panel-close{color:buttonText}}@media screen and (width < 1200px){.actor .panel-close{color:#fff}.mobile-menu{display:block;grid-area:menu;z-index:5}.main-page.menu-open .panel-backdrop{display:block}.main-page.menu-open.menu-open-actor .actor,.main-page.menu-open.menu-open-filters .toots-filters,.main-page.menu-open.menu-open-tags .toots-tags{left:75px;box-shadow:-1rem 0 1rem 1rem #00000080}}@media screen and (width < 1200px) and (prefers-reduced-motion: no-preference){.main-page.menu-open.menu-open-actor .actor,.main-page.menu-open.menu-open-filters .toots-filters,.main-page.menu-open.menu-open-tags .toots-tags{transition-duration:.35s}}@media screen and (width < 1200px){.main-page.menu-open.menu-open-actor .menu-actor,.main-page.menu-open.menu-open-filters .menu-filters,.main-page.menu-open.menu-open-tags .menu-tags{color:var(--menu-fg-active);background-color:#fff3}}@media screen and (width < 500px){.main-section-inner{grid-template-areas:"header" "toots" "menu";grid-template-columns:1fr;grid-template-rows:min-content 1fr min-content}.mobile-menu{box-shadow:none}.mobile-menu ul{flex-direction:row;justify-content:space-around;height:auto;padding:0}.mobile-menu li{flex-grow:1}.mobile-menu button{aspect-ratio:unset;width:100%}.mobile-menu-panel{height:auto;bottom:75px;width:100dvw;left:-100dvw;box-shadow:none}.main-page.menu-open.menu-open-actor .actor,.main-page.menu-open.menu-open-filters .toots-filters,.main-page.menu-open.menu-open-tags .toots-tags{left:0}.toots-tags{padding-bottom:1rem}.tags-title{padding-bottom:0}}.overlay{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center}.overlay-content{position:relative;z-index:5}.overlay-content,.overlay-content img{max-width:calc(100dvw - 88px);max-height:calc(100dvh - 44px)}.overlay-content img{box-shadow:0 1rem 2.5rem -1rem #000}.overlay-ui{position:absolute;inset:0;z-index:1}.overlay-ui button{display:block;padding:0;position:absolute;z-index:2;cursor:pointer;border:none;background:transparent;box-shadow:none}.overlay-ui button svg{display:block;width:36px;height:36px;fill:var(--overlay-icon)}.overlay-ui button:hover svg,.overlay-ui button:focus svg,.overlay-ui button:active svg{fill:var(--overlay-icon-hover)}.overlay-ui .viewer-close{top:4px;right:4px}.overlay-ui .viewer-close svg{width:24px;height:24px}.overlay-ui .viewer-next{right:4px;top:calc(50% - 18px)}.overlay-ui .viewer-prev{left:4px;top:calc(50% - 18px)}.overlay-ui .backdrop{position:absolute;inset:0;z-index:1;background-color:var(--overlay-backdrop)} diff --git a/dist/index.html b/dist/index.html index f83d81a..1e4a624 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,22 +1,24 @@ - MARL - Mastodon Archive Reader Lite
    \ No newline at end of file diff --git a/dist/js/init.js b/dist/js/init.js new file mode 100644 index 0000000..8539d5a --- /dev/null +++ b/dist/js/init.js @@ -0,0 +1,30 @@ +drag.init("app"); + +document.addEventListener("alpine:init", () => { + // create and init stores + Alpine.store("files", filesStore); + Alpine.store("lightbox", lightboxStore); + Alpine.store("ui", uiStore); + Alpine.store("userPrefs", userPrefsStore); + + const salutations = [ + "Hi!", + "Hiya!", + "Hello there!", + "Good day!", + "Hullo!", + "Buongiorno!", + "Guten Tag!", + "Bonjour!", + "Oh hey!", + ]; + Alpine.store("ui").logMsg(`MARL loaded. ${salutations[Math.floor(Math.random() * salutations.length)]} 😊`); + + resetStores(); +}); + +document.addEventListener("alpine-i18n:ready", function () { + AlpineI18n.create("en", appStrings); + AlpineI18n.fallbackLocale = "en"; + setLang(); +}); diff --git a/dist/js/libs.js b/dist/js/libs.js new file mode 100644 index 0000000..b8db65e --- /dev/null +++ b/dist/js/libs.js @@ -0,0 +1,36 @@ +const isFileProtocol = window.location.protocol === "file:"; + +const scripts = [ + { + src: "js/jszip.min.js", + integrity: "sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==", + crossorigin: "anonymous", + defer: false, + }, + // Note: Alpine plug-ins must be inserted BEFORE alpinejs + { + src: "js/alpinejs-i18n.min.js", + integrity: "sha256-o204NcFyHPFzboSC51fufMqFe2KJdQfSCl8AlvSZO/E=", + crossorigin: "anonymous", + defer: true, + }, + { + src: "js/alpinejs.min.js", + integrity: "sha512-FUaEyIgi9bspXaH6hUadCwBLxKwdH7CW24riiOqA5p8hTNR/RCLv9UpAILKwqs2AN5WtKB52CqbiePBei3qjKg==", + crossorigin: "anonymous", + defer: true, + }, +]; + +scripts.forEach(({ src, integrity, crossorigin, defer }) => { + const script = document.createElement("script"); + script.src = src; + if (!isFileProtocol) { + script.integrity = integrity; + script.crossOrigin = crossorigin; + } + if (defer) { + script.defer = true; + } + document.head.appendChild(script); +}); diff --git a/dist/js/main.js b/dist/js/main.js deleted file mode 100644 index dd0d4c8..0000000 --- a/dist/js/main.js +++ /dev/null @@ -1,1507 +0,0 @@ -// stores definitions - -const userPrefsStore = { - prefix: "marl_", - - save(pref, value) { - localStorage.setItem(this.prefix + pref, value); - }, - load(pref) { - const value = localStorage.getItem(this.prefix + pref); - if (value !== null) { - this.set(pref, value); - } - }, - set(pref, value) { - switch (pref) { - case "sortAsc": - value = +value === 1 ? true : false; - if (value !== Alpine.store("files").sortAsc) { - Alpine.store("files").sortAsc = value; - } - break; - case "pageSize": - value = +value; - if (typeof value == "number" && !isNaN(value) && value > 0 && value !== Alpine.store("files").pageSize) { - Alpine.store("files").pageSize = value; - } - break; - case "lang": - // ### validation - Alpine.store("ui").lang = value; - break; - } - }, -}; - -const filesStore = { - resetState() { - this.sources = []; - this.toots = []; - this.toc = []; - this.duplicates = false; - - this.sortAsc = true; // -> userPrefs - this.pageSize = 10; // -> userPrefs - this.currentPage = 1; - - this.loading = false; - this.someFilesLoaded = false; - - this.languages = {}; - this.boostsAuthors = []; - - this.filters = {}; - this.filtersDefault = { - fullText: "", - hashtagText: "", - mentionText: "", - externalLink: "", - summary: "", - isEdited: false, - isDuplicate: false, - noStartingAt: false, - hasExternalLink: false, - hasHashtags: false, - hasMentions: false, - hasSummary: false, - isSensitive: false, - visibilityPublic: true, - visibilityUnlisted: true, - visibilityFollowers: true, - visibilityMentioned: true, - typeOriginal: true, - typeBoost: true, - attachmentAny: false, - attachmentImage: false, - attachmentVideo: false, - attachmentSound: false, - attachmentNoAltText: false, - attachmentWithAltText: false, - - // automatically generated (see loadJsonFile()): - // lang_en: true, - // lang_fr: true, - // lang_de: true, - // etc - // actor_0: true, - // actor_1: true, - // actor_2: true, - // etc - }; - this.filtersActive = false; - - this.tagsFilters = { - hashtags: "", - mentions: "", - boostsAuthors: "", - }; - }, - - setFilter() { - this.checkPagingValue(); - scrollTootsToTop(); - pagingUpdated(); - if (JSON.stringify(this.filters) === JSON.stringify(this.filtersDefault)) { - this.filtersActive = false; - } else { - this.filtersActive = true; - } - - const self = this; - setTimeout(() => { - self.checkPagingValue(); - }, 50); - }, - filterByTag(filter, value, id) { - if (value) { - if (value === this.filters[filter]) { - this.filters[filter] = ""; - } else { - this.filters[filter] = value; - } - } - - // "boosted users" group - // in this case let's also (un)check the 'boost type' filters - if (filter == "fullText") { - if (this.filters[filter] === "") { - this.filters.typeBoost = true; - this.filters.typeOriginal = true; - } else { - this.filters.typeBoost = true; - this.filters.typeOriginal = false; - } - } - - this.setFilter(); - - // keyboard focus may be lost when tags list changes - setTimeout(() => { - document.getElementById(id).focus(); - }, 100); - }, - resetFilters(userAction) { - this.filters = JSON.parse(JSON.stringify(this.filtersDefault)); - if (userAction) { - this.currentPage = 1; - this.filtersActive = false; - scrollTootsToTop(); - pagingUpdated(); - } - }, - - get filteredToots() { - const f = this.filters; - const fa = this.filtersActive; - return this.toots.filter((t) => { - if (!fa) { - return true; - } - - if (f.fullText) { - let show = false; - if (t._marl.textContent) { - const filterValue = f.fullText.toLowerCase(); - - if (filterValue && t._marl.textContent && t._marl.textContent.indexOf(filterValue) >= 0) { - show = true; - } - } - if (!show) { - return show; - } - } - - if (f.hashtagText) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - const filterValue = f.hashtagText.toLowerCase(); - if ( - !t.object.tag.some((t) => { - return t.type === "Hashtag" && t.name.toLowerCase().indexOf(filterValue) > -1; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.mentionText) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - const filterValue = f.mentionText.toLowerCase(); - if ( - !t.object.tag.some((t) => { - return t.type === "Mention" && t.name.toLowerCase().indexOf(filterValue) > -1; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.summary) { - if (t._marl.summary) { - const filterValue = f.summary.toLowerCase(); - if (t._marl.summary.indexOf(filterValue) === -1) { - return false; - } - } else { - return false; - } - } - - if (f.isEdited) { - if (!(typeof t.object === "object" && t.object !== null && t.object.updated)) { - return false; - } - } - - if (f.isDuplicate) { - if (!t._marl.duplicate) { - return false; - } - } - - if (f.noStartingAt) { - if (!t._marl.textContent || t._marl.textContent.indexOf("@") === 0) { - return false; - } - } - - if (f.hasExternalLink) { - if (!t._marl.externalLinks || !t._marl.externalLinks.length) { - return false; - } - } - - if (f.hasHashtags) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - if ( - !t.object.tag.some((t) => { - return t.type === "Hashtag"; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.hasMentions) { - if (typeof t.object === "object" && t.object !== null && t.object.tag) { - if ( - !t.object.tag.some((t) => { - return t.type === "Mention"; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.hasSummary) { - if (typeof t.object === "object" && t.object !== null) { - if (!t.object.summary) { - return false; - } - } else { - return false; - } - } - - if (f.isSensitive) { - if (typeof t.object === "object" && t.object !== null) { - if (!t.object.sensitive) { - return false; - } - } else { - return false; - } - } - - if (f.externalLink) { - let show = false; - if (t._marl.externalLinks && t._marl.externalLinks.length) { - const filterValue = f.externalLink.toLowerCase(); - show = t._marl.externalLinks.some((link) => { - return link.href.indexOf(filterValue) > -1 || link.text.indexOf(filterValue) > -1; - }); - } - if (!show) { - return false; - } - } - - if (!f.visibilityPublic && t._marl.visibility[0] === "public") { - return false; - } - if (!f.visibilityUnlisted && t._marl.visibility[0] === "unlisted") { - return false; - } - if (!f.visibilityFollowers && t._marl.visibility[0] === "followers") { - return false; - } - if (!f.visibilityMentioned && t._marl.visibility[0] === "mentioned") { - return false; - } - - if (!f.typeOriginal && t.type === "Create") { - return false; - } - if (!f.typeBoost && t.type === "Announce") { - return false; - } - - if (f.attachmentAny) { - if (!t._marl.hasAttachments) { - return false; - } - } - if (f.attachmentImage) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return attachmentIsImage(att); - }) - ) { - return false; - } - } else { - return false; - } - } - if (f.attachmentVideo) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return attachmentIsVideo(att); - }) - ) { - return false; - } - } else { - return false; - } - } - if (f.attachmentSound) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return attachmentIsSound(att); - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.attachmentNoAltText) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return att.name === null; - }) - ) { - return false; - } - } else { - return false; - } - } - - if (f.attachmentWithAltText) { - if (t._marl.hasAttachments) { - if ( - !t.object.attachment.some((att) => { - return att.name; - }) - ) { - return false; - } - } else { - return false; - } - } - - for (const lang in this.languages) { - if (f.hasOwnProperty("lang_" + lang) && f["lang_" + lang] === false) { - if (t._marl.langs.includes(lang) || t._marl.langs.length === 0) { - return false; - } - } - } - - for (const source of this.sources) { - const id = source.id; - if (f.hasOwnProperty("actor_" + id) && f["actor_" + id] === false) { - if (t._marl.source === id) { - return false; - } - } - } - - return true; - }); - }, - - get listHashtags() { - return this.listTags("Hashtag"); - }, - get listMentions() { - return this.listTags("Mention"); - }, - listTags(type) { - let filterSource = ""; - switch (type) { - case "Mention": - filterSource = "mentions"; - break; - case "Hashtag": - filterSource = "hashtags"; - break; - } - let h = this.filteredToots.reduce((accu, toot) => { - if (tootHasTags(toot)) { - for (const key in toot.object.tag) { - const tag = toot.object.tag[key]; - if ( - tag.type && - tag.type === type && - tag.name && - tag.name.toLowerCase().indexOf(this.tagsFilters[filterSource].toLowerCase()) >= 0 - ) { - if ( - accu.some((item) => { - return item.name === tag.name; - }) - ) { - accu.map((item) => { - if (item.name === tag.name) { - item.nb++; - } - }); - } else { - accu.push({ - name: tag.name, - href: tag.href, - nb: 1, - }); - } - } - } - } - return accu; - }, []); - - h.sort((a, b) => { - if (a.nb === b.nb) { - return a.name.localeCompare(b.name); - } else { - return b.nb - a.nb; - } - }); - - return h; - }, - get listBoostsAuthors() { - let r = this.boostsAuthors.reduce((accu, item) => { - if (item.name.toLowerCase().indexOf(this.tagsFilters.boostsAuthors.toLowerCase()) >= 0) { - accu.push(item); - } - return accu; - }, []); - r.sort((a, b) => { - if (a.nb === b.nb) { - let aHasNoName = a.name.indexOf("? ") === 0; - let bHasNoName = b.name.indexOf("? ") === 0; - if (aHasNoName && bHasNoName) { - return a.name.localeCompare(b.name); - } else if (aHasNoName) { - return 1; - } else if (bHasNoName) { - return -1; - } else { - return a.name.localeCompare(b.name); - } - } else { - if (a.nb === b.nb) { - return a.name.localeCompare(b.name); - } else { - return b.nb - a.nb; - } - } - }); - return r; - }, - - get sortedLanguages() { - let langs = []; - for (const lang in this.languages) { - langs.push([lang, this.languages[lang]]); - } - langs.sort((a, b) => { - if (a[0] === "undefined") { - return 1; - } - if (b[0] === "undefined") { - return -1; - } - if (a[1] === b[1]) { - return a[0].localeCompare(b[0]); - } - return b[1] - a[1]; - }); - return langs; - }, - - get appReady() { - if (this.sources.length === 0) { - return false; - } - - let r = true; - for (let i = 0; i < this.sources.length; i++) { - const source = this.sources[i]; - if ( - !source.loaded.actor || - !source.loaded.avatar || - !source.loaded.header || - !source.loaded.outbox || - !source.loaded.likes || - !source.loaded.bookmarks - ) { - r = false; - } - } - return r; - }, - - get totalPages() { - return Math.ceil(this.filteredToots.length / this.pageSize); - }, - get pagedToots() { - if (this.filteredToots) { - return this.filteredToots.filter((_, index) => { - let start = (this.currentPage - 1) * this.pageSize; - let end = this.currentPage * this.pageSize; - if (index >= start && index < end) return true; - }); - } else { - return []; - } - }, - - sortToots() { - this.toots.sort((a, b) => { - if (this.sortAsc) { - return a.published.localeCompare(b.published); - } else { - return b.published.localeCompare(a.published); - } - }); - }, - toggleTootsOrder() { - this.sortAsc = !this.sortAsc; - this.sortToots(); - scrollTootsToTop(); - pagingUpdated(); - }, - - checkPagingValue() { - if (this.currentPage < 1) { - this.currentPage = 1; - } else if (this.currentPage > this.totalPages) { - this.currentPage = this.totalPages; - } - }, - nextPage(setFocusTo) { - if (this.currentPage * this.pageSize < this.filteredToots.length) { - this.currentPage++; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - } - }, - prevPage(setFocusTo) { - if (this.currentPage > 1) { - this.currentPage--; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - } - }, - firstPage(setFocusTo) { - this.currentPage = 1; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - }, - lastPage(setFocusTo) { - this.currentPage = this.totalPages; - scrollTootsToTop(setFocusTo); - pagingUpdated(); - }, -}; - -const lightboxStore = { - resetState() { - this.show = false; - this.data = []; - this.source = 0; - this.index = 0; - this.origin = ""; - }, - - open(toot, index, origin) { - this.data = toot.object.attachment; - this.source = toot._marl.source; - this.show = true; - this.index = index; - this.origin = origin; - document.getElementById("main-section-inner").setAttribute("inert", true); - setTimeout(() => { - document.getElementById("lightbox").focus(); - }, 50); - }, - openProfileImg(name, origin, source) { - const data = { - object: { - attachment: [ - { - name: name, - url: name, - mediaType: Alpine.store("files").sources[source][name].type, - }, - ], - }, - _marl: { - source: source, - }, - }; - this.open(data, 0, origin); - }, - close() { - const origin = this.origin; - this.data = []; - this.index = 0; - this.show = false; - this.origin = ""; - document.getElementById("main-section-inner").removeAttribute("inert"); - document.getElementById(origin).focus(); - }, - showNext() { - this.index++; - if (this.index >= this.data.length) { - this.index = 0; - } - if (!attachmentIsImage(this.data[this.index])) { - this.showNext(); - } - }, - showPrev() { - this.index--; - if (this.index < 0) { - this.index = this.data.length - 1; - } - if (!attachmentIsImage(this.data[this.index])) { - this.showPrev(); - } - }, -}; - -const uiStore = { - resetState() { - this.pagingOptionsVisible = false; - this.openMenu = ""; - this.actorPanel = 0; - this.menuIsActive = false; - this.lang = "en"; - }, - - togglePagingOptions() { - this.pagingOptionsVisible = !this.pagingOptionsVisible; - - if (this.pagingOptionsVisible) { - setTimeout(() => { - document.getElementById("paging-options").focus(); - }, 100); - } - }, - get pagingOptionsClass() { - return this.pagingOptionsVisible ? "open" : ""; - }, - - openActorPanel(id) { - this.actorPanel = id; - }, - switchActorPanel(dir) { - let id = this.actorPanel; - if (dir === "up") { - id++; - if (id >= Alpine.store("files").sources.length) { - id = 0; - } - } else { - id--; - if (id < 0) { - id = Alpine.store("files").sources.length - 1; - } - } - this.actorPanel = id; - document.getElementById("actortab-" + id).focus(); - }, - - menuClose() { - const name = this.openMenu; - this.openMenu = ""; - this.setInert(); - - // bring focus back to where it was before the panel was opened - document.querySelector("#main-section-inner .mobile-menu .menu-" + name).focus(); - }, - menuOpen(name) { - this.openMenu = name; - this.resetPanels(); - this.setInert(); - - setTimeout(() => { - document.getElementById("panel-" + name).focus(); - }, 100); - }, - menuToggle(name) { - switch (name) { - case "actor": - case "filters": - case "tags": - if (this.openMenu === name) { - this.menuClose(); - } else { - this.menuOpen(name); - } - break; - } - }, - resetPanels() { - const name = this.openMenu; - document.querySelectorAll(`#panel-${name} details[open]`).forEach((e) => { - e.removeAttribute("open"); - }); - setTimeout(() => { - document.getElementById("panel-" + name).scrollTop = 0; - }, 250); - }, - checkMenuState() { - const menu = document.getElementById("mobile-menu"); - if (window.getComputedStyle(menu, null).display === "none") { - this.menuIsActive = false; - } else { - this.menuIsActive = true; - } - - this.setInert(); - }, - setInert() { - // set the 'inert' state on the side panels (actor, filters, tags) - // depending on whether they are hidden or not, AND whether the - // mobile menu is active - - document.querySelectorAll("#main-section-inner > *").forEach((e) => { - e.removeAttribute("inert"); - }); - - if (this.menuIsActive) { - if (this.openMenu) { - document - .querySelectorAll("#main-section-inner > *:not(.mobile-menu, .panel-backdrop, #panel-" + this.openMenu) - .forEach((e) => { - e.setAttribute("inert", true); - }); - } else { - document.querySelectorAll("#panel-actor, #panel-filters, #panel-tags").forEach((e) => { - e.setAttribute("inert", true); - }); - } - } - }, - - get appClasses() { - let classes = []; - if (this.openMenu) { - classes.push("menu-open menu-open-" + this.openMenu); - } else { - classes.push("menu-closed"); - } - return classes; - }, -}; - -// utils - -function resetStores() { - Alpine.store("files").resetState(); - Alpine.store("lightbox").resetState(); - Alpine.store("ui").resetState(); - - Alpine.store("userPrefs").load("sortAsc"); - Alpine.store("userPrefs").load("pageSize"); - Alpine.store("userPrefs").load("lang"); -} - -function unZip(files) { - const firstLoad = Alpine.store("files").sources.length === 0; - if (firstLoad) { - resetStores(); - } - Alpine.store("files").loading = true; - - for (let i = 0; i < files.length; i++) { - const file = files[i]; - - if ( - Alpine.store("files").sources.some((source) => { - return ( - source.fileInfos.name === file.name && - source.fileInfos.size === file.size && - source.fileInfos.lastModified === file.lastModified - ); - }) - ) { - console.warn("File already loaded:", file.name); - continue; - } - - JSZip.loadAsync(file).then( - (content) => { - const index = Alpine.store("files").sources.length; - const fileInfos = { - name: file.name, - size: file.size, - lastModified: file.lastModified, - }; - - Alpine.store("files").sources[index] = { - id: index, - fileInfos: { - name: file.name, - size: file.size, - lastModified: file.lastModified, - }, - nbToots: 0, - - actor: {}, - outbox: {}, - likes: [], - bookmarks: [], - avatar: {}, - header: {}, - - loaded: { - actor: false, - avatar: false, - header: false, - outbox: false, - likes: false, - bookmarks: false, - }, - }; - - Alpine.store("files").sources[index]._raw = content.files; - - loadJsonFile("actor", index, fileInfos); - loadJsonFile("outbox", index, fileInfos); - loadJsonFile("likes", index, fileInfos); - loadJsonFile("bookmarks", index, fileInfos); - }, - (error) => { - console.error(`Error loading ${file.name}:`, error.message); - } - ); - } -} - -function loadJsonFile(name, index, fileInfos) { - const content = Alpine.store("files").sources[index]._raw; - - if (content[name + ".json"] === undefined) { - if (name === "likes" || name === "bookmarks") { - // we can still run the app without those files - console.warn(`${fileInfos.name}: File ${name}.json not found in archive.`); - Alpine.store("files").sources[index].loaded[name] = true; - } else { - // this should NOT happen and will prevent the app from running - console.error(`${fileInfos.name}: File ${name}.json not found in archive.`); - } - return; - } - - content[name + ".json"].async("text").then(function (txt) { - if (name === "actor") { - Alpine.store("files").sources[index].actor = JSON.parse(txt); - loadActorImages(index); - Alpine.store("files").sources[index].loaded.actor = true; - } // actor.json - - if (name === "outbox") { - let data = JSON.parse(txt); - - let toots = data.orderedItems.reduce((accu, t) => { - let t2 = preprocessToots(t, index); - if (t2) { - accu.push(t2); - } - return accu; - }, []); - - Alpine.store("files").toots = Alpine.store("files").toots.concat(toots); - Alpine.store("files").sources[index].nbToots = toots.length; - delete data.orderedItems; - Alpine.store("files").sources[index].outbox = data; - Alpine.store("files").sources[index].loaded.outbox = true; - } // outbox.json - - if (name === "likes" || name === "bookmarks") { - const tmp = JSON.parse(txt); - Alpine.store("files").sources[index][name] = tmp.orderedItems; - Alpine.store("files").sources[index].loaded[name] = true; - } // likes.json || bookmarks.json - }); -} - -function buildTootsInfos() { - let langs = {}; - let boosts = []; - - if (Alpine.store("files").toots.length > 0) { - let infos = Alpine.store("files").toots.reduce( - (accu, toot) => { - for (let lang in toot._marl.langs) { - const l = toot._marl.langs[lang]; - if (!accu.langs[l]) { - accu.langs[l] = 1; - } else { - accu.langs[l]++; - } - } - - if (toot.type === "Announce") { - // since Mastodon doesn't allow (yet?) cross-origin requests to - // retrieve post data (for boosts), we try to at least extract the - // user names for all the boosts contained in the archive - - // [ISSUE] "object" value is a string most of the times, but - // sometimes it's a complex object similar to type "Create" - if (typeof toot.object === "object" && toot.object !== null) { - // let's ignore this case for now... - // [TODO], but not clear how it should be handled - } else if (toot.object) { - // if it's not an object and it has a value, then it's simply a - // url (string) pointing to the original (boosted) post. - // [ISSUE] URL format not always consistent... (esp. in the case - // of non-Mastodon instances) - e.g: - // https://craftopi.art/objects/[...] - // https://firefish.city/notes/[...] - // https://bsky.brid.gy/convert/ap/at://did:plc:[...]/app.bsky.feed.post/[...] - // -> the user name is not always present in URL - const url = toot.object.split("/"); - let name; - let user; - let domain; - if (url.length > 2) { - domain = url[2]; - - if (url[0] === "https:" && url[3] === "users" && url[5] === "statuses") { - // Mastodon URL format -> user name - name = url[4]; - user = `https://${url[2]}/users/${url[4]}/`; - } else { - // other URL format -> domain name - name = `? ${url[2]}`; - user = `https://${url[2]}/`; - } - - if (!accu.boosts[name]) { - accu.boosts[name] = { - nb: 1, - name: name, - url: user, - domain: domain, - }; - } else { - accu.boosts[name].nb++; - } - } - } - } - return accu; - }, - { langs: {}, boosts: {} } - ); - - langs = infos.langs; - - boosts = []; - for (var key in infos.boosts) { - boosts.push(infos.boosts[key]); - } - } - - Alpine.store("files").languages = langs; - Alpine.store("files").boostsAuthors = boosts; -} - -function buildDynamicFilters() { - for (const lang in Alpine.store("files").languages) { - Alpine.store("files").filtersDefault["lang_" + lang] = true; - } - - for (const source of Alpine.store("files").sources) { - Alpine.store("files").filtersDefault["actor_" + source.id] = true; - } - - Alpine.store("files").resetFilters(false); -} - -function preprocessToots(t, index) { - // build the '_marl' prop for each toot - let marl = { - langs: [], - source: index, - }; - - // check for duplicates (in case of multiple archive files) - if (Alpine.store("files").toc.includes(t.id)) { - const alts = Alpine.store("files").toots.filter((t2) => t2.id === t.id); - - let identical = false; - const flat1 = JSON.stringify(t); - - alts.forEach((alt) => { - let alt2 = JSON.parse(JSON.stringify(alt)); - delete alt2._marl; - const flat2 = JSON.stringify(alt2); - - if (flat1 === flat2) { - identical = true; - } else { - alt._marl.duplicate = true; - marl.duplicate = true; - Alpine.store("files").duplicates = true; - } - }); - if (identical) { - return false; - } - } else { - Alpine.store("files").toc.push(t.id); - } - - if (t.type === "Create") { - if (typeof t.object === "object" && t.object !== null && t.object.contentMap) { - let langs = []; - for (let lang in t.object.contentMap) { - langs.push(lang); - } - marl.langs = langs; - } else { - marl.langs = ["undefined"]; - } - } - - if (typeof t.object === "object" && t.object !== null) { - if (t.object.content) { - const content = t.object.content.toLowerCase(); - marl.textContent = stripHTML(content); - marl.externalLinks = extractExternalLinks(content); - } - if (t.object.summary) { - marl.summary = t.object.summary.toLowerCase(); - } - - if (t.object.attachment && t.object.attachment.length) { - marl.hasAttachments = true; - } - } else if (t.object) { - marl.textContent = t.object.toLowerCase(); - } - - marl.visibility = tootVisibility(t); - - const id = t.id.split("/"); - marl.id = id[id.length - 2]; - - t._marl = marl; - return t; -} - -function loadActorImages(index) { - const actor = Alpine.store("files").sources[index].actor; - const content = Alpine.store("files").sources[index]._raw; - - if (actor.icon && actor.icon.type === "Image" && actor.icon.url && content[actor.icon.url]) { - const image = actor.icon; - content[image.url].async("base64").then(function (content) { - Alpine.store("files").sources[index].avatar = { - type: image.mediaType, - content: content, - noImg: false, - }; - Alpine.store("files").sources[index].loaded.avatar = true; - }); - } else { - Alpine.store("files").sources[index].avatar = { noImg: true }; - Alpine.store("files").sources[index].loaded.avatar = true; - } - - if (actor.image && actor.image.type === "Image" && actor.image.url && content[actor.image.url]) { - const image = actor.image; - content[image.url].async("base64").then(function (content) { - Alpine.store("files").sources[index].header = { - type: image.mediaType, - content: content, - noImg: false, - }; - Alpine.store("files").sources[index].loaded.header = true; - }); - } else { - Alpine.store("files").sources[index].header = { noImg: true }; - Alpine.store("files").sources[index].loaded.header = true; - } -} - -function setHueForSources() { - const nbSources = Alpine.store("files").sources.length; - const hueStart = Math.round(Math.random() * 360); // MARL accent: 59.17 - const hueSpacing = Math.round(360 / nbSources); - - for (let i = 0; i < nbSources; i++) { - Alpine.store("files").sources[i].hue = hueStart + hueSpacing * i; - } -} - -function checkAppReady(ok) { - if (ok) { - buildTootsInfos(); - buildDynamicFilters(); - cleanUpRaw(); - setHueForSources(); - document.getElementById("main-section").focus(); - Alpine.store("ui").checkMenuState(); - Alpine.store("files").sortToots(); - Alpine.store("files").loading = false; - Alpine.store("files").someFilesLoaded = true; - } -} - -function cleanUpRaw() { - for (let i = 0; i < Alpine.store("files").sources.length; i++) { - const content = Alpine.store("files").sources[i]._raw; - if (content.cleanedUp) { - continue; - } - - const actor = Alpine.store("files").sources[i].actor; - if (actor.image && actor.image.url) { - delete content[actor.image.url]; - } - if (actor.icon && actor.icon.url) { - delete content[actor.icon.url]; - } - delete content["actor.json"]; - delete content["outbox.json"]; - delete content["likes.json"]; - delete content["bookmarks.json"]; - content.cleanedUp = true; - - Alpine.store("files").sources[i]._raw = content; - } -} - -function loadAttachedMedia(att, index) { - if (attachmentIsImage(att) || attachmentIsVideo(att) || attachmentIsSound(att)) { - const data = Alpine.store("files").sources[index]._raw; - let url = att.url; - // ?! some instances seem to add their own name in front of the path, - // resulting in an invalid path with relation to the archive - // structure (e.g. "/framapiaf/media_attachments/...", but in the - // archive there is only a folder "/media_attachments") - // => So we remove everything that comes before "media_attachments/", - // hoping it doesn't break something else... :/ - const prefix = url.indexOf("media_attachments/"); - if (prefix > 0) { - url = url.slice(prefix); - } - if (!data[url]) { - return; - } - data[url].async("base64").then((content) => { - Alpine.store("files").sources[index][att.url] = { - type: att.mediaType, - content: content, - }; - }); - } -} - -function pagingUpdated() { - document.querySelectorAll(`#toots details[open]`).forEach((e) => { - e.removeAttribute("open"); - }); -} - -function scrollTootsToTop(setFocusTo) { - setTimeout(() => { - document.getElementById("toots").scrollTop = 0; - if (setFocusTo) { - // for keyboard users: we transfer the focus to the corresponding button - // in the upper paging module; or, in the cases where said button is - // disabled, we set the focus on the list of posts. - document.getElementById(setFocusTo).focus(); - } - }, 50); -} - -function contentType(data) { - let r = ""; - switch (data) { - case "Create": - r = "Post"; - break; - case "Announce": - r = "Boost"; - break; - } - return r; -} - -function tootVisibility(data) { - if (data.to.includes("https://www.w3.org/ns/activitystreams#Public")) { - return ["public", AlpineI18n.t("filters.visibilityPublic")]; - } - if ( - data.to.some((x) => x.indexOf("/followers") > -1) && - !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && - data.cc.includes("https://www.w3.org/ns/activitystreams#Public") - ) { - return ["unlisted", AlpineI18n.t("filters.visibilityUnlisted")]; - } - if ( - data.to.some((x) => x.indexOf("/followers") > -1) && - !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && - !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") - ) { - return ["followers", AlpineI18n.t("filters.visibilityFollowers")]; - } - if ( - !data.to.some((x) => x.indexOf("/followers") > -1) && - !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && - !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") - ) { - return ["mentioned", AlpineI18n.t("filters.visibilityMentioned")]; - } -} - -function tootHasTags(toot) { - return typeof toot.object === "object" && toot.object !== null && toot.object.tag && toot.object.tag.length; -} - -function formatJson(data) { - let r = data; - if (r._marl) { - // not a part of the source data; let's hide it to avoid confusion - r = JSON.parse(JSON.stringify(data)); - delete r._marl; - } - return JSON.stringify(r, null, 4); -} - -function formatAuthor(author, plainText) { - if (plainText) { - return author.split("/").pop(); - } else { - return `${author.split("/").pop()}`; - } -} - -function formatDateTime(data) { - let date = new Date(data); - const dateOptions = { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - hour: "numeric", - minute: "2-digit", - second: "2-digit", - }; - return date.toLocaleDateString(undefined, dateOptions); -} - -function formatDate(data) { - let date = new Date(data); - const dateOptions = { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric", - }; - return date.toLocaleDateString(undefined, dateOptions); -} - -function formatNumber(nb) { - return nb.toLocaleString(); -} - -function formatLikesBookmarks(url) { - const u = url.split("/"); - u.splice(0, 2); - - // 0 [domain] - // 1 "users" - // 2 [username] - // 3 "statuses" - // 4 [post id] - - let text = `${u[0]}`; - if (u[1] === "users" && u[3] === "statuses") { - text += `${u[2]}${u[4]}`; - } else { - u.splice(0, 1); - text += `${u.join("/")}`; - } - return text; -} - -function stripHTML(str) { - let doc = new DOMParser().parseFromString(str, "text/html"); - return doc.body.textContent || ""; -} - -function extractExternalLinks(str) { - const doc = new DOMParser().parseFromString(str, "text/html"); - const nodes = doc.querySelectorAll("a[href]:not(.mention)"); - let links = []; - nodes.forEach((link) => { - links.push({ - href: link.href, - text: link.textContent, - }); - }); - return links; -} - -function attachmentIsImage(att) { - return att.mediaType === "image/jpeg" || att.mediaType === "image/png"; -} - -function attachmentIsVideo(att) { - return att.mediaType === "video/mp4"; -} - -function attachmentIsSound(att) { - return att.mediaType === "audio/mpeg"; -} - -function attachmentWrapperClass(att) { - let r = []; - if (attachmentIsImage(att)) { - r.push("att-img"); - } else if (attachmentIsSound(att)) { - r.push("att-sound"); - } else if (attachmentIsVideo(att)) { - r.push("att-video"); - } - - if (!att.name) { - r.push("no-alt-text"); - } - - return r; -} - -function isFilterActive(name) { - return Alpine.store("files").filters[name] !== Alpine.store("files").filtersDefault[name]; -} - -function startOver() { - const txt = AlpineI18n.t("menu.newFileConfirm"); - if (confirm(txt)) { - location.reload(); - } -} - -// drag'n'drop over entire page - -const drag = { - el: null, - - init(el) { - this.dropArea = document.getElementById(el); - - ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { - this.dropArea.addEventListener(eventName, (e) => this.preventDragDefaults(e), false); - }); - ["dragenter", "dragover"].forEach((eventName) => { - this.dropArea.addEventListener(eventName, () => this.highlightDrag(), false); - }); - ["dragleave", "drop"].forEach((eventName) => { - this.dropArea.addEventListener(eventName, () => this.unhighlightDrag(), false); - }); - this.dropArea.addEventListener("drop", (e) => this.handleDrop(e), false); - }, - - preventDragDefaults(e) { - e.preventDefault(); - e.stopPropagation(); - }, - highlightDrag() { - this.dropArea.classList.add("highlight-drag"); - }, - unhighlightDrag() { - this.dropArea.classList.remove("highlight-drag"); - }, - handleDrop(e) { - const dt = e.dataTransfer; - const files = dt.files; - unZip(files); - }, -}; - -// initialization - -drag.init("app"); - -const isFileProtocol = window.location.protocol === "file:"; -const scripts = [ - { - src: "js/jszip.min.js", - integrity: "sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==", - crossorigin: "anonymous", - defer: false, - }, - { - src: "js/alpinejs-i18n.min.js", - integrity: "sha256-o204NcFyHPFzboSC51fufMqFe2KJdQfSCl8AlvSZO/E=", - crossorigin: "anonymous", - defer: true, - }, - { - src: "js/alpinejs.min.js", - integrity: "sha512-FUaEyIgi9bspXaH6hUadCwBLxKwdH7CW24riiOqA5p8hTNR/RCLv9UpAILKwqs2AN5WtKB52CqbiePBei3qjKg==", - crossorigin: "anonymous", - defer: true, - }, -]; - -scripts.forEach(({ src, integrity, crossorigin, defer }) => { - const script = document.createElement("script"); - script.src = src; - if (!isFileProtocol) { - script.integrity = integrity; - script.crossOrigin = crossorigin; - } - if (defer) { - script.defer = true; - } - document.head.appendChild(script); -}); - -document.addEventListener("alpine:init", () => { - Alpine.store("files", filesStore); - Alpine.store("lightbox", lightboxStore); - Alpine.store("ui", uiStore); - Alpine.store("userPrefs", userPrefsStore); - - resetStores(); - - // watch user preference values for changes - Alpine.effect(() => { - const pageSize = Alpine.store("files").pageSize; - const sortAsc = Alpine.store("files").sortAsc; - const lang = Alpine.store("ui").lang; - - console.log("===> effect!"); - - Alpine.store("userPrefs").save("pageSize", pageSize); - Alpine.store("userPrefs").save("sortAsc", sortAsc ? 1 : 0); - Alpine.store("userPrefs").save("lang", lang); - }); -}); - -document.addEventListener("alpine-i18n:ready", function () { - window.AlpineI18n.create("en", appStrings); - window.AlpineI18n.fallbackLocale = "en"; - - console.log("\tlocale 1", AlpineI18n.locale); - // Alpine.store("userPrefs").load("lang"); - const lang = Alpine.store("ui").lang; - // ### if undefined - // ### validate against list of langs - AlpineI18n.locale = lang; - // console.log("\tlocale 2", AlpineI18n.locale); -}); diff --git a/dist/js/stores.js b/dist/js/stores.js new file mode 100644 index 0000000..da227b1 --- /dev/null +++ b/dist/js/stores.js @@ -0,0 +1,888 @@ +// stores definitions + +const userPrefsStore = { + prefix: "marl_", + + save(pref, value) { + const msg = `Saving user preference (${pref}: ${value})`; + Alpine.store("ui").logMsg(msg, "info"); + localStorage.setItem(this.prefix + pref, value); + }, + load(pref) { + const value = localStorage.getItem(this.prefix + pref); + if (value !== null) { + this.set(pref, value); + } else if (pref === "lang") { + this.set(pref, value); + } + }, + set(pref, value) { + switch (pref) { + case "sortAsc": + value = +value === 1 ? true : false; + if (value !== Alpine.store("files").sortAsc) { + Alpine.store("files").sortAsc = value; + } + break; + + case "pageSize": + value = +value; + if (typeof value == "number" && !isNaN(value) && value > 0 && value !== Alpine.store("files").pageSize) { + Alpine.store("files").pageSize = value; + } + break; + + case "lang": + if (!value) { + value = detectLangFromBrowser(); + if (value) { + this.save("lang", value); + } + } + if (!value || !Alpine.store("ui").appLangs[value]) { + if (value) { + const msg = `Unrecognized language in user preferences: ${value}`; + console.warn(msg); + Alpine.store("ui").logMsg(msg, "warn"); + } + value = "en"; + this.save("lang", value); + } + Alpine.store("ui").lang = value; + break; + + case "theme": + if (!(value === "dark" || value === "light")) { + value = "light"; + this.save("theme", value); + } + Alpine.store("ui").theme = value; + setTheme(value); + break; + } + }, +}; + +const filesStore = { + resetState() { + this.sources = []; + this.toots = []; + this.toc = []; + this.duplicates = false; + + this.sortAsc = true; // -> userPrefs + this.pageSize = 10; // -> userPrefs + this.currentPage = 1; + + this.loading = false; + this.someFilesLoaded = false; + + this.languages = {}; + this.boostsAuthors = []; + + this.filters = {}; + this.filtersDefault = { + fullText: "", + hashtagText: "", + mentionText: "", + externalLink: "", + summary: "", + isEdited: false, + isDuplicate: false, + noStartingAt: false, + hasExternalLink: false, + hasHashtags: false, + hasMentions: false, + hasSummary: false, + isSensitive: false, + visibilityPublic: true, + visibilityUnlisted: true, + visibilityFollowers: true, + visibilityMentioned: true, + typeOriginal: true, + typeBoost: true, + attachmentAny: false, + attachmentImage: false, + attachmentVideo: false, + attachmentSound: false, + attachmentNoAltText: false, + attachmentWithAltText: false, + + // automatically generated (see loadJsonFile()): + // lang_en: true, + // lang_fr: true, + // lang_de: true, + // etc + // actor_0: true, + // actor_1: true, + // actor_2: true, + // etc + }; + this.filtersActive = false; + + this.tagsFilters = { + hashtags: "", + mentions: "", + boostsAuthors: "", + }; + + Alpine.store("userPrefs").load("sortAsc"); + Alpine.store("userPrefs").load("pageSize"); + }, + + setFilter() { + this.checkPagingValue(); + scrollTootsToTop(); + pagingUpdated(); + if (JSON.stringify(this.filters) === JSON.stringify(this.filtersDefault)) { + this.filtersActive = false; + } else { + this.filtersActive = true; + } + + const self = this; + setTimeout(() => { + self.checkPagingValue(); + }, 50); + }, + filterByTag(filter, value, id) { + if (value) { + if (value === this.filters[filter]) { + this.filters[filter] = ""; + } else { + this.filters[filter] = value; + } + } + + // "boosted users" group + // in this case let's also (un)check the 'boost type' filters + if (filter == "fullText") { + if (this.filters[filter] === "") { + this.filters.typeBoost = true; + this.filters.typeOriginal = true; + } else { + this.filters.typeBoost = true; + this.filters.typeOriginal = false; + } + } + + this.setFilter(); + + // keyboard focus may be lost when tags list changes + setTimeout(() => { + document.getElementById(id).focus(); + }, 100); + }, + resetFilters(userAction) { + this.filters = JSON.parse(JSON.stringify(this.filtersDefault)); + if (userAction) { + this.currentPage = 1; + this.filtersActive = false; + scrollTootsToTop(); + pagingUpdated(); + } + }, + + get filteredToots() { + const f = this.filters; + const fa = this.filtersActive; + return this.toots.filter((t) => { + if (!fa) { + return true; + } + + if (f.fullText) { + let show = false; + if (t._marl.textContent) { + const filterValue = f.fullText.toLowerCase(); + + if (filterValue && t._marl.textContent && t._marl.textContent.indexOf(filterValue) >= 0) { + show = true; + } + } + if (!show) { + return show; + } + } + + if (f.hashtagText) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + const filterValue = f.hashtagText.toLowerCase(); + if ( + !t.object.tag.some((t) => { + return t.type === "Hashtag" && t.name.toLowerCase().indexOf(filterValue) > -1; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.mentionText) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + const filterValue = f.mentionText.toLowerCase(); + if ( + !t.object.tag.some((t) => { + return t.type === "Mention" && t.name.toLowerCase().indexOf(filterValue) > -1; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.summary) { + if (t._marl.summary) { + const filterValue = f.summary.toLowerCase(); + if (t._marl.summary.indexOf(filterValue) === -1) { + return false; + } + } else { + return false; + } + } + + if (f.isEdited) { + if (!(typeof t.object === "object" && t.object !== null && t.object.updated)) { + return false; + } + } + + if (f.isDuplicate) { + if (!t._marl.duplicate) { + return false; + } + } + + if (f.noStartingAt) { + if (!t._marl.textContent || t._marl.textContent.indexOf("@") === 0) { + return false; + } + } + + if (f.hasExternalLink) { + if (!t._marl.externalLinks || !t._marl.externalLinks.length) { + return false; + } + } + + if (f.hasHashtags) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + if ( + !t.object.tag.some((t) => { + return t.type === "Hashtag"; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.hasMentions) { + if (typeof t.object === "object" && t.object !== null && t.object.tag) { + if ( + !t.object.tag.some((t) => { + return t.type === "Mention"; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.hasSummary) { + if (typeof t.object === "object" && t.object !== null) { + if (!t.object.summary) { + return false; + } + } else { + return false; + } + } + + if (f.isSensitive) { + if (typeof t.object === "object" && t.object !== null) { + if (!t.object.sensitive) { + return false; + } + } else { + return false; + } + } + + if (f.externalLink) { + let show = false; + if (t._marl.externalLinks && t._marl.externalLinks.length) { + const filterValue = f.externalLink.toLowerCase(); + show = t._marl.externalLinks.some((link) => { + return link.href.indexOf(filterValue) > -1 || link.text.indexOf(filterValue) > -1; + }); + } + if (!show) { + return false; + } + } + + if (!f.visibilityPublic && t._marl.visibility[0] === "public") { + return false; + } + if (!f.visibilityUnlisted && t._marl.visibility[0] === "unlisted") { + return false; + } + if (!f.visibilityFollowers && t._marl.visibility[0] === "followers") { + return false; + } + if (!f.visibilityMentioned && t._marl.visibility[0] === "mentioned") { + return false; + } + + if (!f.typeOriginal && t.type === "Create") { + return false; + } + if (!f.typeBoost && t.type === "Announce") { + return false; + } + + if (f.attachmentAny) { + if (!t._marl.hasAttachments) { + return false; + } + } + if (f.attachmentImage) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return attachmentIsImage(att); + }) + ) { + return false; + } + } else { + return false; + } + } + if (f.attachmentVideo) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return attachmentIsVideo(att); + }) + ) { + return false; + } + } else { + return false; + } + } + if (f.attachmentSound) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return attachmentIsSound(att); + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.attachmentNoAltText) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return att.name === null; + }) + ) { + return false; + } + } else { + return false; + } + } + + if (f.attachmentWithAltText) { + if (t._marl.hasAttachments) { + if ( + !t.object.attachment.some((att) => { + return att.name; + }) + ) { + return false; + } + } else { + return false; + } + } + + for (const lang in this.languages) { + if (f.hasOwnProperty("lang_" + lang) && f["lang_" + lang] === false) { + if (t._marl.langs.includes(lang) || t._marl.langs.length === 0) { + return false; + } + } + } + + for (const source of this.sources) { + const id = source.id; + if (f.hasOwnProperty("actor_" + id) && f["actor_" + id] === false) { + if (t._marl.source === id) { + return false; + } + } + } + + return true; + }); + }, + + get listHashtags() { + return this.listTags("Hashtag"); + }, + get listMentions() { + return this.listTags("Mention"); + }, + listTags(type) { + let filterSource = ""; + switch (type) { + case "Mention": + filterSource = "mentions"; + break; + case "Hashtag": + filterSource = "hashtags"; + break; + } + let h = this.filteredToots.reduce((accu, toot) => { + if (tootHasTags(toot)) { + for (const key in toot.object.tag) { + const tag = toot.object.tag[key]; + if ( + tag.type && + tag.type === type && + tag.name && + tag.name.toLowerCase().indexOf(this.tagsFilters[filterSource].toLowerCase()) >= 0 + ) { + if ( + accu.some((item) => { + return item.name === tag.name; + }) + ) { + accu.map((item) => { + if (item.name === tag.name) { + item.nb++; + } + }); + } else { + accu.push({ + name: tag.name, + href: tag.href, + nb: 1, + }); + } + } + } + } + return accu; + }, []); + + h.sort((a, b) => { + if (a.nb === b.nb) { + return a.name.localeCompare(b.name); + } else { + return b.nb - a.nb; + } + }); + + return h; + }, + get listBoostsAuthors() { + let r = this.boostsAuthors.reduce((accu, item) => { + if (item.name.toLowerCase().indexOf(this.tagsFilters.boostsAuthors.toLowerCase()) >= 0) { + accu.push(item); + } + return accu; + }, []); + r.sort((a, b) => { + if (a.nb === b.nb) { + let aHasNoName = a.name.indexOf("? ") === 0; + let bHasNoName = b.name.indexOf("? ") === 0; + if (aHasNoName && bHasNoName) { + return a.name.localeCompare(b.name); + } else if (aHasNoName) { + return 1; + } else if (bHasNoName) { + return -1; + } else { + return a.name.localeCompare(b.name); + } + } else { + if (a.nb === b.nb) { + return a.name.localeCompare(b.name); + } else { + return b.nb - a.nb; + } + } + }); + return r; + }, + + get sortedLanguages() { + let langs = []; + for (const lang in this.languages) { + langs.push([lang, this.languages[lang]]); + } + langs.sort((a, b) => { + if (a[0] === "undefined") { + return 1; + } + if (b[0] === "undefined") { + return -1; + } + if (a[1] === b[1]) { + return a[0].localeCompare(b[0]); + } + return b[1] - a[1]; + }); + return langs; + }, + + get appReady() { + if (this.sources.length === 0) { + return false; + } + + let r = true; + for (let i = 0; i < this.sources.length; i++) { + const source = this.sources[i]; + if ( + !source.loaded.actor || + !source.loaded.avatar || + !source.loaded.header || + !source.loaded.outbox || + !source.loaded.likes || + !source.loaded.bookmarks + ) { + r = false; + } + } + return r; + }, + + get totalPages() { + return Math.ceil(this.filteredToots.length / this.pageSize); + }, + get pagedToots() { + if (this.filteredToots) { + return this.filteredToots.filter((_, index) => { + let start = (this.currentPage - 1) * this.pageSize; + let end = this.currentPage * this.pageSize; + if (index >= start && index < end) return true; + }); + } else { + return []; + } + }, + + sortToots() { + this.toots.sort((a, b) => { + if (this.sortAsc) { + return a.published.localeCompare(b.published); + } else { + return b.published.localeCompare(a.published); + } + }); + }, + toggleTootsOrder() { + this.sortAsc = !this.sortAsc; + Alpine.store("userPrefs").save("sortAsc", this.sortAsc ? 1 : 0); + this.sortToots(); + scrollTootsToTop(); + pagingUpdated(); + }, + + setPostsPerPage() { + this.checkPagingValue(); + Alpine.store("userPrefs").save("pageSize", this.pageSize); + }, + checkPagingValue() { + if (this.currentPage < 1) { + this.currentPage = 1; + } else if (this.currentPage > this.totalPages) { + this.currentPage = this.totalPages; + } + }, + nextPage(setFocusTo) { + if (this.currentPage * this.pageSize < this.filteredToots.length) { + this.currentPage++; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + } + }, + prevPage(setFocusTo) { + if (this.currentPage > 1) { + this.currentPage--; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + } + }, + firstPage(setFocusTo) { + this.currentPage = 1; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + }, + lastPage(setFocusTo) { + this.currentPage = this.totalPages; + scrollTootsToTop(setFocusTo); + pagingUpdated(); + }, +}; + +const lightboxStore = { + resetState() { + this.show = false; + this.data = []; + this.source = 0; + this.index = 0; + this.origin = ""; + }, + + open(toot, index, origin) { + this.data = toot.object.attachment; + this.source = toot._marl.source; + this.show = true; + this.index = index; + this.origin = origin; + document.getElementById("main-section-inner").setAttribute("inert", true); + setTimeout(() => { + document.getElementById("lightbox").focus(); + }, 50); + }, + openProfileImg(name, origin, source) { + const data = { + object: { + attachment: [ + { + name: name, + url: name, + mediaType: Alpine.store("files").sources[source][name].type, + }, + ], + }, + _marl: { + source: source, + }, + }; + this.open(data, 0, origin); + }, + close() { + const origin = this.origin; + this.data = []; + this.index = 0; + this.show = false; + this.origin = ""; + document.getElementById("main-section-inner").removeAttribute("inert"); + document.getElementById(origin).focus(); + }, + showNext() { + this.index++; + if (this.index >= this.data.length) { + this.index = 0; + } + if (!attachmentIsImage(this.data[this.index])) { + this.showNext(); + } + }, + showPrev() { + this.index--; + if (this.index < 0) { + this.index = this.data.length - 1; + } + if (!attachmentIsImage(this.data[this.index])) { + this.showPrev(); + } + }, +}; + +const uiStore = { + log: [], + resetState() { + this.pagingOptionsVisible = false; + this.openMenu = ""; + this.actorPanel = 0; + this.menuIsActive = false; + this.lang = "en"; + this.appLangs = appLangs ?? { en: "English" }; + this.theme = "light"; + this.log = this.log ?? []; + + Alpine.store("userPrefs").load("lang"); + Alpine.store("userPrefs").load("theme"); + }, + + logMsg(msg, type) { + type = type ?? "info"; + const dateOptions = { + hour12: false, + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + const time = new Date().toLocaleTimeString(Alpine.store("ui").lang, dateOptions); + let m = { + msg: msg, + type: type, + time: time, + }; + this.log.unshift(m); + }, + + toggleTheme() { + this.theme = this.theme === "light" ? "dark" : "light"; + Alpine.store("userPrefs").save("theme", this.theme); + setTheme(this.theme); + }, + + togglePagingOptions() { + this.pagingOptionsVisible = !this.pagingOptionsVisible; + + if (this.pagingOptionsVisible) { + setTimeout(() => { + document.getElementById("paging-options").focus(); + }, 100); + } + }, + get pagingOptionsClass() { + return this.pagingOptionsVisible ? "open" : ""; + }, + + openActorPanel(id) { + this.actorPanel = id; + }, + switchActorPanel(dir) { + let id = this.actorPanel; + if (dir === "up") { + id++; + if (id >= Alpine.store("files").sources.length) { + id = 0; + } + } else { + id--; + if (id < 0) { + id = Alpine.store("files").sources.length - 1; + } + } + this.actorPanel = id; + document.getElementById("actortab-" + id).focus(); + }, + + menuClose() { + const name = this.openMenu; + this.openMenu = ""; + this.setInert(); + + // bring focus back to where it was before the panel was opened + document.querySelector("#main-section-inner .mobile-menu .menu-" + name).focus(); + }, + menuOpen(name) { + this.openMenu = name; + this.resetPanels(); + this.setInert(); + + setTimeout(() => { + document.getElementById("panel-" + name).focus(); + }, 100); + }, + menuToggle(name) { + switch (name) { + case "actor": + case "filters": + case "tags": + case "tools": + if (this.openMenu === name) { + this.menuClose(); + } else { + this.menuOpen(name); + } + break; + } + }, + resetPanels() { + const name = this.openMenu; + document.querySelectorAll(`#panel-${name} details[open]`).forEach((e) => { + e.removeAttribute("open"); + }); + setTimeout(() => { + document.getElementById("panel-" + name).scrollTop = 0; + }, 250); + }, + checkMenuState() { + const menu = document.getElementById("mobile-menu"); + if (window.getComputedStyle(menu, null).display === "none") { + this.menuIsActive = false; + } else { + this.menuIsActive = true; + } + + this.setInert(); + }, + + setInertMain() { + document + .querySelectorAll("#main-section-inner > *:not(.mobile-menu, .panel-backdrop, #panel-" + this.openMenu) + .forEach((e) => { + e.setAttribute("inert", true); + }); + }, + setInertPanels() { + document.querySelectorAll("#panel-actor, #panel-filters, #panel-tags, #panel-tools").forEach((e) => { + e.setAttribute("inert", true); + }); + }, + setInertTools() { + document.querySelectorAll("#panel-tools").forEach((e) => { + e.setAttribute("inert", true); + }); + }, + setInert() { + // set the 'inert' state on the side panels or the main part of the app + // depending on whether they are hidden or not, AND whether the mobile + // menu is active + + document.querySelectorAll("#main-section-inner > *").forEach((e) => { + e.removeAttribute("inert"); + }); + + if (this.menuIsActive) { + if (this.openMenu) { + this.setInertMain(); + } else { + this.setInertPanels(); + } + } else { + if (this.openMenu === "tools") { + this.setInertMain(); + } else { + this.setInertTools(); + } + } + }, + + get appClasses() { + let classes = []; + if (this.openMenu) { + classes.push("menu-open menu-open-" + this.openMenu); + } else { + classes.push("menu-closed"); + } + return classes; + }, +}; diff --git a/dist/js/strings.js b/dist/js/strings.js index 2d01956..1a31ea5 100644 --- a/dist/js/strings.js +++ b/dist/js/strings.js @@ -1,7 +1,7 @@ -const appLangs = [ - ["en", "English"], - ["fr", "Français"], -]; +const appLangs = { + en: "English", + fr: "Français", +}; const appStrings = { en: { @@ -16,7 +16,6 @@ const appStrings = { p3: `Start by opening your archive file with MARL.
    You can drag and drop it anywhere on this page, or {labelStart}click here to select it{labelEnd}.`, - projectPage: `Project page (github)`, }, misc: { loading: "Loading", @@ -27,8 +26,7 @@ const appStrings = { filters: "Filters", filtersActive: "some filters are active", tags: "Tags", - newFile: "New File", - newFileConfirm: "Discard current data and load a new archive file?", + tools: "Tools", }, lightbox: { next: "Next image", @@ -50,8 +48,8 @@ const appStrings = { cache. Posts that are not in your instance cache any more are not included in your archive. This affects boosts, likes, and bookmarks.`, rawData: "Raw data {fileName}", - favorites: "Favorites", - favoritesEmpty: "no favorites", + likes: "Favorites", + likesEmpty: "no favorites", bookmarks: "Bookmarks", bookmarksEmpty: "no bookmarks", }, @@ -118,7 +116,7 @@ const appStrings = { posts: { panelTitle: "Posts", noResults: "No results for the specified filters", - noPostsError: "No posts found in archive (?!)", + noPostsError: "No posts found in archive", }, post: { by: "by", @@ -140,6 +138,21 @@ const appStrings = { mentionsFilter: "Filter mentions", boostsFilter: "Filter boosted users", }, + tools: { + panelTitle: "Tools", + appSettings: "App settings", + selectLanguage: "Select language", + useDarkTheme: "Use dark theme", + useLightTheme: "Use light theme", + loadedFiles: "Loaded files", + addAnother: "Add another archive", + addAnotherTip: + "Tip: You can open multiple archives at once.
    You can also drag and drop your archive files anywhere on this window.", + startOver: "Start over", + startOverConfirm: "Discard current data and load a new archive file?", + appLog: "App log", + projectPage: `Project page (github)`, + }, }, fr: { @@ -154,129 +167,142 @@ const appStrings = { p3: `Commencez par ouvrir votre archive avec MARL.
    Vous pouvez la glisser-déposer n'importe où sur cette page, ou {labelStart}cliquer ici pour la sélectionner{labelEnd}.`, - projectPage: `Page du project (github)`, }, misc: { - loading: "Loading", - closePanelBtn: "Close panel", + loading: "Chargement", + closePanelBtn: "Fermer le panneau", }, menu: { - profile: "Profile", - filters: "Filters", - filtersActive: "some filters are active", + profile: "Profil", + filters: "Filtres", + filtersActive: "certains filtres sont actifs", tags: "Tags", - newFile: "New File", - newFileConfirm: "Discard current data and load a new archive file?", + tools: "Outils", }, lightbox: { - next: "Next image", - prev: "Previous image", - close: "Close image", + next: "Image suivante", + prev: "Image précédente", + close: "Fermer l'image", }, actor: { - accountInfo: "Account info", - accounts: "Accounts", - noAvatarImage: "No avatar image", - noHeaderImage: "No header image", - headerImage: "Header", - memberSince: "Member since", + accountInfo: "Infos du compte", + accounts: "Comptes", + noAvatarImage: "Pas d'avatar", + noHeaderImage: "pas d'image d'en-tête", + headerImage: "En-tête", + memberSince: "Membre depuis", countPosts: "posts", - countInArchive: "in archive", - countDiffWhy: "Why are those two numbers different?", - countDiffExplanation: `Posts that are not directly hosted on your instance are kept - in a cache by your instance for a given time, after what they are deleted from that - cache. Posts that are not in your instance cache any more are not included in your - archive. This affects boosts, likes, and bookmarks.`, - rawData: "Raw data {fileName}", - favorites: "Favorites", - favoritesEmpty: "no favorites", - bookmarks: "Bookmarks", - bookmarksEmpty: "no bookmarks", + countInArchive: "dans l'archive", + countDiffWhy: "Pourquoi ces deux nombres sont-ils différents ?", + countDiffExplanation: `Les posts qui ne sont pas hébergés directement sur votre instance + sont gardés en cache par celle-ci pour une durée limitée, après quoi ils sont supprimés + de ce cache. Les posts qui ne sont plus présents dans le cache de votre instance ne sont + pas inclus dans votre archive. Cela concerne les partages, les favoris et les marque-pages.`, + rawData: "Données brutes {fileName}", + likes: "Favoris", + likesEmpty: "aucun favori", + bookmarks: "Marque-pages", + bookmarksEmpty: "aucun marque-page", }, filters: { - panelTitle: "Filter posts", - panelNotice: `The list of posts will be automatically updated based on the active - filters below.`, - fullText: "Full text", + panelTitle: "Filtrer les posts", + panelNotice: `La liste des posts sera automatiquement mise à jour en fonction des filtres + activés ci-dessous.`, + fullText: "Partout", hashtagText: "Hashtags", mentionText: "Mentions", - externalLink: "External links", - summary: "Summary (CW)", - isEdited: "Has been edited", - isDuplicate: "Non-exact duplicates", + externalLink: "Liens externes", + summary: "Avertissement de contenu", + isEdited: "A été modifié", + isDuplicate: "Doublons imparfaits", - mustContain: "Must contain", + mustContain: "Doit contenir", hasHashtags: "Hashtag(s)", hasMentions: "Mention(s)", - hasExternalLink: "External link(s)", - hasSummary: "Summary (CW)", + hasExternalLink: "Lien(s) externe(s)", + hasSummary: "Avertissement de contenu", type: "Type", - typeOriginal: "Original posts (incl. replies)", - typeBoost: "Boosts", - noStartingAt: 'Does not start with "@"', - isSensitive: "Marked as sensitive", + typeOriginal: "Posts originaux (y.c. réponses)", + typeBoost: "Partages", + noStartingAt: 'Ne commence pas par "@"', + isSensitive: "Marqué comme sensible", - mustHaveAttachement: "Must have attachment", - attachmentAny: "Any type", + mustHaveAttachement: "Doit avoir un fichier joint", + attachmentAny: "N'importe quel type", attachmentImage: "Image(s)", - attachmentVideo: "Video(s)", - attachmentSound: "Sound(s)", - attachmentNoAltText: "Without alt text", - attachmentWithAltText: "With alt text", + attachmentVideo: "Vidéo(s)", + attachmentSound: "Son(s)", + attachmentNoAltText: "Sans description alternative", + attachmentWithAltText: "Avec description alternative", - visibility: "Visibility", + visibility: "Confidentialité", visibilityPublic: "Public", - visibilityUnlisted: "Unlisted", - visibilityFollowers: "Followers only", - visibilityMentioned: "Mentioned people only", + visibilityUnlisted: "Public discret", + visibilityFollowers: "Abonnés", + visibilityMentioned: "Personnes spécifiques", - language: "Language", - author: "Author", + language: "Langue", + author: "Auteur", - resetFilters: "Reset filters", + resetFilters: "Réinitialiser les filtres", }, header: { countLabel: "posts", - oldestFirst: "oldest first", - latestFirst: "latest first", - reverse: "Reverse", - loadNewFile: "Load new file", + oldestFirst: "les plus anciens d'abord", + latestFirst: "les plus récents d'abord", + reverse: "Inverser", + loadNewFile: "Charger un nouveau fichier", }, paging: { - first: "First", - prev: "Prev", - next: "Next", - last: "Last", - pagingOptions: "Paging options", + first: "Première", + prev: "Précédente", + next: "Suivante", + last: "Dernière", + pagingOptions: "Options de pagination", page: "Page", - postsPerPage: "posts per page", - reverseOrder: "Reverse order", + postsPerPage: "posts par page", + reverseOrder: "Inverser l'ordre", }, posts: { panelTitle: "Posts", - noResults: "No results for the specified filters", - noPostsError: "No posts found in archive (?!)", + noResults: "Pas de résultats pour les filtres spécifiés", + noPostsError: "Aucun post trouvé dans l'archive", }, post: { - by: "by", - lastUpdated: "Last updated", - linkToPost: "link", - attachmentNoAlt: "No description provided", - attachmentInArchive: "In archive:", - people: "People", + by: "par", + lastUpdated: "Dernière modification", + linkToPost: "lien", + attachmentNoAlt: "Aucune description fournie", + attachmentInArchive: "Dans l'archive :", + people: "Personnes", hashtags: "Hashtags", - extLinks: "External links", - rawData: "Raw data", + extLinks: "Liens externes", + rawData: "Données brutes", }, tags: { panelTitle: "Tags", hashtags: "Hashtags", mentions: "Mentions", - boosts: "Boosted users", - hashtagsFilter: "Filter hashtags", - mentionsFilter: "Filter mentions", - boostsFilter: "Filter boosted users", + boosts: "Utilisateurs partagés", + hashtagsFilter: "Filtrer les hashtags", + mentionsFilter: "Filtrer les mentions", + boostsFilter: "Filter utilisateurs partagés", + }, + tools: { + panelTitle: "Outils", + appSettings: "Réglages de l'app", + selectLanguage: "Choisir la langue", + useDarkTheme: "Utiliser le thème sombre", + useLightTheme: "Utiliser le thème clair", + loadedFiles: "Fichiers chargés", + addAnother: "Ajouter une autre archive", + addAnotherTip: + "Astuce: Vous pouvez ouvrir plusieurs archives en même temps.
    Vous pouvez aussi glisser-déposer vos fichiers d'archive n'importe où dans cette fenêtre.", + startOver: "Recommencer", + startOverConfirm: "Repartir de zéro et charger un nouveau fichier ?", + appLog: "Journal", + projectPage: `Page du project (github)`, }, }, }; diff --git a/dist/js/utils.js b/dist/js/utils.js new file mode 100644 index 0000000..a8448cf --- /dev/null +++ b/dist/js/utils.js @@ -0,0 +1,678 @@ +function resetStores() { + Alpine.store("files").resetState(); + Alpine.store("lightbox").resetState(); + Alpine.store("ui").resetState(); +} + +function unZip(files) { + const firstLoad = Alpine.store("files").sources.length === 0; + if (firstLoad) { + resetStores(); + } + Alpine.store("files").loading = true; + + for (let i = 0; i < files.length; i++) { + const file = files[i]; + + if ( + Alpine.store("files").sources.some((source) => { + return ( + source.fileInfos.name === file.name && + source.fileInfos.size === file.size && + source.fileInfos.lastModified === file.lastModified + ); + }) + ) { + const msg = `File already loaded: ${file.name}`; + console.warn(msg); + Alpine.store("ui").logMsg(msg, "warn"); + continue; + } + + Alpine.store("ui").logMsg(`Loading file: ${file.name}`, "info"); + + JSZip.loadAsync(file).then( + (content) => { + const index = Alpine.store("files").sources.length; + const fileInfos = { + name: file.name, + size: file.size, + lastModified: file.lastModified, + }; + + Alpine.store("files").sources[index] = { + id: index, + fileInfos: fileInfos, + nbToots: 0, + + actor: {}, + outbox: {}, + likes: [], + bookmarks: [], + avatar: {}, + header: {}, + + loaded: { + actor: false, + avatar: false, + header: false, + outbox: false, + likes: false, + bookmarks: false, + }, + }; + + Alpine.store("files").sources[index]._raw = content.files; + + loadJsonFile("actor", index, fileInfos); + loadJsonFile("outbox", index, fileInfos); + loadJsonFile("likes", index, fileInfos); + loadJsonFile("bookmarks", index, fileInfos); + }, + (error) => { + const msg = `Error loading ${file.name}: ${error.message}`; + console.error(msg); + Alpine.store("ui").logMsg(msg, "error"); + } + ); + } +} + +function loadJsonFile(name, index, fileInfos) { + const content = Alpine.store("files").sources[index]._raw; + + if (content[name + ".json"] === undefined) { + if (name === "likes" || name === "bookmarks") { + // we can still run the app without those files + const msg = `${fileInfos.name}: File ${name}.json not found in archive.`; + console.warn(msg); + Alpine.store("ui").logMsg(msg, "warn"); + Alpine.store("files").sources[index].loaded[name] = true; + } else { + // this should NOT happen and will prevent the app from running + const msg = `Critical error - ${fileInfos.name}: File ${name}.json not found in archive.`; + console.error(msg); + Alpine.store("ui").logMsg(msg, "error"); + } + return; + } + + content[name + ".json"].async("text").then(function (txt) { + if (name === "actor") { + Alpine.store("files").sources[index].actor = JSON.parse(txt); + loadActorImages(index); + Alpine.store("files").sources[index].loaded.actor = true; + } // actor.json + + if (name === "outbox") { + let data = JSON.parse(txt); + + let toots = data.orderedItems.reduce((accu, t) => { + let t2 = preprocessToots(t, index); + if (t2) { + accu.push(t2); + } + return accu; + }, []); + + Alpine.store("files").toots = Alpine.store("files").toots.concat(toots); + Alpine.store("files").sources[index].nbToots = toots.length; + delete data.orderedItems; + Alpine.store("files").sources[index].outbox = data; + Alpine.store("files").sources[index].loaded.outbox = true; + } // outbox.json + + if (name === "likes" || name === "bookmarks") { + const tmp = JSON.parse(txt); + Alpine.store("files").sources[index][name] = tmp.orderedItems; + Alpine.store("files").sources[index].loaded[name] = true; + } // likes.json || bookmarks.json + }); +} + +function buildTootsInfos() { + let langs = {}; + let boosts = []; + + if (Alpine.store("files").toots.length > 0) { + let infos = Alpine.store("files").toots.reduce( + (accu, toot) => { + for (let lang in toot._marl.langs) { + const l = toot._marl.langs[lang]; + if (!accu.langs[l]) { + accu.langs[l] = 1; + } else { + accu.langs[l]++; + } + } + + if (toot.type === "Announce") { + // since Mastodon doesn't allow (yet?) cross-origin requests to + // retrieve post data (for boosts), we try to at least extract the + // user names for all the boosts contained in the archive + + // [ISSUE] "object" value is a string most of the times, but + // sometimes it's a complex object similar to type "Create" + if (typeof toot.object === "object" && toot.object !== null) { + // let's ignore this case for now... + // [TODO], but not clear how it should be handled + } else if (toot.object) { + // if it's not an object and it has a value, then it's simply a + // url (string) pointing to the original (boosted) post. + // [ISSUE] URL format not always consistent... (esp. in the case + // of non-Mastodon instances) - e.g: + // https://craftopi.art/objects/[...] + // https://firefish.city/notes/[...] + // https://bsky.brid.gy/convert/ap/at://did:plc:[...]/app.bsky.feed.post/[...] + // -> the user name is not always present in URL + const url = toot.object.split("/"); + let name; + let user; + let domain; + if (url.length > 2) { + domain = url[2]; + + if (url[0] === "https:" && url[3] === "users" && url[5] === "statuses") { + // Mastodon URL format -> user name + name = url[4]; + user = `https://${url[2]}/users/${url[4]}/`; + } else { + // other URL format -> domain name + name = `? ${url[2]}`; + user = `https://${url[2]}/`; + } + + if (!accu.boosts[name]) { + accu.boosts[name] = { + nb: 1, + name: name, + url: user, + domain: domain, + }; + } else { + accu.boosts[name].nb++; + } + } + } + } + return accu; + }, + { langs: {}, boosts: {} } + ); + + langs = infos.langs; + + boosts = []; + for (var key in infos.boosts) { + boosts.push(infos.boosts[key]); + } + } + + Alpine.store("files").languages = langs; + Alpine.store("files").boostsAuthors = boosts; +} + +function buildDynamicFilters() { + for (const lang in Alpine.store("files").languages) { + Alpine.store("files").filtersDefault["lang_" + lang] = true; + } + + for (const source of Alpine.store("files").sources) { + Alpine.store("files").filtersDefault["actor_" + source.id] = true; + } + + Alpine.store("files").resetFilters(false); +} + +function preprocessToots(t, index) { + // build the '_marl' prop for each toot + let marl = { + langs: [], + source: index, + }; + + // check for duplicates (in case of multiple archive files) + if (Alpine.store("files").toc.includes(t.id)) { + const alts = Alpine.store("files").toots.filter((t2) => t2.id === t.id); + + let identical = false; + const flat1 = JSON.stringify(t); + + alts.forEach((alt) => { + let alt2 = JSON.parse(JSON.stringify(alt)); + delete alt2._marl; + const flat2 = JSON.stringify(alt2); + + if (flat1 === flat2) { + identical = true; + } else { + alt._marl.duplicate = true; + marl.duplicate = true; + Alpine.store("files").duplicates = true; + } + }); + if (identical) { + return false; + } + } else { + Alpine.store("files").toc.push(t.id); + } + + if (t.type === "Create") { + if (typeof t.object === "object" && t.object !== null && t.object.contentMap) { + let langs = []; + for (let lang in t.object.contentMap) { + langs.push(lang); + } + marl.langs = langs; + } else { + marl.langs = ["undefined"]; + } + } + + if (typeof t.object === "object" && t.object !== null) { + if (t.object.content) { + const content = t.object.content.toLowerCase(); + marl.textContent = stripHTML(content); + marl.externalLinks = extractExternalLinks(content); + } + if (t.object.summary) { + marl.summary = t.object.summary.toLowerCase(); + } + + if (t.object.attachment && t.object.attachment.length) { + marl.hasAttachments = true; + } + } else if (t.object) { + marl.textContent = t.object.toLowerCase(); + } + + marl.visibility = tootVisibility(t); + + const id = t.id.split("/"); + marl.id = id[id.length - 2]; + + t._marl = marl; + return t; +} + +function loadActorImages(index) { + const actor = Alpine.store("files").sources[index].actor; + const content = Alpine.store("files").sources[index]._raw; + + if (actor.icon && actor.icon.type === "Image" && actor.icon.url && content[actor.icon.url]) { + const image = actor.icon; + content[image.url].async("base64").then(function (content) { + Alpine.store("files").sources[index].avatar = { + type: image.mediaType, + content: content, + noImg: false, + }; + Alpine.store("files").sources[index].loaded.avatar = true; + }); + } else { + Alpine.store("files").sources[index].avatar = { noImg: true }; + Alpine.store("files").sources[index].loaded.avatar = true; + } + + if (actor.image && actor.image.type === "Image" && actor.image.url && content[actor.image.url]) { + const image = actor.image; + content[image.url].async("base64").then(function (content) { + Alpine.store("files").sources[index].header = { + type: image.mediaType, + content: content, + noImg: false, + }; + Alpine.store("files").sources[index].loaded.header = true; + }); + } else { + Alpine.store("files").sources[index].header = { noImg: true }; + Alpine.store("files").sources[index].loaded.header = true; + } +} + +function setHueForSources() { + const nbSources = Alpine.store("files").sources.length; + const hueStart = Math.round(Math.random() * 360); // MARL accent: 59.17 + const hueSpacing = Math.round(360 / nbSources); + + for (let i = 0; i < nbSources; i++) { + Alpine.store("files").sources[i].hue = hueStart + hueSpacing * i; + } +} + +function checkAppReady(ok) { + if (ok) { + buildTootsInfos(); + buildDynamicFilters(); + cleanUpRaw(); + setHueForSources(); + document.getElementById("main-section").focus(); + Alpine.store("ui").checkMenuState(); + Alpine.store("files").sortToots(); + Alpine.store("files").loading = false; + Alpine.store("files").someFilesLoaded = true; + } +} + +function cleanUpRaw() { + for (let i = 0; i < Alpine.store("files").sources.length; i++) { + const content = Alpine.store("files").sources[i]._raw; + if (content.cleanedUp) { + continue; + } + + const actor = Alpine.store("files").sources[i].actor; + if (actor.image && actor.image.url) { + delete content[actor.image.url]; + } + if (actor.icon && actor.icon.url) { + delete content[actor.icon.url]; + } + delete content["actor.json"]; + delete content["outbox.json"]; + delete content["likes.json"]; + delete content["bookmarks.json"]; + content.cleanedUp = true; + + Alpine.store("files").sources[i]._raw = content; + } +} + +function loadAttachedMedia(att, index) { + if (attachmentIsImage(att) || attachmentIsVideo(att) || attachmentIsSound(att)) { + const data = Alpine.store("files").sources[index]._raw; + let url = att.url; + // ?! some instances seem to add their own name in front of the path, + // resulting in an invalid path with relation to the archive + // structure (e.g. "/framapiaf/media_attachments/...", but in the + // archive there is only a folder "/media_attachments") + // => So we remove everything that comes before "media_attachments/", + // hoping it doesn't break something else... :/ + const prefix = url.indexOf("media_attachments/"); + if (prefix > 0) { + url = url.slice(prefix); + } + if (!data[url]) { + return; + } + data[url].async("base64").then((content) => { + Alpine.store("files").sources[index][att.url] = { + type: att.mediaType, + content: content, + }; + }); + } +} + +function pagingUpdated() { + document.querySelectorAll(`#toots details[open]`).forEach((e) => { + e.removeAttribute("open"); + }); +} + +function scrollTootsToTop(setFocusTo) { + setTimeout(() => { + document.getElementById("toots").scrollTop = 0; + if (setFocusTo) { + // for keyboard users: we transfer the focus to the corresponding button + // in the upper paging module; or, in the cases where said button is + // disabled, we set the focus on the list of posts. + document.getElementById(setFocusTo).focus(); + } + }, 50); +} + +function contentType(data) { + let r = ""; + switch (data) { + case "Create": + r = "Post"; + break; + case "Announce": + r = "Boost"; + break; + } + return r; +} + +function tootVisibility(data) { + if (data.to.includes("https://www.w3.org/ns/activitystreams#Public")) { + return ["public", AlpineI18n.t("filters.visibilityPublic")]; + } + if ( + data.to.some((x) => x.indexOf("/followers") > -1) && + !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && + data.cc.includes("https://www.w3.org/ns/activitystreams#Public") + ) { + return ["unlisted", AlpineI18n.t("filters.visibilityUnlisted")]; + } + if ( + data.to.some((x) => x.indexOf("/followers") > -1) && + !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && + !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") + ) { + return ["followers", AlpineI18n.t("filters.visibilityFollowers")]; + } + if ( + !data.to.some((x) => x.indexOf("/followers") > -1) && + !data.to.includes("https://www.w3.org/ns/activitystreams#Public") && + !data.cc.includes("https://www.w3.org/ns/activitystreams#Public") + ) { + return ["mentioned", AlpineI18n.t("filters.visibilityMentioned")]; + } +} + +function tootHasTags(toot) { + return typeof toot.object === "object" && toot.object !== null && toot.object.tag && toot.object.tag.length; +} + +function formatJson(data) { + let r = data; + if (r._marl) { + // not a part of the source data; let's hide it to avoid confusion + r = JSON.parse(JSON.stringify(data)); + delete r._marl; + } + return JSON.stringify(r, null, 4); +} + +function formatAuthor(author, plainText) { + if (plainText) { + return author.split("/").pop(); + } else { + return `${author.split("/").pop()}`; + } +} + +function formatDateTime(data) { + let date = new Date(data); + const dateOptions = { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + hour: "numeric", + minute: "2-digit", + second: "2-digit", + }; + return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); +} + +function formatFileDateTime(data) { + let date = new Date(data); + const dateOptions = { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour12: false, + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + }; + return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); +} +function formatFileSize(size) { + var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)); + return +(size / Math.pow(1024, i)).toFixed(2) * 1 + " " + ["B", "kB", "MB", "GB", "TB"][i]; // ### i18n +} + +function formatDate(data) { + let date = new Date(data); + const dateOptions = { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }; + return date.toLocaleDateString(Alpine.store("ui").lang, dateOptions); +} + +function formatNumber(nb) { + return nb.toLocaleString(); +} + +function formatLikesBookmarks(url) { + const u = url.split("/"); + u.splice(0, 2); + + // 0 [domain] + // 1 "users" + // 2 [username] + // 3 "statuses" + // 4 [post id] + + let text = `${u[0]}`; + if (u[1] === "users" && u[3] === "statuses") { + text += `${u[2]}${u[4]}`; + } else { + u.splice(0, 1); + text += `${u.join("/")}`; + } + return text; +} + +function stripHTML(str) { + let doc = new DOMParser().parseFromString(str, "text/html"); + return doc.body.textContent || ""; +} + +function extractExternalLinks(str) { + const doc = new DOMParser().parseFromString(str, "text/html"); + const nodes = doc.querySelectorAll("a[href]:not(.mention)"); + let links = []; + nodes.forEach((link) => { + links.push({ + href: link.href, + text: link.textContent, + }); + }); + return links; +} + +function attachmentIsImage(att) { + return att.mediaType === "image/jpeg" || att.mediaType === "image/png"; +} + +function attachmentIsVideo(att) { + return att.mediaType === "video/mp4"; +} + +function attachmentIsSound(att) { + return att.mediaType === "audio/mpeg"; +} + +function attachmentWrapperClass(att) { + let r = []; + if (attachmentIsImage(att)) { + r.push("att-img"); + } else if (attachmentIsSound(att)) { + r.push("att-sound"); + } else if (attachmentIsVideo(att)) { + r.push("att-video"); + } + + if (!att.name) { + r.push("no-alt-text"); + } + + return r; +} + +function isFilterActive(name) { + return Alpine.store("files").filters[name] !== Alpine.store("files").filtersDefault[name]; +} + +function startOver() { + const txt = AlpineI18n.t("tools.startOverConfirm"); + if (confirm(txt)) { + location.reload(); + } +} + +function detectLangFromBrowser() { + const langs = navigator.languages; + if (langs && langs.length) { + for (let i = 0; i < langs.length; i++) { + let lang = langs[i].split("-")[0]; + if (Alpine.store("ui").appLangs[lang]) { + const msg = `Setting language based on browser preference: '${lang}' (${ + Alpine.store("ui").appLangs[lang] + })`; + Alpine.store("ui").logMsg(msg, "info"); + return lang; + } + } + } + return false; +} + +function setLang() { + const lang = Alpine.store("ui").lang; + AlpineI18n.locale = lang; + Alpine.store("userPrefs").save("lang", lang); + + const msg = `App language set to '${lang}' (${Alpine.store("ui").appLangs[lang]})`; + Alpine.store("ui").logMsg(msg); +} + +function setTheme(theme) { + document.getElementsByTagName("html")[0].setAttribute("class", theme); +} + +// drag'n'drop over entire page + +const drag = { + el: null, + + init(el) { + this.dropArea = document.getElementById(el); + + ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { + this.dropArea.addEventListener(eventName, (e) => this.preventDragDefaults(e), false); + }); + ["dragenter", "dragover"].forEach((eventName) => { + this.dropArea.addEventListener(eventName, () => this.highlightDrag(), false); + }); + ["dragleave", "drop"].forEach((eventName) => { + this.dropArea.addEventListener(eventName, () => this.unhighlightDrag(), false); + }); + this.dropArea.addEventListener("drop", (e) => this.handleDrop(e), false); + }, + + preventDragDefaults(e) { + e.preventDefault(); + e.stopPropagation(); + }, + highlightDrag() { + this.dropArea.classList.add("highlight-drag"); + }, + unhighlightDrag() { + this.dropArea.classList.remove("highlight-drag"); + }, + handleDrop(e) { + const dt = e.dataTransfer; + const files = dt.files; + unZip(files); + }, +}; diff --git a/readme.md b/readme.md index aa1fa99..09f67af 100644 --- a/readme.md +++ b/readme.md @@ -121,6 +121,9 @@ Mastodon: https://lou.lt/@s427 ## Version history +- v. 2.1 + - Internationalization: MARL is now usable in two languages (English and French), with support for more languages. Get in touch if you want to help translating MARL in your language! + - Tools panel: a new "Tools" button will a new panel, where you can change various settings for the app (language, theme) as well as see information about your loaded files, messages from the app, and an "about" section. - v. 2.0 - code refactoring: MARL now uses Astro to build its HTML code as well as optimize and bundle its assets (CSS, SVG, images). - all assets used to build MARL are now stored in the `dev` folder.