Forocoches Premium

Improves Forocoches thread reading

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Advertisement:

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

Advertisement:

// ==UserScript==
// @name         Forocoches Premium
// @namespace    http://tampermonkey.net/
// @version      2026-06-12-1
// @description  Improves Forocoches thread reading
// @author       victor141516
// @match        https://forocoches.com/foro/*
// @icon         https://forocoches.com/favicon.ico
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(() => {
  // src/config/domIds.ts
  var STYLE_ID = "fc-premium-style";
  var INSTANCE_KEY = "__fcPremiumThreadEnhancerStarted";
  var SHORTCUT_HELP_CONTAINER_ID = "fc-premium-shortcut-help-container";
  var SHORTCUT_HELP_BUTTON_ID = "fc-premium-shortcut-help-button";
  var SHORTCUT_HELP_POPOVER_ID = "fc-premium-shortcut-help-popover";
  var HIDDEN_THREADS_BUTTON_ID = "fc-premium-hidden-threads-button";
  var HIDDEN_THREADS_MODAL_ID = "fc-premium-hidden-threads-modal";
  var HIDDEN_THREADS_MODAL_BODY_ID = "fc-premium-hidden-threads-modal-body";
  var MODAL_OPEN_CLASS = "fc-premium-modal-open";
  var TOP_TAGS_ID = "fc-premium-top-tags";
  var FORUM_SIDEBAR_TOGGLE_BAR_ID = "fc-premium-forum-sidebar-toggle-bar";
  var FORUM_SIDEBAR_TOGGLE_ID = "fc-premium-forum-sidebar-toggle";
  var FORUM_CONTROLS_ROW_ID = "fc-premium-forum-controls-row";
  var FORUM_SEARCH_SLOT_ID = "fc-premium-forum-search-slot";
  var FORUM_LOADING_STATUS_ID = "fc-premium-forum-loading-status";
  var THREAD_PROGRESS_ID = "fc-premium-thread-progress";
  var NAVIGATION_STATUS_ID = "fc-premium-navigation-status";
  var THREAD_SUMMARY_ID = "fc-premium-thread-summary";
  var THREAD_CONTROLS_ID = "fc-premium-thread-controls";
  var THREAD_SEARCH_PANEL_ID = "fc-premium-thread-search-panel";
  var THREAD_SEARCH_HEADER_SLOT_ID = "fc-premium-thread-search-header-slot";
  var THREAD_SEARCH_TEXT_INPUT_ID = "fc-premium-thread-search-text";
  var THREAD_SEARCH_AUTHOR_INPUT_ID = "fc-premium-thread-search-author";
  var THREAD_SEARCH_AUTHOR_DATALIST_ID = "fc-premium-thread-search-authors";
  var THREAD_SEARCH_SELECTED_AUTHORS_ID = "fc-premium-thread-search-selected-authors";
  var THREAD_SEARCH_STATUS_ID = "fc-premium-thread-search-status";
  var THREAD_SEARCH_EMPTY_ID = "fc-premium-thread-search-empty";
  var FORUM_SIDEBAR_HIDDEN_CLASS = "fc-premium-forum-sidebar-hidden";
  // src/config/keyboard.ts
  var KEY_NAV_PREVIOUS_POST = "ArrowUp";
  var KEY_NAV_NEXT_POST = "ArrowDown";
  var KEY_NAV_PREVIOUS_PAGE = "ArrowLeft";
  var KEY_NAV_NEXT_PAGE = "ArrowRight";
  var KEY_CLEAR_ACTIVE_VIEW = "Escape";
  var KEY_OPEN_SHORTCUT_HELP = "?";
  var KEY_QUOTE_SELECTED_POST = "Enter";
  var KEY_OPEN_SELECTED_THREAD_IN_NEW_TAB = "Enter";
  var KEY_OPEN_SELECTED_THREAD_IN_NEW_TAB_MODIFIER = "Cmd/Ctrl";
  var KEY_HIDE_SELECTED_THREAD = "h";
  var KEY_NEW_THREAD_REPLY = "n";
  var KEY_MULTIQUOTE_SELECTED_POST = "m";
  var KEY_RETURN_TO_THREAD_LIST = "l";
  // src/config/cache.ts
  var FORUM_SIDEBAR_STORAGE_KEY = "fcPremiumForumSidebarHidden";
  var THREAD_CACHE_MAX_AGE_MS = 10 * 60 * 1000;
  var THREAD_CACHE_MAX_BYTES = 500 * 1024 * 1024;
  var THREAD_CACHE_DB_NAME = "fcPremiumThreadCache";
  var THREAD_CACHE_DB_VERSION = 3;
  var THREAD_CACHE_STORE_NAME = "threads";
  var FORUM_THREAD_CACHE_STORE_NAME = "forumThreads";
  var THREAD_CACHE_RECORD_VERSION = 3;
  var FORUM_THREAD_CACHE_RECORD_VERSION = 1;
  var FORUM_THREAD_CACHE_RECENT_PAGES = 10;
  var FORUM_THREAD_CACHE_MAX_RECORDS = 1000;
  var FORUM_THREAD_FALLBACK_PAGE_SIZE = 40;
  var FORUM_LIVE_SEARCH_DEBOUNCE_MS = 220;
  var THREAD_CACHE_LEGACY_STORAGE_PREFIX = "fcPremiumThreadCache:";
  // src/config/query.ts
  var THREAD_STATE_QUERY_PARAMS = {
    graphType: "fcp_graph",
    graphRoot: "fcp_root",
    graphRelated: "fcp_related",
    pageFilter: "fcp_page",
    authorFilter: "fcp_author",
    searchQuery: "fcp_search"
  };
  var LEGACY_THREAD_STATE_QUERY_PARAMS = ["fcp_mode"];
  var FORUM_STATE_QUERY_PARAMS = {
    tag: "fcp_tag"
  };
  // src/config/selectors.ts
  var SELECTED_ATTRIBUTE = "data-fc-premium-selected";
  var FORUM_LAYOUT_HIDDEN_ATTRIBUTE = "data-fc-premium-layout-hidden";
  var POSTS_SELECTOR = "#posts";
  var POST_TABLE_SELECTOR = "table[id^='post']";
  var THREAD_TITLE_SELECTOR = "a[id^='thread_title_'][href*='showthread.php?t=']";
  var HIDDEN_THREAD_ATTRIBUTE = "data-fc-premium-tag-hidden";
  var HIDDEN_POST_FILTER_ATTRIBUTE = "data-fc-premium-filter-hidden";
  var HIDDEN_POST_PAGE_ATTRIBUTE = "data-fc-premium-page-hidden";
  // src/config/domain.ts
  var SCRIPT_INSTANCE_VERSION = "2026-06-09-19";
  var PAGE_LOAD_DELAY_MS = 250;
  var GRAPH_VIEW_TYPES = ["quoted-sources", "quoted-by", "conversation"];
  // src/shared/dom.ts
  function normalizeText(text) {
    return (text || "").replace(/\s+/g, " ").trim();
  }
  function normalizeLayoutText(text) {
    return normalizeText(text).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
  }
  function normalizeAuthorName(author) {
    return normalizeText(author).toLowerCase();
  }
  function sleep(ms) {
    return new Promise((resolve) => window.setTimeout(resolve, ms));
  }
  function isVisible(element) {
    const rect = element.getBoundingClientRect();
    const style = getComputedStyle(element);
    return rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden";
  }
  function toUrl(href) {
    try {
      return new URL(href, location.href);
    } catch (_error) {
      return null;
    }
  }
  function getThreadId(url) {
    return url.searchParams.get("t");
  }
  function getPostQueryId(url) {
    return url.searchParams.get("p");
  }
  function getPageNumber(url) {
    const page = Number(url.searchParams.get("page") || "1");
    return Number.isFinite(page) && page > 0 ? page : 1;
  }
  function getLocationPostHashId(url = new URL(location.href)) {
    const match = url.hash.match(/^#post(\d+)$/);
    return match?.[1] || null;
  }
  function isThreadPage() {
    return location.pathname.endsWith("/showthread.php") && Boolean(getThreadId(new URL(location.href)) || getPostQueryId(new URL(location.href)));
  }
  function isForumDisplayPage() {
    return location.pathname.endsWith("/forumdisplay.php");
  }
  function getForumId(url = new URL(location.href)) {
    return url.searchParams.get("f") || "";
  }

  // src/ui/navigationDom.ts
  var THREAD_NAVIGATION_TOP_OFFSET_RATIO = 0.15;
  function getThreadTitleNavigationItems() {
    const items = [];
    for (const link of document.querySelectorAll(THREAD_TITLE_SELECTOR)) {
      if (!(link instanceof HTMLAnchorElement) || !isVisible(link)) {
        continue;
      }
      items.push({
        element: getThreadNavigationOwner(link),
        link
      });
    }
    return items;
  }
  function getPostNavigationItems(posts) {
    if (!posts) {
      return [];
    }
    const items = [];
    for (const wrapper of posts.querySelectorAll(".fc-premium-post-wrapper")) {
      if (!(wrapper instanceof HTMLElement) || !isVisible(wrapper)) {
        continue;
      }
      const postId = getPostIdFromNavigationElement(wrapper);
      const link = postId ? wrapper.querySelector(`#postcount${postId}`) : wrapper.querySelector("a[id^='postcount']");
      items.push({
        element: wrapper,
        link: link instanceof HTMLAnchorElement ? link : null
      });
    }
    return items;
  }
  function clearNavigationSelection() {
    for (const selected of document.querySelectorAll(`[${SELECTED_ATTRIBUTE}]`)) {
      selected.removeAttribute(SELECTED_ATTRIBUTE);
    }
  }
  function markNavigationItemSelected(item) {
    item.element.setAttribute(SELECTED_ATTRIBUTE, "true");
  }
  function scrollNavigationElementIntoView(element, block) {
    if (block === "start") {
      scrollElementToViewportOffset(element, THREAD_NAVIGATION_TOP_OFFSET_RATIO);
      return;
    }
    element.scrollIntoView({
      behavior: "smooth",
      block
    });
  }
  function scrollElementToViewportOffset(element, offsetRatio) {
    const targetTop = element.getBoundingClientRect().top + window.scrollY - window.innerHeight * offsetRatio;
    window.scrollTo({
      top: Math.max(0, targetTop),
      behavior: "smooth"
    });
  }
  function getPostIdFromNavigationElement(element) {
    if (!(element instanceof HTMLElement)) {
      return null;
    }
    const postTable = element.querySelector(POST_TABLE_SELECTOR);
    const postId = postTable?.id.match(/^post(\d+)$/)?.[1];
    return postId || null;
  }
  function getSelectedPostWrapper() {
    const marked = document.querySelector(`.fc-premium-post-wrapper[${SELECTED_ATTRIBUTE}]`);
    return marked instanceof HTMLElement ? marked : null;
  }
  function getThreadNavigationOwner(link) {
    const row = link.closest("tr");
    if (row instanceof HTMLElement) {
      return row;
    }
    return link;
  }

  // node_modules/preact/dist/preact.module.js
  var n;
  var l;
  var u;
  var t;
  var i;
  var r;
  var o;
  var e;
  var f;
  var c;
  var a;
  var s;
  var h;
  var p;
  var v;
  var y;
  var d = {};
  var w = [];
  var _ = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;
  var g = Array.isArray;
  function m(n2, l2) {
    for (var u2 in l2)
      n2[u2] = l2[u2];
    return n2;
  }
  function b(n2) {
    n2 && n2.parentNode && n2.parentNode.removeChild(n2);
  }
  function k(l2, u2, t2) {
    var i2, r2, o2, e2 = {};
    for (o2 in u2)
      o2 == "key" ? i2 = u2[o2] : o2 == "ref" ? r2 = u2[o2] : e2[o2] = u2[o2];
    if (arguments.length > 2 && (e2.children = arguments.length > 3 ? n.call(arguments, 2) : t2), typeof l2 == "function" && l2.defaultProps != null)
      for (o2 in l2.defaultProps)
        e2[o2] === undefined && (e2[o2] = l2.defaultProps[o2]);
    return x(l2, e2, i2, r2, null);
  }
  function x(n2, t2, i2, r2, o2) {
    var e2 = { type: n2, props: t2, key: i2, ref: r2, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: undefined, __v: o2 == null ? ++u : o2, __i: -1, __u: 0 };
    return o2 == null && l.vnode != null && l.vnode(e2), e2;
  }
  function S(n2) {
    return n2.children;
  }
  function C(n2, l2) {
    this.props = n2, this.context = l2;
  }
  function $(n2, l2) {
    if (l2 == null)
      return n2.__ ? $(n2.__, n2.__i + 1) : null;
    for (var u2;l2 < n2.__k.length; l2++)
      if ((u2 = n2.__k[l2]) != null && u2.__e != null)
        return u2.__e;
    return typeof n2.type == "function" ? $(n2) : null;
  }
  function I(n2) {
    if (n2.__P && n2.__d) {
      var u2 = n2.__v, t2 = u2.__e, i2 = [], r2 = [], o2 = m({}, u2);
      o2.__v = u2.__v + 1, l.vnode && l.vnode(o2), q(n2.__P, o2, u2, n2.__n, n2.__P.namespaceURI, 32 & u2.__u ? [t2] : null, i2, t2 == null ? $(u2) : t2, !!(32 & u2.__u), r2), o2.__v = u2.__v, o2.__.__k[o2.__i] = o2, D(i2, o2, r2), u2.__e = u2.__ = null, o2.__e != t2 && P(o2);
    }
  }
  function P(n2) {
    if ((n2 = n2.__) != null && n2.__c != null)
      return n2.__e = n2.__c.base = null, n2.__k.some(function(l2) {
        if (l2 != null && l2.__e != null)
          return n2.__e = n2.__c.base = l2.__e;
      }), P(n2);
  }
  function A(n2) {
    (!n2.__d && (n2.__d = true) && i.push(n2) && !H.__r++ || r != l.debounceRendering) && ((r = l.debounceRendering) || o)(H);
  }
  function H() {
    try {
      for (var n2, l2 = 1;i.length; )
        i.length > l2 && i.sort(e), n2 = i.shift(), l2 = i.length, I(n2);
    } finally {
      i.length = H.__r = 0;
    }
  }
  function L(n2, l2, u2, t2, i2, r2, o2, e2, f2, c2, a2) {
    var s2, h2, p2, v2, y2, _2, g2, m2 = t2 && t2.__k || w, b2 = l2.length;
    for (f2 = T(u2, l2, m2, f2, b2), s2 = 0;s2 < b2; s2++)
      (p2 = u2.__k[s2]) != null && (h2 = p2.__i != -1 && m2[p2.__i] || d, p2.__i = s2, _2 = q(n2, p2, h2, i2, r2, o2, e2, f2, c2, a2), v2 = p2.__e, p2.ref && h2.ref != p2.ref && (h2.ref && J(h2.ref, null, p2), a2.push(p2.ref, p2.__c || v2, p2)), y2 == null && v2 != null && (y2 = v2), (g2 = !!(4 & p2.__u)) || h2.__k === p2.__k ? (f2 = j(p2, f2, n2, g2), g2 && h2.__e && (h2.__e = null)) : typeof p2.type == "function" && _2 !== undefined ? f2 = _2 : v2 && (f2 = v2.nextSibling), p2.__u &= -7);
    return u2.__e = y2, f2;
  }
  function T(n2, l2, u2, t2, i2) {
    var r2, o2, e2, f2, c2, a2 = u2.length, s2 = a2, h2 = 0;
    for (n2.__k = new Array(i2), r2 = 0;r2 < i2; r2++)
      (o2 = l2[r2]) != null && typeof o2 != "boolean" && typeof o2 != "function" ? (typeof o2 == "string" || typeof o2 == "number" || typeof o2 == "bigint" || o2.constructor == String ? o2 = n2.__k[r2] = x(null, o2, null, null, null) : g(o2) ? o2 = n2.__k[r2] = x(S, { children: o2 }, null, null, null) : o2.constructor === undefined && o2.__b > 0 ? o2 = n2.__k[r2] = x(o2.type, o2.props, o2.key, o2.ref ? o2.ref : null, o2.__v) : n2.__k[r2] = o2, f2 = r2 + h2, o2.__ = n2, o2.__b = n2.__b + 1, e2 = null, (c2 = o2.__i = O(o2, u2, f2, s2)) != -1 && (s2--, (e2 = u2[c2]) && (e2.__u |= 2)), e2 == null || e2.__v == null ? (c2 == -1 && (i2 > a2 ? h2-- : i2 < a2 && h2++), typeof o2.type != "function" && (o2.__u |= 4)) : c2 != f2 && (c2 == f2 - 1 ? h2-- : c2 == f2 + 1 ? h2++ : (c2 > f2 ? h2-- : h2++, o2.__u |= 4))) : n2.__k[r2] = null;
    if (s2)
      for (r2 = 0;r2 < a2; r2++)
        (e2 = u2[r2]) != null && (2 & e2.__u) == 0 && (e2.__e == t2 && (t2 = $(e2)), K(e2, e2));
    return t2;
  }
  function j(n2, l2, u2, t2) {
    var i2, r2;
    if (typeof n2.type == "function") {
      for (i2 = n2.__k, r2 = 0;i2 && r2 < i2.length; r2++)
        i2[r2] && (i2[r2].__ = n2, l2 = j(i2[r2], l2, u2, t2));
      return l2;
    }
    n2.__e != l2 && (t2 && (l2 && n2.type && !l2.parentNode && (l2 = $(n2)), u2.insertBefore(n2.__e, l2 || null)), l2 = n2.__e);
    do {
      l2 = l2 && l2.nextSibling;
    } while (l2 != null && l2.nodeType == 8);
    return l2;
  }
  function O(n2, l2, u2, t2) {
    var i2, r2, o2, e2 = n2.key, f2 = n2.type, c2 = l2[u2], a2 = c2 != null && (2 & c2.__u) == 0;
    if (c2 === null && e2 == null || a2 && e2 == c2.key && f2 == c2.type)
      return u2;
    if (t2 > (a2 ? 1 : 0)) {
      for (i2 = u2 - 1, r2 = u2 + 1;i2 >= 0 || r2 < l2.length; )
        if ((c2 = l2[o2 = i2 >= 0 ? i2-- : r2++]) != null && (2 & c2.__u) == 0 && e2 == c2.key && f2 == c2.type)
          return o2;
    }
    return -1;
  }
  function z(n2, l2, u2) {
    l2[0] == "-" ? n2.setProperty(l2, u2 == null ? "" : u2) : n2[l2] = u2 == null ? "" : typeof u2 != "number" || _.test(l2) ? u2 : u2 + "px";
  }
  function N(n2, l2, u2, t2, i2) {
    var r2, o2;
    n:
      if (l2 == "style")
        if (typeof u2 == "string")
          n2.style.cssText = u2;
        else {
          if (typeof t2 == "string" && (n2.style.cssText = t2 = ""), t2)
            for (l2 in t2)
              u2 && l2 in u2 || z(n2.style, l2, "");
          if (u2)
            for (l2 in u2)
              t2 && u2[l2] == t2[l2] || z(n2.style, l2, u2[l2]);
        }
      else if (l2[0] == "o" && l2[1] == "n")
        r2 = l2 != (l2 = l2.replace(s, "$1")), o2 = l2.toLowerCase(), l2 = o2 in n2 || l2 == "onFocusOut" || l2 == "onFocusIn" ? o2.slice(2) : l2.slice(2), n2.l || (n2.l = {}), n2.l[l2 + r2] = u2, u2 ? t2 ? u2[a] = t2[a] : (u2[a] = h, n2.addEventListener(l2, r2 ? v : p, r2)) : n2.removeEventListener(l2, r2 ? v : p, r2);
      else {
        if (i2 == "http://www.w3.org/2000/svg")
          l2 = l2.replace(/xlink(H|:h)/, "h").replace(/sName$/, "s");
        else if (l2 != "width" && l2 != "height" && l2 != "href" && l2 != "list" && l2 != "form" && l2 != "tabIndex" && l2 != "download" && l2 != "rowSpan" && l2 != "colSpan" && l2 != "role" && l2 != "popover" && l2 in n2)
          try {
            n2[l2] = u2 == null ? "" : u2;
            break n;
          } catch (n3) {}
        typeof u2 == "function" || (u2 == null || u2 === false && l2[4] != "-" ? n2.removeAttribute(l2) : n2.setAttribute(l2, l2 == "popover" && u2 == 1 ? "" : u2));
      }
  }
  function V(n2) {
    return function(u2) {
      if (this.l) {
        var t2 = this.l[u2.type + n2];
        if (u2[c] == null)
          u2[c] = h++;
        else if (u2[c] < t2[a])
          return;
        return t2(l.event ? l.event(u2) : u2);
      }
    };
  }
  function q(n2, u2, t2, i2, r2, o2, e2, f2, c2, a2) {
    var s2, h2, p2, v2, y2, d2, _2, k2, x2, M, $2, I2, P2, A2, H2, T2 = u2.type;
    if (u2.constructor !== undefined)
      return null;
    128 & t2.__u && (c2 = !!(32 & t2.__u), o2 = [f2 = u2.__e = t2.__e]), (s2 = l.__b) && s2(u2);
    n:
      if (typeof T2 == "function")
        try {
          if (k2 = u2.props, x2 = T2.prototype && T2.prototype.render, M = (s2 = T2.contextType) && i2[s2.__c], $2 = s2 ? M ? M.props.value : s2.__ : i2, t2.__c ? _2 = (h2 = u2.__c = t2.__c).__ = h2.__E : (x2 ? u2.__c = h2 = new T2(k2, $2) : (u2.__c = h2 = new C(k2, $2), h2.constructor = T2, h2.render = Q), M && M.sub(h2), h2.state || (h2.state = {}), h2.__n = i2, p2 = h2.__d = true, h2.__h = [], h2._sb = []), x2 && h2.__s == null && (h2.__s = h2.state), x2 && T2.getDerivedStateFromProps != null && (h2.__s == h2.state && (h2.__s = m({}, h2.__s)), m(h2.__s, T2.getDerivedStateFromProps(k2, h2.__s))), v2 = h2.props, y2 = h2.state, h2.__v = u2, p2)
            x2 && T2.getDerivedStateFromProps == null && h2.componentWillMount != null && h2.componentWillMount(), x2 && h2.componentDidMount != null && h2.__h.push(h2.componentDidMount);
          else {
            if (x2 && T2.getDerivedStateFromProps == null && k2 !== v2 && h2.componentWillReceiveProps != null && h2.componentWillReceiveProps(k2, $2), u2.__v == t2.__v || !h2.__e && h2.shouldComponentUpdate != null && h2.shouldComponentUpdate(k2, h2.__s, $2) === false) {
              u2.__v != t2.__v && (h2.props = k2, h2.state = h2.__s, h2.__d = false), u2.__e = t2.__e, u2.__k = t2.__k, u2.__k.some(function(n3) {
                n3 && (n3.__ = u2);
              }), w.push.apply(h2.__h, h2._sb), h2._sb = [], h2.__h.length && e2.push(h2);
              break n;
            }
            h2.componentWillUpdate != null && h2.componentWillUpdate(k2, h2.__s, $2), x2 && h2.componentDidUpdate != null && h2.__h.push(function() {
              h2.componentDidUpdate(v2, y2, d2);
            });
          }
          if (h2.context = $2, h2.props = k2, h2.__P = n2, h2.__e = false, I2 = l.__r, P2 = 0, x2)
            h2.state = h2.__s, h2.__d = false, I2 && I2(u2), s2 = h2.render(h2.props, h2.state, h2.context), w.push.apply(h2.__h, h2._sb), h2._sb = [];
          else
            do {
              h2.__d = false, I2 && I2(u2), s2 = h2.render(h2.props, h2.state, h2.context), h2.state = h2.__s;
            } while (h2.__d && ++P2 < 25);
          h2.state = h2.__s, h2.getChildContext != null && (i2 = m(m({}, i2), h2.getChildContext())), x2 && !p2 && h2.getSnapshotBeforeUpdate != null && (d2 = h2.getSnapshotBeforeUpdate(v2, y2)), A2 = s2 != null && s2.type === S && s2.key == null ? E(s2.props.children) : s2, f2 = L(n2, g(A2) ? A2 : [A2], u2, t2, i2, r2, o2, e2, f2, c2, a2), h2.base = u2.__e, u2.__u &= -161, h2.__h.length && e2.push(h2), _2 && (h2.__E = h2.__ = null);
        } catch (n3) {
          if (u2.__v = null, c2 || o2 != null)
            if (n3.then) {
              for (u2.__u |= c2 ? 160 : 128;f2 && f2.nodeType == 8 && f2.nextSibling; )
                f2 = f2.nextSibling;
              o2[o2.indexOf(f2)] = null, u2.__e = f2;
            } else {
              for (H2 = o2.length;H2--; )
                b(o2[H2]);
              B(u2);
            }
          else
            u2.__e = t2.__e, u2.__k = t2.__k, n3.then || B(u2);
          l.__e(n3, u2, t2);
        }
      else
        o2 == null && u2.__v == t2.__v ? (u2.__k = t2.__k, u2.__e = t2.__e) : f2 = u2.__e = G(t2.__e, u2, t2, i2, r2, o2, e2, c2, a2);
    return (s2 = l.diffed) && s2(u2), 128 & u2.__u ? undefined : f2;
  }
  function B(n2) {
    n2 && (n2.__c && (n2.__c.__e = true), n2.__k && n2.__k.some(B));
  }
  function D(n2, u2, t2) {
    for (var i2 = 0;i2 < t2.length; i2++)
      J(t2[i2], t2[++i2], t2[++i2]);
    l.__c && l.__c(u2, n2), n2.some(function(u3) {
      try {
        n2 = u3.__h, u3.__h = [], n2.some(function(n3) {
          n3.call(u3);
        });
      } catch (n3) {
        l.__e(n3, u3.__v);
      }
    });
  }
  function E(n2) {
    return typeof n2 != "object" || n2 == null || n2.__b > 0 ? n2 : g(n2) ? n2.map(E) : n2.constructor !== undefined ? null : m({}, n2);
  }
  function G(u2, t2, i2, r2, o2, e2, f2, c2, a2) {
    var s2, h2, p2, v2, y2, w2, _2, m2 = i2.props || d, k2 = t2.props, x2 = t2.type;
    if (x2 == "svg" ? o2 = "http://www.w3.org/2000/svg" : x2 == "math" ? o2 = "http://www.w3.org/1998/Math/MathML" : o2 || (o2 = "http://www.w3.org/1999/xhtml"), e2 != null) {
      for (s2 = 0;s2 < e2.length; s2++)
        if ((y2 = e2[s2]) && "setAttribute" in y2 == !!x2 && (x2 ? y2.localName == x2 : y2.nodeType == 3)) {
          u2 = y2, e2[s2] = null;
          break;
        }
    }
    if (u2 == null) {
      if (x2 == null)
        return document.createTextNode(k2);
      u2 = document.createElementNS(o2, x2, k2.is && k2), c2 && (l.__m && l.__m(t2, e2), c2 = false), e2 = null;
    }
    if (x2 == null)
      m2 === k2 || c2 && u2.data == k2 || (u2.data = k2);
    else {
      if (e2 = x2 == "textarea" && k2.defaultValue != null ? null : e2 && n.call(u2.childNodes), !c2 && e2 != null)
        for (m2 = {}, s2 = 0;s2 < u2.attributes.length; s2++)
          m2[(y2 = u2.attributes[s2]).name] = y2.value;
      for (s2 in m2)
        y2 = m2[s2], s2 == "dangerouslySetInnerHTML" ? p2 = y2 : s2 == "children" || (s2 in k2) || s2 == "value" && ("defaultValue" in k2) || s2 == "checked" && ("defaultChecked" in k2) || N(u2, s2, null, y2, o2);
      for (s2 in k2)
        y2 = k2[s2], s2 == "children" ? v2 = y2 : s2 == "dangerouslySetInnerHTML" ? h2 = y2 : s2 == "value" ? w2 = y2 : s2 == "checked" ? _2 = y2 : c2 && typeof y2 != "function" || m2[s2] === y2 || N(u2, s2, y2, m2[s2], o2);
      if (h2)
        c2 || p2 && (h2.__html == p2.__html || h2.__html == u2.innerHTML) || (u2.innerHTML = h2.__html), t2.__k = [];
      else if (p2 && (u2.innerHTML = ""), L(t2.type == "template" ? u2.content : u2, g(v2) ? v2 : [v2], t2, i2, r2, x2 == "foreignObject" ? "http://www.w3.org/1999/xhtml" : o2, e2, f2, e2 ? e2[0] : i2.__k && $(i2, 0), c2, a2), e2 != null)
        for (s2 = e2.length;s2--; )
          b(e2[s2]);
      c2 && x2 != "textarea" || (s2 = "value", x2 == "progress" && w2 == null ? u2.removeAttribute("value") : w2 != null && (w2 !== u2[s2] || x2 == "progress" && !w2 || x2 == "option" && w2 != m2[s2]) && N(u2, s2, w2, m2[s2], o2), s2 = "checked", _2 != null && _2 != u2[s2] && N(u2, s2, _2, m2[s2], o2));
    }
    return u2;
  }
  function J(n2, u2, t2) {
    try {
      if (typeof n2 == "function") {
        var i2 = typeof n2.__u == "function";
        i2 && n2.__u(), i2 && u2 == null || (n2.__u = n2(u2));
      } else
        n2.current = u2;
    } catch (n3) {
      l.__e(n3, t2);
    }
  }
  function K(n2, u2, t2) {
    var i2, r2;
    if (l.unmount && l.unmount(n2), (i2 = n2.ref) && (i2.current && i2.current != n2.__e || J(i2, null, u2)), (i2 = n2.__c) != null) {
      if (i2.componentWillUnmount)
        try {
          i2.componentWillUnmount();
        } catch (n3) {
          l.__e(n3, u2);
        }
      i2.base = i2.__P = null;
    }
    if (i2 = n2.__k)
      for (r2 = 0;r2 < i2.length; r2++)
        i2[r2] && K(i2[r2], u2, t2 || typeof n2.type != "function");
    t2 || b(n2.__e), n2.__c = n2.__ = n2.__e = undefined;
  }
  function Q(n2, l2, u2) {
    return this.constructor(n2, u2);
  }
  function R(u2, t2, i2) {
    var r2, o2, e2, f2;
    t2 == document && (t2 = document.documentElement), l.__ && l.__(u2, t2), o2 = (r2 = typeof i2 == "function") ? null : i2 && i2.__k || t2.__k, e2 = [], f2 = [], q(t2, u2 = (!r2 && i2 || t2).__k = k(S, null, [u2]), o2 || d, d, t2.namespaceURI, !r2 && i2 ? [i2] : o2 ? null : t2.firstChild ? n.call(t2.childNodes) : null, e2, !r2 && i2 ? i2 : o2 ? o2.__e : t2.firstChild, r2, f2), D(e2, u2, f2);
  }
  n = w.slice, l = { __e: function(n2, l2, u2, t2) {
    for (var i2, r2, o2;l2 = l2.__; )
      if ((i2 = l2.__c) && !i2.__)
        try {
          if ((r2 = i2.constructor) && r2.getDerivedStateFromError != null && (i2.setState(r2.getDerivedStateFromError(n2)), o2 = i2.__d), i2.componentDidCatch != null && (i2.componentDidCatch(n2, t2 || {}), o2 = i2.__d), o2)
            return i2.__E = i2;
        } catch (l3) {
          n2 = l3;
        }
    throw n2;
  } }, u = 0, t = function(n2) {
    return n2 != null && n2.constructor === undefined;
  }, C.prototype.setState = function(n2, l2) {
    var u2;
    u2 = this.__s != null && this.__s != this.state ? this.__s : this.__s = m({}, this.state), typeof n2 == "function" && (n2 = n2(m({}, u2), this.props)), n2 && m(u2, n2), n2 != null && this.__v && (l2 && this._sb.push(l2), A(this));
  }, C.prototype.forceUpdate = function(n2) {
    this.__v && (this.__e = true, n2 && this.__h.push(n2), A(this));
  }, C.prototype.render = S, i = [], o = typeof Promise == "function" ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout, e = function(n2, l2) {
    return n2.__v.__b - l2.__v.__b;
  }, H.__r = 0, f = Math.random().toString(8), c = "__d" + f, a = "__a" + f, s = /(PointerCapture)$|Capture$/i, h = 0, p = V(false), v = V(true), y = 0;

  // src/ui/render.ts
  function renderElement(node) {
    const host = document.createElement("div");
    R(node, host);
    const element = host.firstElementChild;
    if (!(element instanceof HTMLElement)) {
      throw new Error("Preact component did not render an HTMLElement");
    }
    return element;
  }
  // node_modules/preact/jsx-runtime/dist/jsxRuntime.module.js
  var f2 = 0;
  function u2(e2, t2, n2, o2, i2, u3) {
    t2 || (t2 = {});
    var a2, c2, p2 = t2;
    if ("ref" in p2)
      for (c2 in p2 = {}, t2)
        c2 == "ref" ? a2 = t2[c2] : p2[c2] = t2[c2];
    var l2 = { type: e2, props: p2, key: n2, ref: a2, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: undefined, __v: --f2, __i: -1, __u: 0, __source: i2, __self: u3 };
    if (typeof e2 == "function" && (a2 = e2.defaultProps))
      for (c2 in a2)
        p2[c2] === undefined && (p2[c2] = a2[c2]);
    return l.vnode && l.vnode(l2), l2;
  }

  // src/ui/shortcutHelp.tsx
  function ShortcutHelpContainer(props) {
    return renderElement(/* @__PURE__ */ u2("div", {
      id: SHORTCUT_HELP_CONTAINER_ID,
      children: [
        /* @__PURE__ */ u2("button", {
          id: SHORTCUT_HELP_BUTTON_ID,
          type: "button",
          "aria-label": "Mostrar atajos de teclado",
          "aria-haspopup": "dialog",
          "aria-expanded": "false",
          onClick: (event) => {
            event.preventDefault();
            event.stopPropagation();
            props.onToggle();
          },
          children: "?"
        }, undefined, false, undefined, this),
        /* @__PURE__ */ u2("div", {
          id: SHORTCUT_HELP_POPOVER_ID,
          hidden: true,
          role: "dialog",
          "aria-label": "Atajos de teclado",
          children: [
            /* @__PURE__ */ u2("div", {
              className: "fc-premium-shortcut-help-title",
              children: "Atajos de teclado"
            }, undefined, false, undefined, this),
            props.items.map((item) => /* @__PURE__ */ u2(ShortcutHelpRow, {
              item,
              formatKey: props.formatKey
            }, undefined, false, undefined, this))
          ]
        }, undefined, true, undefined, this)
      ]
    }, undefined, true, undefined, this));
  }
  function ShortcutHelpRow(props) {
    return /* @__PURE__ */ u2("div", {
      className: "fc-premium-shortcut-help-row",
      children: [
        /* @__PURE__ */ u2("span", {
          className: "fc-premium-shortcut-help-keys",
          children: props.item.keys.map((key) => /* @__PURE__ */ u2("kbd", {
            className: "fc-premium-shortcut-help-key",
            children: props.formatKey(key)
          }, undefined, false, undefined, this))
        }, undefined, false, undefined, this),
        /* @__PURE__ */ u2("span", {
          className: "fc-premium-shortcut-help-description",
          children: props.item.description
        }, undefined, false, undefined, this)
      ]
    }, undefined, true, undefined, this);
  }

  // src/ui/shortcutHelpDom.ts
  var documentClickInstalled = false;
  function isShortcutHelpPopoverOpen() {
    const popover = document.getElementById(SHORTCUT_HELP_POPOVER_ID);
    return popover instanceof HTMLElement && !popover.hidden;
  }
  function closeShortcutHelpPopover() {
    setShortcutHelpPopoverOpen(false);
  }
  function setShortcutHelpPopoverOpen(open) {
    const button = document.getElementById(SHORTCUT_HELP_BUTTON_ID);
    const popover = document.getElementById(SHORTCUT_HELP_POPOVER_ID);
    if (!(button instanceof HTMLButtonElement) || !popover) {
      return;
    }
    popover.hidden = !open;
    button.setAttribute("aria-expanded", open ? "true" : "false");
  }
  function renderShortcutHelpButton(options) {
    if (!document.body) {
      return;
    }
    document.getElementById(SHORTCUT_HELP_CONTAINER_ID)?.remove();
    document.getElementById(SHORTCUT_HELP_BUTTON_ID)?.remove();
    document.getElementById(SHORTCUT_HELP_POPOVER_ID)?.remove();
    const container = ShortcutHelpContainer({
      items: options.items,
      formatKey: options.formatKey,
      onToggle: () => {
        setShortcutHelpPopoverOpen(!isShortcutHelpPopoverOpen());
      }
    });
    installShortcutHelpDocumentClick();
    document.body.prepend(container);
  }
  function installShortcutHelpDocumentClick() {
    if (documentClickInstalled) {
      return;
    }
    documentClickInstalled = true;
    document.addEventListener("click", handleShortcutHelpDocumentClick, true);
  }
  function handleShortcutHelpDocumentClick(event) {
    const target = event.target;
    if (!(target instanceof Node)) {
      return;
    }
    const button = document.getElementById(SHORTCUT_HELP_BUTTON_ID);
    const popover = document.getElementById(SHORTCUT_HELP_POPOVER_ID);
    if (button?.contains(target) || popover?.contains(target)) {
      return;
    }
    closeShortcutHelpPopover();
  }

  // src/ui/shortcutHelpItems.ts
  function getShortcutHelpItems() {
    return [
      {
        keys: [KEY_NAV_PREVIOUS_POST, KEY_NAV_NEXT_POST],
        description: "Seleccionar mensaje anterior/siguiente"
      },
      {
        keys: [KEY_NAV_PREVIOUS_PAGE, KEY_NAV_NEXT_PAGE],
        description: "Ir a la pagina anterior/siguiente"
      },
      {
        keys: [KEY_RETURN_TO_THREAD_LIST],
        description: "Volver a la lista de hilos"
      },
      {
        keys: [KEY_QUOTE_SELECTED_POST],
        description: "Abrir/citar el seleccionado"
      },
      {
        keys: [
          KEY_OPEN_SELECTED_THREAD_IN_NEW_TAB_MODIFIER,
          KEY_OPEN_SELECTED_THREAD_IN_NEW_TAB
        ],
        description: "Abrir hilo seleccionado en nueva pestaña"
      },
      {
        keys: [KEY_HIDE_SELECTED_THREAD],
        description: "Esconder hilo seleccionado"
      },
      {
        keys: [KEY_NEW_THREAD_REPLY],
        description: "Responder sin cita"
      },
      {
        keys: [KEY_MULTIQUOTE_SELECTED_POST],
        description: "Alternar multicita"
      },
      {
        keys: [KEY_CLEAR_ACTIVE_VIEW],
        description: "Limpiar filtros o cerrar ayuda"
      },
      {
        keys: [KEY_OPEN_SHORTCUT_HELP],
        description: "Mostrar estos atajos"
      }
    ];
  }
  function formatShortcutHelpKey(key) {
    if (key === KEY_NAV_PREVIOUS_POST) {
      return "Arriba";
    }
    if (key === KEY_NAV_NEXT_POST) {
      return "Abajo";
    }
    if (key === KEY_NAV_PREVIOUS_PAGE) {
      return "Izquierda";
    }
    if (key === KEY_NAV_NEXT_PAGE) {
      return "Derecha";
    }
    if (key === KEY_CLEAR_ACTIVE_VIEW) {
      return "Esc";
    }
    if (key.length === 1) {
      return key.toUpperCase();
    }
    return key;
  }

  // src/shared/hash.ts
  function hashString(value) {
    let hash = 0;
    for (let index = 0;index < value.length; index += 1) {
      hash = hash * 31 + value.charCodeAt(index) >>> 0;
    }
    return hash;
  }

  // src/domain/forumThreadList.ts
  function getForumThreadRowsSignature(rowHtmlList, scope) {
    return `${scope}|${rowHtmlList.length}|${rowHtmlList.map((html) => hashString(html).toString(36)).join(":")}`;
  }
  function getForumThreadListPage(records, requestedPage, pageSize) {
    const totalPages = getForumThreadListTotalPages(records.length, pageSize);
    const currentPage = clampForumThreadListPage(requestedPage, totalPages);
    const start = (currentPage - 1) * pageSize;
    const pageRecords = records.slice(start, start + pageSize);
    return {
      currentPage,
      totalPages,
      pageSize,
      records: pageRecords,
      rowHtmlList: pageRecords.map((record) => record.html)
    };
  }
  function getForumThreadListTotalPages(totalRecords, pageSize) {
    return Math.max(1, Math.ceil(totalRecords / pageSize));
  }
  function clampForumThreadListPage(pageNumber, totalPages) {
    return Math.min(Math.max(pageNumber, 1), totalPages);
  }

  // src/services/queryState.ts
  function readForumQueryState(url = new URL(location.href)) {
    const tag = normalizeAuthorName(url.searchParams.get(FORUM_STATE_QUERY_PARAMS.tag));
    const page = Number(url.searchParams.get("page") || "1");
    return {
      tag: tag || null,
      page: Number.isFinite(page) && page > 0 ? Math.floor(page) : 1
    };
  }
  function clearForumStateQueryParams(url) {
    for (const param of Object.values(FORUM_STATE_QUERY_PARAMS)) {
      url.searchParams.delete(param);
    }
  }
  function isGraphViewType(type) {
    return GRAPH_VIEW_TYPES.includes(type || "");
  }
  function isThreadUrl(url) {
    return url.pathname.endsWith("/showthread.php") && Boolean(getThreadId(url) || getPostQueryId(url));
  }
  function clearThreadStateQueryParams(url) {
    for (const param of Object.values(THREAD_STATE_QUERY_PARAMS)) {
      url.searchParams.delete(param);
    }
    for (const param of LEGACY_THREAD_STATE_QUERY_PARAMS) {
      url.searchParams.delete(param);
    }
  }
  function readThreadQueryState(url = new URL(location.href)) {
    const emptyState = {
      graphView: null,
      pageFilter: null,
      authorFilters: [],
      searchQuery: ""
    };
    if (!isThreadUrl(url)) {
      return emptyState;
    }
    const graphType = url.searchParams.get(THREAD_STATE_QUERY_PARAMS.graphType);
    const graphRoot = url.searchParams.get(THREAD_STATE_QUERY_PARAMS.graphRoot);
    const graphRelated = url.searchParams.get(THREAD_STATE_QUERY_PARAMS.graphRelated);
    const pageFilter = Number(url.searchParams.get(THREAD_STATE_QUERY_PARAMS.pageFilter) || "");
    const authorFilters = Array.from(new Set(url.searchParams.getAll(THREAD_STATE_QUERY_PARAMS.authorFilter).map((author) => normalizeAuthorName(author)).filter(Boolean)));
    const searchQuery = normalizeText(url.searchParams.get(THREAD_STATE_QUERY_PARAMS.searchQuery));
    const graphView = isGraphViewType(graphType) && graphRoot ? {
      type: graphType,
      rootPostId: graphRoot,
      relatedPostId: graphRelated || null
    } : null;
    return {
      graphView,
      pageFilter: !graphView && authorFilters.length === 0 && !searchQuery && Number.isFinite(pageFilter) && pageFilter > 0 ? pageFilter : null,
      authorFilters,
      searchQuery
    };
  }

  // src/services/keyboard.ts
  function isEditableTarget(target) {
    if (!(target instanceof HTMLElement)) {
      return false;
    }
    return Boolean(target.closest("input, textarea, select, [contenteditable='true']"));
  }
  function hasKeyboardModifier(event) {
    return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
  }
  function keyboardShortcutMatches(event, key) {
    if (key.length === 1) {
      return event.key.toLowerCase() === key.toLowerCase();
    }
    return event.key === key;
  }
  function isMacKeyboardPlatform() {
    return /Mac|iPhone|iPad|iPod/i.test(navigator.platform || "");
  }
  function isOpenInNewTabKeyboardShortcut(event, key) {
    if (event.key !== key || event.altKey || event.shiftKey) {
      return false;
    }
    return isMacKeyboardPlatform() ? event.metaKey && !event.ctrlKey : event.ctrlKey && !event.metaKey;
  }

  // src/app/core/navigationController.ts
  function createNavigationController(options, state = {
    navigationItems: [],
    selectedNavigationIndex: -1
  }) {
    function collectNavigationItems() {
      return options.collectNavigationItems();
    }
    function renderNavigationStatus(selected) {
      options.onRenderNavigationStatus?.(selected);
    }
    function updateSelectedPostUrl(selected) {
      options.onUpdateSelectedThreadUrl?.(selected);
    }
    function getPostsElement() {
      return options.getPostsElement();
    }
    function renderNavigationSelection(renderOptions = {}) {
      clearNavigationSelection();
      const selected = state.navigationItems[state.selectedNavigationIndex];
      if (!selected) {
        renderNavigationStatus(null);
        return;
      }
      markNavigationItemSelected(selected);
      renderNavigationStatus(selected);
      if (renderOptions.updateUrl && isThreadPage()) {
        updateSelectedPostUrl(selected);
      }
      if (renderOptions.scroll) {
        const mode = isThreadPage() ? "start" : "nearest";
        scrollNavigationElementIntoView(selected.element, mode);
      }
    }
    function refreshNavigation(options2 = {}) {
      const previousElement = state.navigationItems[state.selectedNavigationIndex]?.element;
      state.navigationItems = collectNavigationItems();
      if (state.navigationItems.length === 0) {
        state.selectedNavigationIndex = -1;
        renderNavigationSelection(options2);
        return;
      }
      if (options2.reset || state.selectedNavigationIndex < 0) {
        state.selectedNavigationIndex = 0;
      } else {
        const preservedIndex = state.navigationItems.findIndex((item) => item.element === previousElement);
        state.selectedNavigationIndex = preservedIndex >= 0 ? preservedIndex : 0;
      }
      renderNavigationSelection(options2);
    }
    function moveNavigation(direction) {
      if (state.navigationItems.length === 0) {
        refreshNavigation({ reset: true });
      }
      if (state.navigationItems.length === 0) {
        return;
      }
      state.selectedNavigationIndex = Math.min(Math.max(state.selectedNavigationIndex + direction, 0), state.navigationItems.length - 1);
      renderNavigationSelection({ scroll: true, updateUrl: true });
    }
    function selectNavigationIndex(index) {
      if (state.navigationItems.length === 0) {
        refreshNavigation({ reset: true });
      }
      if (state.navigationItems.length === 0) {
        return;
      }
      state.selectedNavigationIndex = Math.min(Math.max(index, 0), state.navigationItems.length - 1);
      renderNavigationSelection({ scroll: true, updateUrl: true });
    }
    function selectNavigationElement(element, options2 = {}) {
      const index = state.navigationItems.findIndex((item) => item.element === element);
      if (index < 0) {
        if (options2.scroll !== false) {
          const mode = isThreadPage() ? "start" : "nearest";
          scrollNavigationElementIntoView(element, mode);
        }
        return;
      }
      state.selectedNavigationIndex = index;
      renderNavigationSelection({
        scroll: options2.scroll !== false,
        updateUrl: options2.updateUrl !== false
      });
    }
    function getSelectedPostWrapper2() {
      if (state.navigationItems.length === 0) {
        refreshNavigation({ reset: true });
      }
      const selected = state.navigationItems[state.selectedNavigationIndex]?.element;
      if (selected instanceof HTMLElement && selected.matches(".fc-premium-post-wrapper")) {
        return selected;
      }
      return getSelectedPostWrapper();
    }
    function getSelectedNavigationItem() {
      if (state.navigationItems.length === 0) {
        refreshNavigation({ reset: true });
      }
      return state.navigationItems[state.selectedNavigationIndex] || null;
    }
    return {
      refreshNavigation,
      getNavigationItems: () => state.navigationItems,
      getSelectedNavigationItem,
      getNavigationLength: () => state.navigationItems.length,
      getSelectedPostWrapper: getSelectedPostWrapper2,
      moveNavigation,
      selectNavigationIndex,
      selectNavigationElement
    };
  }

  // src/ui/components/ForumControls.tsx
  function ForumSidebarToggleButton(props) {
    return renderElement(/* @__PURE__ */ u2("button", {
      id: FORUM_SIDEBAR_TOGGLE_ID,
      type: "button",
      title: props.hidden ? "Mostrar la columna izquierda" : "Ocultar la columna izquierda",
      "aria-expanded": !props.hidden,
      onClick: props.onToggle,
      children: props.hidden ? "Mostrar panel izquierdo" : "Ocultar panel izquierdo"
    }, undefined, false, undefined, this));
  }
  function HiddenThreadsToolbarCell(props) {
    return renderElement(/* @__PURE__ */ u2("td", {
      id: HIDDEN_THREADS_BUTTON_ID,
      className: "vbmenu_control",
      ...{ noWrap: true },
      style: "cursor: pointer",
      children: /* @__PURE__ */ u2("a", {
        href: "#",
        onClick: (event) => {
          event.preventDefault();
          props.onOpen();
        },
        children: "Hilos escondidos"
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this));
  }
  function ForumLoadingStatus() {
    return renderElement(/* @__PURE__ */ u2("span", {
      id: FORUM_LOADING_STATUS_ID,
      children: [
        /* @__PURE__ */ u2("span", {
          className: "fc-premium-spinner",
          "aria-hidden": "true"
        }, undefined, false, undefined, this),
        /* @__PURE__ */ u2("span", {
          "data-fc-premium-loading-text": "true"
        }, undefined, false, undefined, this)
      ]
    }, undefined, true, undefined, this));
  }

  // src/domain/tags.ts
  var SPECIAL_TAGS = [
    "tema serio"
  ];
  var SINGLE_WORD_TAG_PATTERN = /^[A-Za-z0-9_-]+/;
  var SPECIAL_TAG_PATTERNS = SPECIAL_TAGS.map((tag) => ({
    tag,
    pattern: new RegExp(`^${escapeRegExp(tag).replace(/\\ /g, "\\s+")}(?![\\p{L}\\p{N}_-])`, "iu")
  }));
  function normalizeTag(tag) {
    return tag.replace(/\s+/g, " ").trim().toLowerCase();
  }
  function findTagsInText(source) {
    const text = String(source || "");
    const matches = [];
    for (let index = 0;index < text.length; index += 1) {
      if (text[index] !== "+") {
        continue;
      }
      const tagMatch = matchTagAfterPlus(text.slice(index + 1));
      if (!tagMatch) {
        continue;
      }
      matches.push({
        tag: tagMatch.tag,
        start: index,
        end: index + 1 + tagMatch.length
      });
      index += tagMatch.length;
    }
    return matches;
  }
  function getTagsFromText(source) {
    return Array.from(new Set(findTagsInText(source).map((match) => match.tag)));
  }
  function splitTextByTags(source) {
    const parts = [];
    let currentIndex = 0;
    for (const match of findTagsInText(source)) {
      if (match.start > currentIndex) {
        parts.push({
          type: "text",
          text: source.slice(currentIndex, match.start)
        });
      }
      parts.push({
        type: "tag",
        text: source.slice(match.start, match.end),
        tag: match.tag
      });
      currentIndex = match.end;
    }
    if (currentIndex < source.length) {
      parts.push({
        type: "text",
        text: source.slice(currentIndex)
      });
    }
    return parts;
  }
  function matchTagAfterPlus(source) {
    const leadingWhitespaceLength = source.match(/^\s*/)?.[0].length || 0;
    const candidate = source.slice(leadingWhitespaceLength);
    for (const special of SPECIAL_TAG_PATTERNS) {
      const match = candidate.match(special.pattern);
      if (match?.[0]) {
        return {
          tag: normalizeTag(special.tag),
          length: leadingWhitespaceLength + match[0].length
        };
      }
    }
    const wordMatch = candidate.match(SINGLE_WORD_TAG_PATTERN);
    if (!wordMatch?.[0]) {
      return null;
    }
    return {
      tag: normalizeTag(wordMatch[0]),
      length: leadingWhitespaceLength + wordMatch[0].length
    };
  }
  function escapeRegExp(value) {
    return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }

  // src/ui/components/Tags.tsx
  function TagChip(props) {
    return renderElement(/* @__PURE__ */ u2(TagChipView, {
      ...props
    }, undefined, false, undefined, this));
  }
  function TagChipView(props) {
    const canonicalTag = normalizeTag(props.tag);
    return /* @__PURE__ */ u2(TagBase, {
      tag: canonicalTag,
      role: "button",
      tabIndex: 0,
      title: props.title || `Filtrar por +${props.tag}`,
      "aria-pressed": props.pressed,
      onClick: (event) => {
        event.preventDefault();
        event.stopPropagation();
        props.onToggle(canonicalTag);
      },
      onKeyDown: (event) => {
        if (event.key !== "Enter" && event.key !== " ") {
          return;
        }
        event.preventDefault();
        event.stopPropagation();
        props.onToggle(canonicalTag);
      },
      children: props.label || `+${props.tag}`
    }, undefined, false, undefined, this);
  }
  function TagLabelView(props) {
    return /* @__PURE__ */ u2(TagBase, {
      tag: props.tag,
      title: props.title,
      children: props.label || `+${props.tag}`
    }, undefined, false, undefined, this);
  }
  function TopTagBar(props) {
    return renderElement(/* @__PURE__ */ u2("div", {
      id: "fc-premium-top-tags",
      children: [
        /* @__PURE__ */ u2("span", {
          children: "Top tags:"
        }, undefined, false, undefined, this),
        props.tags.map((summary) => /* @__PURE__ */ u2(TagChipView, {
          tag: summary.tag,
          label: `+${summary.tag} (${summary.count})`,
          title: `Filtrar ${summary.count} hilos con +${summary.tag}`,
          pressed: props.activeTag === summary.tag,
          onToggle: props.onToggle
        }, undefined, false, undefined, this))
      ]
    }, undefined, true, undefined, this));
  }
  function getTagColors(tag) {
    const hue = hashString(tag.toLowerCase()) % 360;
    return {
      background: `hsl(${hue}, 82%, 92%)`,
      border: `hsl(${hue}, 58%, 60%)`,
      color: `hsl(${hue}, 70%, 24%)`
    };
  }
  function TagBase(props) {
    const { tag, children, ...elementProps } = props;
    const canonicalTag = normalizeTag(tag);
    const colors = getTagColors(canonicalTag);
    return /* @__PURE__ */ u2("span", {
      ...elementProps,
      className: "fc-premium-tag-chip",
      "data-fc-premium-tag": canonicalTag,
      style: {
        "--fc-premium-tag-bg": colors.background,
        "--fc-premium-tag-border": colors.border,
        "--fc-premium-tag-color": colors.color
      },
      children
    }, undefined, false, undefined, this);
  }

  // src/ui/components/HiddenThreadsModal.tsx
  function HiddenThreadsModal(props) {
    return renderElement(/* @__PURE__ */ u2("div", {
      id: HIDDEN_THREADS_MODAL_ID,
      hidden: true,
      role: "dialog",
      "aria-modal": "true",
      "aria-label": "Hilos escondidos",
      onClick: (event) => {
        if (event.target === event.currentTarget) {
          props.onClose();
        }
      },
      children: /* @__PURE__ */ u2("div", {
        className: "fc-premium-hidden-threads-dialog",
        children: [
          /* @__PURE__ */ u2("div", {
            className: "fc-premium-hidden-threads-header",
            children: [
              /* @__PURE__ */ u2("span", {
                children: "Hilos escondidos"
              }, undefined, false, undefined, this),
              /* @__PURE__ */ u2("button", {
                type: "button",
                onClick: props.onClose,
                children: "Cerrar"
              }, undefined, false, undefined, this)
            ]
          }, undefined, true, undefined, this),
          /* @__PURE__ */ u2(HiddenThreadsModalBodyView, {
            records: props.records,
            onRestore: props.onRestore
          }, undefined, false, undefined, this)
        ]
      }, undefined, true, undefined, this)
    }, undefined, false, undefined, this));
  }
  function HiddenThreadsModalBody(props) {
    return renderElement(/* @__PURE__ */ u2(HiddenThreadsModalBodyView, {
      records: props.records,
      onRestore: props.onRestore
    }, undefined, false, undefined, this));
  }
  function HiddenThreadsModalBodyView(props) {
    return /* @__PURE__ */ u2("div", {
      id: HIDDEN_THREADS_MODAL_BODY_ID,
      children: props.records.length === 0 ? /* @__PURE__ */ u2("div", {
        className: "fc-premium-hidden-threads-empty",
        children: "No hay hilos escondidos en este foro."
      }, undefined, false, undefined, this) : /* @__PURE__ */ u2("table", {
        className: "fc-premium-hidden-threads-table",
        children: [
          /* @__PURE__ */ u2("thead", {
            children: /* @__PURE__ */ u2("tr", {
              children: [
                /* @__PURE__ */ u2("th", {
                  children: "Hilo"
                }, undefined, false, undefined, this),
                /* @__PURE__ */ u2("th", {
                  children: "Info"
                }, undefined, false, undefined, this),
                /* @__PURE__ */ u2("th", {
                  children: "Oculto"
                }, undefined, false, undefined, this),
                /* @__PURE__ */ u2("th", {
                  children: "Accion"
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this)
          }, undefined, false, undefined, this),
          /* @__PURE__ */ u2("tbody", {
            children: props.records.map((record) => /* @__PURE__ */ u2(HiddenThreadRow, {
              record,
              onRestore: props.onRestore
            }, undefined, false, undefined, this))
          }, undefined, false, undefined, this)
        ]
      }, undefined, true, undefined, this)
    }, undefined, false, undefined, this);
  }
  function HiddenThreadRow(props) {
    const record = props.record;
    const info = [
      record.author ? `Autor: ${record.author}` : "",
      record.statsText,
      record.lastPostText
    ].filter(Boolean);
    return /* @__PURE__ */ u2("tr", {
      children: [
        /* @__PURE__ */ u2("td", {
          children: [
            /* @__PURE__ */ u2("a", {
              className: "fc-premium-hidden-thread-title",
              href: record.url,
              children: record.title || `Hilo ${record.id}`
            }, undefined, false, undefined, this),
            record.tags.length > 0 ? /* @__PURE__ */ u2("div", {
              className: "fc-premium-hidden-thread-meta",
              children: [
                record.tags.slice(0, 5).map((tag) => /* @__PURE__ */ u2(TagLabelView, {
                  tag
                }, undefined, false, undefined, this)),
                record.tags.length > 5 ? ` +${record.tags.length - 5}` : ""
              ]
            }, undefined, true, undefined, this) : null
          ]
        }, undefined, true, undefined, this),
        /* @__PURE__ */ u2("td", {
          children: info.length > 0 ? info.join(" · ") : "-"
        }, undefined, false, undefined, this),
        /* @__PURE__ */ u2("td", {
          children: formatHiddenThreadDate(record.hiddenAt)
        }, undefined, false, undefined, this),
        /* @__PURE__ */ u2("td", {
          children: /* @__PURE__ */ u2("button", {
            type: "button",
            className: "fc-premium-hidden-thread-restore",
            onClick: () => props.onRestore(record.id),
            children: "Restaurar"
          }, undefined, false, undefined, this)
        }, undefined, false, undefined, this)
      ]
    }, undefined, true, undefined, this);
  }
  function formatHiddenThreadDate(timestamp) {
    if (!timestamp) {
      return "Sin fecha";
    }
    try {
      return new Date(timestamp).toLocaleString();
    } catch (_error) {
      return "Sin fecha";
    }
  }

  // src/ui/hiddenThreadsModalDom.ts
  function renderHiddenThreadsToolbarButton(options) {
    if (!options.toolbarRow || !(options.toolsCell instanceof HTMLTableCellElement)) {
      return;
    }
    const existing = document.getElementById(HIDDEN_THREADS_BUTTON_ID);
    const cell = HiddenThreadsToolbarCell({
      onOpen: options.onOpen
    });
    if (existing instanceof HTMLTableCellElement) {
      existing.replaceWith(cell);
    }
    if (cell.parentElement !== options.toolbarRow || cell.nextElementSibling !== options.toolsCell) {
      options.toolbarRow.insertBefore(cell, options.toolsCell);
    }
  }
  function ensureHiddenThreadsModal(options) {
    let modal = document.getElementById(HIDDEN_THREADS_MODAL_ID);
    if (modal instanceof HTMLElement) {
      return modal;
    }
    modal = HiddenThreadsModal({
      records: options.records,
      onClose: options.onClose,
      onRestore: options.onRestore
    });
    document.body.append(modal);
    return modal;
  }
  function isHiddenThreadsModalOpen() {
    const modal = document.getElementById(HIDDEN_THREADS_MODAL_ID);
    return modal instanceof HTMLElement && !modal.hidden;
  }
  function closeHiddenThreadsModal() {
    const modal = document.getElementById(HIDDEN_THREADS_MODAL_ID);
    if (modal instanceof HTMLElement) {
      modal.hidden = true;
    }
    document.documentElement.classList.remove(MODAL_OPEN_CLASS);
    document.body?.classList.remove(MODAL_OPEN_CLASS);
  }
  function renderHiddenThreadsModalBody(options) {
    const body = options.modal.querySelector(`#${HIDDEN_THREADS_MODAL_BODY_ID}`);
    if (!(body instanceof HTMLElement)) {
      return;
    }
    body.replaceWith(HiddenThreadsModalBody({
      records: options.records,
      onRestore: options.onRestore
    }));
  }
  function openHiddenThreadsModal(options) {
    renderHiddenThreadsModalBody({
      modal: options.modal,
      records: options.records,
      onRestore: options.onRestore
    });
    options.modal.hidden = false;
    document.documentElement.classList.add(MODAL_OPEN_CLASS);
    document.body.classList.add(MODAL_OPEN_CLASS);
    options.modal.querySelector("button")?.focus({ preventScroll: true });
  }

  // src/adapters/forocoches/forumLayout.ts
  function getForumThreadsTable() {
    const table = document.getElementById("threadslist");
    if (table instanceof HTMLTableElement) {
      return table;
    }
    const title = document.querySelector(THREAD_TITLE_SELECTOR);
    const owner = title?.closest("table");
    return owner instanceof HTMLTableElement ? owner : null;
  }
  function getForumThreadListHeaderTable() {
    const threadsTable = getForumThreadsTable();
    let sibling = threadsTable?.previousElementSibling || null;
    while (sibling) {
      if (sibling instanceof HTMLTableElement && normalizeText(sibling.querySelector("td.tcat")?.textContent).startsWith("Temas en el Foro")) {
        return sibling;
      }
      sibling = sibling.previousElementSibling;
    }
    for (const table of document.querySelectorAll("table.tborder")) {
      if (table instanceof HTMLTableElement && normalizeText(table.querySelector("td.tcat")?.textContent).startsWith("Temas en el Foro")) {
        return table;
      }
    }
    return null;
  }
  function removeForumTitleTables() {
    const header = getForumThreadListHeaderTable();
    const forumName = getForumNameFromThreadListHeader();
    for (const table of document.querySelectorAll("table.tborder")) {
      if (table instanceof HTMLTableElement && (isForumTitleSummaryTable(table, header, forumName) || isForumBreadcrumbTitleTable(table, header, forumName))) {
        table.remove();
      }
    }
  }
  function getRelatedForumsPanel() {
    for (const table of document.querySelectorAll("table")) {
      if (!(table instanceof HTMLTableElement)) {
        continue;
      }
      const header = normalizeText(table.querySelector("tr:first-child td")?.textContent).toLowerCase();
      if (header === "foros relacionados" || header === "related forums") {
        return table;
      }
    }
    return null;
  }
  function getForumSidebarCell(panel) {
    let current = panel.parentElement;
    while (current) {
      if (current instanceof HTMLTableCellElement) {
        const cells = getDirectTableCells(current.parentElement);
        const hasMainSibling = cells.some((cell) => cell !== current && cellContainsForumThreads(cell));
        if (hasMainSibling) {
          return current;
        }
      }
      current = current.parentElement;
    }
    return null;
  }
  function getForumMainCell(sidebarCell) {
    const cells = getDirectTableCells(sidebarCell.parentElement);
    return cells.find(cellContainsForumThreads) || null;
  }
  function getForumSidebarSpacerCell(sidebarCell) {
    const mainCell = getForumMainCell(sidebarCell);
    if (!mainCell) {
      return null;
    }
    const cells = getDirectTableCells(sidebarCell.parentElement);
    const sidebarIndex = cells.indexOf(sidebarCell);
    const mainIndex = cells.indexOf(mainCell);
    if (sidebarIndex < 0 || mainIndex < 0 || mainIndex <= sidebarIndex + 1) {
      return null;
    }
    return cells.slice(sidebarIndex + 1, mainIndex).find(isForumSidebarSpacerCell) || null;
  }
  function setForumLayoutElementHidden(element, hidden) {
    if (hidden) {
      element.setAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE, "true");
    } else {
      element.removeAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE);
    }
  }
  function hideElementAndAdjacentSpacers(element) {
    setForumLayoutElementHidden(element, true);
    for (const sibling of [
      element.previousElementSibling,
      element.nextElementSibling
    ]) {
      if (sibling instanceof HTMLElement && isSmallLayoutSpacer(sibling)) {
        setForumLayoutElementHidden(sibling, true);
      }
    }
  }
  function isForumTopShortcutBar(table) {
    return isForumHomeShortcutBar(table) || isForumUserShortcutBar(table);
  }
  function shouldIgnoreTopNavigationTable(table) {
    return table.id === FORUM_CONTROLS_ROW_ID || Boolean(table.closest(`#${FORUM_CONTROLS_ROW_ID}`));
  }
  function hideTopShortcutBarsBefore(anchor) {
    for (const table of document.querySelectorAll("table")) {
      if (!(table instanceof HTMLTableElement)) {
        continue;
      }
      if (shouldIgnoreTopNavigationTable(table)) {
        continue;
      }
      if (isBeforeMainContent(table, anchor) && isForumTopShortcutBar(table)) {
        hideElementAndAdjacentSpacers(table);
      }
    }
  }
  function isBeforeMainContent(element, anchor) {
    if (anchor && element.contains(anchor)) {
      return false;
    }
    return !anchor || Boolean(element.compareDocumentPosition(anchor) & Node.DOCUMENT_POSITION_FOLLOWING);
  }
  function setForumMainCellExpanded(mainCell, expanded) {
    if (mainCell.dataset.fcPremiumOriginalWidth === undefined) {
      mainCell.dataset.fcPremiumOriginalWidth = mainCell.getAttribute("width") || "";
    }
    if (expanded) {
      mainCell.setAttribute("width", "100%");
      mainCell.style.width = "100%";
      return;
    }
    if (mainCell.dataset.fcPremiumOriginalWidth) {
      mainCell.setAttribute("width", mainCell.dataset.fcPremiumOriginalWidth);
    }
    mainCell.style.width = "";
  }
  function getForumNameFromThreadListHeader() {
    const header = getForumThreadListHeaderTable();
    const label = normalizeText(header?.querySelector("td.tcat .normal")?.textContent).replace(/^:\s*/, "").trim();
    return label || null;
  }
  function isForumTitleSummaryTable(table, header, forumName) {
    if (header && !(table.compareDocumentPosition(header) & Node.DOCUMENT_POSITION_FOLLOWING)) {
      return false;
    }
    const title = normalizeText(table.querySelector("h1")?.textContent);
    if (!title) {
      return false;
    }
    if (forumName && title.toLowerCase() !== forumName.toLowerCase()) {
      return false;
    }
    return Boolean(table.querySelector("img[src*='forocoches_recarga']"));
  }
  function isForumBreadcrumbTitleTable(table, header, forumName) {
    if (header && !(table.compareDocumentPosition(header) & Node.DOCUMENT_POSITION_FOLLOWING)) {
      return false;
    }
    const title = normalizeText(table.querySelector("td.navbar strong")?.textContent);
    if (!title) {
      return false;
    }
    if (forumName && !title.toLowerCase().startsWith(forumName.toLowerCase())) {
      return false;
    }
    return Boolean(table.querySelector("img[src*='navbits_finallink']"));
  }
  function getDirectTableCells(row) {
    if (!(row instanceof HTMLTableRowElement)) {
      return [];
    }
    return Array.from(row.children).filter((child) => child instanceof HTMLTableCellElement);
  }
  function cellContainsForumThreads(cell) {
    return Boolean(cell.querySelector("#threadslist") || cell.querySelector(THREAD_TITLE_SELECTOR));
  }
  function isForumSidebarSpacerCell(cell) {
    const width = Number(cell.getAttribute("width") || "0");
    const renderedWidth = cell.getBoundingClientRect().width;
    return normalizeText(cell.textContent).length === 0 && (Number.isFinite(width) && width > 0 && width <= 8 || renderedWidth > 0 && renderedWidth <= 8);
  }
  function isSmallLayoutSpacer(element) {
    if (!(element instanceof HTMLElement)) {
      return false;
    }
    if (normalizeText(element.textContent)) {
      return false;
    }
    if (element instanceof HTMLBRElement) {
      return true;
    }
    const explicitHeight = Number(element.getAttribute("height") || element.style.height.replace("px", ""));
    const renderedHeight = element.getBoundingClientRect().height;
    return ["DIV", "TABLE", "TBODY", "TR"].includes(element.tagName) && (Number.isFinite(explicitHeight) && explicitHeight > 0 && explicitHeight <= 12 || renderedHeight > 0 && renderedHeight <= 12);
  }
  function isForumHomeShortcutBar(table) {
    const text = normalizeLayoutText(table.textContent);
    return text === "inicio foro" || /^inicio foro\b/.test(text);
  }
  function isForumUserShortcutBar(table) {
    const text = normalizeLayoutText(table.textContent);
    return text.includes("panel control") && text.includes("temas iniciados") && text.includes("temas participados") && text.includes("finalizar sesion");
  }

  // src/adapters/forocoches/threadHeader.ts
  function getThreadTitleTable() {
    const table = document.querySelector("table[id^='fcthread']");
    return table instanceof HTMLTableElement ? table : null;
  }
  function getThreadBreadcrumbOuterTable() {
    const titleTable = getThreadTitleTable();
    const table = Array.from(document.querySelectorAll("table.tborder")).find((table2) => {
      if (!(table2 instanceof HTMLTableElement)) {
        return false;
      }
      if (titleTable && !(table2.compareDocumentPosition(titleTable) & Node.DOCUMENT_POSITION_FOLLOWING)) {
        return false;
      }
      return Boolean(table2.querySelector(".navbar") && table2.querySelector("img[src*='navbits_finallink']"));
    });
    return table instanceof HTMLTableElement ? table : null;
  }
  function getThreadBreadcrumbContentTable() {
    const outerTable = getThreadBreadcrumbOuterTable();
    const contentTable = outerTable?.rows[0]?.cells[0]?.querySelector("table");
    return contentTable instanceof HTMLTableElement ? contentTable : null;
  }
  function getNavbarSearchLink() {
    const link = document.getElementById("navbar_search");
    return link instanceof HTMLAnchorElement ? link : null;
  }
  function getThreadForumListLink() {
    const breadcrumbs = document.querySelector(".fc-premium-thread-header-breadcrumbs") || getThreadBreadcrumbContentTable() || getThreadBreadcrumbOuterTable();
    const links = Array.from((breadcrumbs || document).querySelectorAll("a[href*='forumdisplay.php']")).filter((link) => link instanceof HTMLAnchorElement);
    return links.at(-1) || null;
  }
  function moveForumHeaderSearchForm(searchSlot) {
    const parts = getForumHeaderSearchFormParts();
    if (!parts) {
      return false;
    }
    if (searchSlot.contains(parts.form)) {
      return true;
    }
    parts.form.classList.add("fc-premium-thread-header-search-form");
    for (const child of Array.from(parts.controlsCell.childNodes)) {
      if (child !== parts.form) {
        parts.form.append(child);
      }
    }
    searchSlot.append(parts.form);
    if (parts.oldContainer instanceof HTMLElement) {
      hideElementAndAdjacentSpacers(parts.oldContainer);
    }
    return true;
  }
  function hideForumHeaderSearchForm() {
    const parts = getForumHeaderSearchFormParts();
    if (parts?.oldContainer instanceof HTMLElement) {
      hideElementAndAdjacentSpacers(parts.oldContainer);
    }
  }
  function hideNativeThreadSearchMenu() {
    for (const id of ["threadsearch", "threadsearch_menu"]) {
      const element = document.getElementById(id);
      if (element instanceof HTMLElement) {
        element.setAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE, "true");
      }
    }
  }
  function getForumHeaderSearchFormParts() {
    const form = document.querySelector("form[name='busca'][action*='forocoches_search']");
    if (!(form instanceof HTMLFormElement)) {
      return null;
    }
    const nextCell = form.nextElementSibling;
    if (nextCell instanceof HTMLTableCellElement && nextCell.querySelector("input[name='query']")) {
      return {
        form,
        controlsCell: nextCell,
        oldContainer: nextCell.closest("table.cajasprin") || nextCell.closest("table")?.parentElement?.closest("tr") || nextCell.closest("table")
      };
    }
    const queryInput = Array.from(document.querySelectorAll("input[name='query']")).filter((input) => input instanceof HTMLInputElement).find((input) => input.classList.contains("cfield"));
    const controlsCell = queryInput?.closest("td");
    if (!(controlsCell instanceof HTMLTableCellElement)) {
      return null;
    }
    return {
      form,
      controlsCell,
      oldContainer: controlsCell.closest("table.cajasprin") || controlsCell.closest("table")?.parentElement?.closest("tr") || controlsCell.closest("table")
    };
  }

  // src/app/core/forumLayoutController.ts
  function createForumLayoutController(options) {
    let forumSidebarHidden = getSavedForumSidebarHidden();
    function getSavedForumSidebarHidden() {
      const saved = localStorage.getItem(FORUM_SIDEBAR_STORAGE_KEY);
      return saved === null ? true : saved === "true";
    }
    function setSavedForumSidebarHidden(hidden) {
      forumSidebarHidden = hidden;
      localStorage.setItem(FORUM_SIDEBAR_STORAGE_KEY, String(hidden));
      applyForumSidebarVisibility();
    }
    function getMainContentAnchor() {
      return getForumThreadsTable() || options.getPostsElement() || getThreadTitleTable();
    }
    function hideUnusedTopNavigationBars() {
      if (!isForumDisplayPage() && !isThreadPage()) {
        return;
      }
      hideTopShortcutBarsBefore(getMainContentAnchor());
    }
    function getOrCreateForumSidebarToggleButton() {
      const existing = document.getElementById(FORUM_SIDEBAR_TOGGLE_ID);
      const button = ForumSidebarToggleButton({
        hidden: forumSidebarHidden,
        onToggle: () => {
          setSavedForumSidebarHidden(!forumSidebarHidden);
        }
      });
      if (existing instanceof HTMLButtonElement) {
        existing.replaceWith(button);
      }
      return button;
    }
    function getForumToolbarRow() {
      const toolsCell = document.getElementById("forumtools");
      const row = toolsCell?.parentElement;
      return row instanceof HTMLTableRowElement ? row : null;
    }
    function renderHiddenThreadsToolbarButton2() {
      if (!isForumDisplayPage()) {
        return;
      }
      renderHiddenThreadsToolbarButton({
        toolbarRow: getForumToolbarRow(),
        toolsCell: document.getElementById("forumtools"),
        onOpen: openHiddenThreadsModal2
      });
    }
    function ensureHiddenThreadsModal2() {
      return ensureHiddenThreadsModal({
        records: options.getHiddenForumThreadRecordsForCurrentForum(),
        onClose: closeHiddenThreadsModal2,
        onRestore: (threadId) => {
          options.setForumThreadHiddenState(threadId, false);
        }
      });
    }
    function isHiddenThreadsModalOpen2() {
      return isHiddenThreadsModalOpen();
    }
    function closeHiddenThreadsModal2() {
      closeHiddenThreadsModal();
    }
    function renderHiddenThreadsModalBody2() {
      renderHiddenThreadsModalBody({
        modal: ensureHiddenThreadsModal2(),
        records: options.getHiddenForumThreadRecordsForCurrentForum(),
        onRestore: (threadId) => {
          options.setForumThreadHiddenState(threadId, false);
        }
      });
    }
    function openHiddenThreadsModal2() {
      openHiddenThreadsModal({
        modal: ensureHiddenThreadsModal2(),
        records: options.getHiddenForumThreadRecordsForCurrentForum(),
        onRestore: (threadId) => {
          options.setForumThreadHiddenState(threadId, false);
        }
      });
    }
    function isNativeForumControlsTable(table) {
      return Boolean(table.querySelector("a[href*='newthread.php'][href*='do=newthread']") && table.querySelector(".pagenav"));
    }
    function getNativeForumControlsTable() {
      const existing = document.getElementById(FORUM_CONTROLS_ROW_ID);
      if (existing instanceof HTMLTableElement) {
        return existing;
      }
      const threadsTable = getForumThreadsTable();
      const candidates = Array.from(document.querySelectorAll("table")).filter((table) => {
        if (!(table instanceof HTMLTableElement)) {
          return false;
        }
        if (!isNativeForumControlsTable(table)) {
          return false;
        }
        return !threadsTable || Boolean(table.compareDocumentPosition(threadsTable) & Node.DOCUMENT_POSITION_FOLLOWING);
      });
      return candidates[candidates.length - 1] || null;
    }
    function createForumLoadingStatus() {
      return ForumLoadingStatus();
    }
    function renderForumLoadingStatus() {
      const status = document.getElementById(FORUM_LOADING_STATUS_ID);
      if (!(status instanceof HTMLElement)) {
        return;
      }
      const loadState = options.getForumThreadLoadState();
      const visible = loadState.isLoading;
      const loadedPages = Math.min(loadState.loadedPages, loadState.targetPages);
      const text = status.querySelector("[data-fc-premium-loading-text]");
      status.dataset.fcPremiumLoading = String(visible);
      status.setAttribute("aria-hidden", String(!visible));
      status.title = visible ? "Cargando paginas del foro" : "";
      if (text instanceof HTMLElement) {
        text.textContent = `Cargando paginas ${loadedPages}`;
      }
    }
    function installForumLiveSearch(root) {
      const input = root?.querySelector("input[name='query']");
      if (!(input instanceof HTMLInputElement)) {
        return;
      }
      if (input.dataset.fcPremiumLiveSearchInstalled === "true") {
        return;
      }
      input.dataset.fcPremiumLiveSearchInstalled = "true";
      input.addEventListener("input", () => {
        options.scheduleForumLiveSearch(input.value);
      });
    }
    function detachMovedForumSearchForm(controlsTable) {
      const form = document.querySelector("form[name='busca'][action*='forocoches_search']");
      if (form instanceof HTMLFormElement && controlsTable.contains(form)) {
        form.remove();
        return form;
      }
      return null;
    }
    function refreshExistingForumControlsRow(table) {
      table.classList.add("fc-premium-forum-controls-table");
      table.removeAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE);
      const toggleCell = table.querySelector(".fc-premium-forum-sidebar-toggle-cell");
      if (toggleCell instanceof HTMLTableCellElement) {
        const button = getOrCreateForumSidebarToggleButton();
        if (!toggleCell.contains(button)) {
          toggleCell.textContent = "";
          toggleCell.append(button);
        }
      }
      installForumLiveSearch(table);
      if (!document.getElementById(FORUM_LOADING_STATUS_ID)) {
        const searchCell = table.querySelector(`#${FORUM_SEARCH_SLOT_ID}`);
        if (searchCell instanceof HTMLElement) {
          searchCell.append(createForumLoadingStatus());
        }
      }
      renderForumLoadingStatus();
    }
    function renderForumControlsRow() {
      const existing = document.getElementById(FORUM_CONTROLS_ROW_ID);
      if (existing instanceof HTMLTableElement) {
        refreshExistingForumControlsRow(existing);
        return existing;
      }
      const table = getNativeForumControlsTable();
      if (!(table instanceof HTMLTableElement)) {
        renderForumLoadingStatus();
        return null;
      }
      const newThreadLink = table.querySelector("a[href*='newthread.php'][href*='do=newthread']");
      const pager = table.querySelector(".pagenav");
      const searchForm = detachMovedForumSearchForm(table);
      const button = getOrCreateForumSidebarToggleButton();
      newThreadLink?.remove();
      pager?.remove();
      button.remove();
      table.id = FORUM_CONTROLS_ROW_ID;
      table.classList.add("fc-premium-forum-controls-table");
      table.removeAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE);
      const body = table.tBodies[0] || table.createTBody();
      body.textContent = "";
      const row = body.insertRow();
      const toggleCell = row.insertCell();
      toggleCell.className = "smallfont fc-premium-forum-sidebar-toggle-cell";
      toggleCell.append(button);
      const newThreadCell = row.insertCell();
      newThreadCell.className = "smallfont fc-premium-forum-new-thread-cell";
      if (newThreadLink) {
        newThreadCell.append(newThreadLink);
      }
      const searchCell = row.insertCell();
      searchCell.id = FORUM_SEARCH_SLOT_ID;
      searchCell.className = "smallfont fc-premium-forum-search-cell";
      if (searchForm) {
        searchCell.append(searchForm);
      } else {
        moveForumHeaderSearchForm(searchCell);
      }
      installForumLiveSearch(searchCell);
      searchCell.append(createForumLoadingStatus());
      const pagerCell = row.insertCell();
      pagerCell.className = "smallfont fc-premium-forum-pager-cell";
      pagerCell.align = "right";
      if (pager) {
        pagerCell.append(pager);
      }
      renderForumLoadingStatus();
      return table;
    }
    function renderForumSidebarToggle(mainCell) {
      if (renderForumControlsRow()) {
        document.getElementById(FORUM_SIDEBAR_TOGGLE_BAR_ID)?.remove();
        return;
      }
      let bar = document.getElementById(FORUM_SIDEBAR_TOGGLE_BAR_ID);
      if (!(bar instanceof HTMLElement)) {
        bar = document.createElement("div");
        bar.id = FORUM_SIDEBAR_TOGGLE_BAR_ID;
      }
      bar.textContent = "";
      bar.append(getOrCreateForumSidebarToggleButton());
      const anchor = getForumThreadListHeaderTable() || getForumThreadsTable();
      if (anchor?.parentElement === mainCell) {
        mainCell.insertBefore(bar, anchor);
      } else if (bar.parentElement !== mainCell) {
        mainCell.prepend(bar);
      }
    }
    function applyForumSidebarVisibility() {
      const panel = getRelatedForumsPanel();
      if (!panel) {
        return;
      }
      const sidebarCell = getForumSidebarCell(panel);
      if (!sidebarCell) {
        return;
      }
      const mainCell = getForumMainCell(sidebarCell);
      if (!mainCell) {
        return;
      }
      document.body.classList.toggle(FORUM_SIDEBAR_HIDDEN_CLASS, forumSidebarHidden);
      setForumLayoutElementHidden(sidebarCell, forumSidebarHidden);
      const spacerCell = getForumSidebarSpacerCell(sidebarCell);
      if (spacerCell) {
        setForumLayoutElementHidden(spacerCell, forumSidebarHidden);
      }
      setForumMainCellExpanded(mainCell, forumSidebarHidden);
      renderForumSidebarToggle(mainCell);
    }
    function enhanceForumDisplayPage() {
      options.ensureStyle();
      hideUnusedTopNavigationBars();
      removeForumTitleTables();
      applyForumSidebarVisibility();
      renderForumControlsRow();
      renderHiddenThreadsToolbarButton2();
      hideUnusedTopNavigationBars();
    }
    return {
      renderForumControlsRow,
      renderForumLoadingStatus,
      enhanceForumDisplayPage,
      renderHiddenThreadsToolbarButton: renderHiddenThreadsToolbarButton2,
      isHiddenThreadsModalOpen: isHiddenThreadsModalOpen2,
      closeHiddenThreadsModal: closeHiddenThreadsModal2,
      renderHiddenThreadsModalBody: renderHiddenThreadsModalBody2,
      openHiddenThreadsModal: openHiddenThreadsModal2
    };
  }

  // src/app/core/forumPageKeyboardController.ts
  function createForumPageKeyboardController(handlers) {
    function handleNavigationKeyDown(event) {
      if (isEditableTarget(event.target)) {
        return false;
      }
      if ((event.key === KEY_NAV_NEXT_POST || event.key === KEY_NAV_PREVIOUS_POST) && hasKeyboardModifier(event)) {
        return false;
      }
      if ((event.key === KEY_NAV_NEXT_PAGE || event.key === KEY_NAV_PREVIOUS_PAGE) && hasKeyboardModifier(event)) {
        return false;
      }
      if (event.key === KEY_OPEN_SHORTCUT_HELP) {
        event.preventDefault();
        setShortcutHelpPopoverOpen(true);
        return true;
      }
      if (event.key === KEY_CLEAR_ACTIVE_VIEW && isShortcutHelpPopoverOpen()) {
        event.preventDefault();
        closeShortcutHelpPopover();
        return true;
      }
      if (event.key === KEY_NAV_NEXT_POST) {
        if (!hasKeyboardModifier(event)) {
          event.preventDefault();
          handlers.moveNavigation(1);
          return true;
        }
        return false;
      }
      if (event.key === KEY_NAV_PREVIOUS_POST) {
        if (!hasKeyboardModifier(event)) {
          event.preventDefault();
          handlers.moveNavigation(-1);
          return true;
        }
        return false;
      }
      if (event.key === KEY_NAV_PREVIOUS_PAGE) {
        event.preventDefault();
        return handlers.navigateForumPage(-1);
      }
      if (event.key === KEY_NAV_NEXT_PAGE) {
        event.preventDefault();
        return handlers.navigateForumPage(1);
      }
      if (event.key === KEY_OPEN_SELECTED_THREAD_IN_NEW_TAB && handlers.isOpenSelectedThreadInNewTabShortcut(event)) {
        event.preventDefault();
        handlers.openSelectedForumThreadInNewTab();
        return true;
      }
      if (event.key === KEY_CLEAR_ACTIVE_VIEW) {
        if (handlers.isHiddenThreadsModalOpen()) {
          event.preventDefault();
          handlers.closeHiddenThreadsModal();
          return true;
        }
        if (handlers.activeTagFilterExists()) {
          event.preventDefault();
          handlers.clearTagFilter();
          return true;
        }
      }
      if (event.key === KEY_HIDE_SELECTED_THREAD) {
        event.preventDefault();
        handlers.hideSelectedForumThread();
        return true;
      }
      if (event.key === KEY_QUOTE_SELECTED_POST && !handlers.isThreadPage()) {
        event.preventDefault();
        handlers.openSelectedNavigationItem();
        return true;
      }
      return false;
    }
    return { handleNavigationKeyDown };
  }

  // src/domain/forumThreads.ts
  function getForumSearchTokens(query2) {
    return normalizeLayoutText(query2).split(/\s+/).filter(Boolean);
  }
  function forumThreadMatchesSearchTokens(record, tokens) {
    if (tokens.length === 0) {
      return true;
    }
    const text = normalizeLayoutText(record.title);
    return tokens.every((token) => text.includes(token));
  }
  function sortForumThreadRecords(records) {
    return records.slice().sort((left, right) => {
      if (left.lastSeen !== right.lastSeen) {
        return right.lastSeen - left.lastSeen;
      }
      if (left.recentIndex !== right.recentIndex) {
        return left.recentIndex - right.recentIndex;
      }
      return right.updatedAt - left.updatedAt;
    });
  }
  function getVisibleForumThreadRecords(records) {
    return records.filter((record) => !record.isHidden);
  }
  function getHiddenForumThreadRecords(records) {
    return sortForumThreadRecords(records.filter((record) => record.isHidden)).sort((left, right) => right.hiddenAt - left.hiddenAt);
  }
  function filterForumThreadRecords(records, filters) {
    const tokens = getForumSearchTokens(filters.searchQuery);
    return sortForumThreadRecords(getVisibleForumThreadRecords(records).filter((record) => (!filters.tag || record.tags.includes(filters.tag)) && forumThreadMatchesSearchTokens(record, tokens)));
  }

  // src/adapters/forocoches/forumThreadParser.ts
  function getTitleTags(title) {
    const source = title.title || normalizeText(title.textContent);
    return getTagsFromText(source);
  }
  function collectForumThreadRecords(doc, sourceUrl, forumId, pageNumber, pageSize, scrapeStartedAt) {
    const table = getForumThreadsTableFromDocument(doc);
    if (!table) {
      return [];
    }
    const rows = Array.from(table.querySelectorAll("tr")).filter((row) => row instanceof HTMLTableRowElement && row.querySelector(THREAD_TITLE_SELECTOR));
    return rows.map((row, index) => {
      const title = row.querySelector(THREAD_TITLE_SELECTOR);
      const url = title instanceof HTMLAnchorElement ? toUrl(title.getAttribute("href") || title.href) : null;
      const threadId = url ? getThreadId(url) : null;
      return threadId ? createForumThreadRecordFromRow(row, threadId, sourceUrl, forumId, pageNumber, pageSize, index, scrapeStartedAt) : null;
    }).filter((record) => record !== null);
  }
  function getSerializableForumThreadRowHtml(row, sourceUrl) {
    const clone = row.cloneNode(true);
    if (!(clone instanceof HTMLElement)) {
      return row.outerHTML;
    }
    clone.removeAttribute(SELECTED_ATTRIBUTE);
    clone.removeAttribute(HIDDEN_THREAD_ATTRIBUTE);
    clone.querySelectorAll(`[${SELECTED_ATTRIBUTE}]`).forEach((element) => {
      element.removeAttribute(SELECTED_ATTRIBUTE);
    });
    clone.querySelectorAll(`[${HIDDEN_THREAD_ATTRIBUTE}]`).forEach((element) => {
      element.removeAttribute(HIDDEN_THREAD_ATTRIBUTE);
    });
    for (const link of clone.querySelectorAll("a[href]")) {
      if (link instanceof HTMLAnchorElement) {
        link.href = new URL(link.getAttribute("href") || "", sourceUrl).href;
      }
    }
    for (const image of clone.querySelectorAll("img[src]")) {
      if (image instanceof HTMLImageElement) {
        image.src = new URL(image.getAttribute("src") || "", sourceUrl).href;
      }
    }
    return clone.outerHTML;
  }
  function getForumThreadsTableFromDocument(doc) {
    const table = doc.getElementById("threadslist");
    if (table instanceof HTMLTableElement) {
      return table;
    }
    const title = doc.querySelector(THREAD_TITLE_SELECTOR);
    const owner = title?.closest("table");
    return owner instanceof HTMLTableElement ? owner : null;
  }
  function createForumThreadRecordFromRow(row, threadId, sourceUrl, forumId, pageNumber, pageSize, pageIndex, scrapeStartedAt) {
    const title = row.querySelector(THREAD_TITLE_SELECTOR);
    if (!(title instanceof HTMLAnchorElement)) {
      return null;
    }
    const titleText = normalizeText(title.textContent);
    const cells = Array.from(row.cells);
    const titleCell = title.closest("td");
    const titleCellIndex = titleCell instanceof HTMLTableCellElement ? cells.indexOf(titleCell) : -1;
    const author = normalizeText(titleCell?.querySelector(".smallfont span")?.textContent);
    const lastPostCell = titleCellIndex >= 0 ? cells[titleCellIndex + 1] : null;
    const statsCell = titleCellIndex >= 0 ? cells[titleCellIndex + 2] : null;
    const recentIndex = (pageNumber - 1) * pageSize + pageIndex;
    return {
      version: FORUM_THREAD_CACHE_RECORD_VERSION,
      id: threadId,
      forumId,
      url: new URL(title.getAttribute("href") || "", sourceUrl).href,
      title: titleText,
      tags: getTagsFromText(titleText),
      html: getSerializableForumThreadRowHtml(row, sourceUrl),
      preview: normalizeText(titleCell?.getAttribute("title")),
      author,
      lastPostText: normalizeText(lastPostCell?.textContent),
      statsText: normalizeText(statsCell?.getAttribute("title") || statsCell?.textContent),
      rowText: normalizeText(row.textContent),
      sourcePage: pageNumber,
      sourceIndex: pageIndex,
      recentIndex,
      lastSeen: scrapeStartedAt,
      updatedAt: Date.now(),
      isHidden: false,
      hiddenAt: 0
    };
  }

  // src/adapters/forocoches/forumThreadListDom.ts
  function renderVisibleForumThreadTitleTags(root, renderTaggedTitle) {
    for (const title of root.querySelectorAll(THREAD_TITLE_SELECTOR)) {
      if (title instanceof HTMLAnchorElement) {
        renderTaggedTitle(title);
      }
    }
  }
  function renderForumThreadRowsFromHtml(options) {
    const table = getForumThreadsTable();
    if (!table || options.signature === options.currentSignature) {
      return false;
    }
    const template = document.createElement("template");
    template.innerHTML = [
      ...options.headerRowHtml,
      ...options.rowHtmlList
    ].join("");
    for (const body2 of Array.from(table.tBodies)) {
      body2.remove();
    }
    const body = table.createTBody();
    body.append(template.content);
    renderVisibleForumThreadTitleTags(body, options.renderTaggedTitle);
    return true;
  }
  function restoreForumThreadRowsFromHtml(options) {
    if (options.nativeRowHtml.length > 0) {
      return renderForumThreadRowsFromHtml({
        headerRowHtml: options.headerRowHtml,
        rowHtmlList: options.nativeRowHtml,
        signature: options.nativeSignature,
        currentSignature: options.currentSignature,
        renderTaggedTitle: options.renderTaggedTitle
      });
    }
    return false;
  }
  function applyHiddenForumThreadRows(hiddenThreadIds) {
    const table = getForumThreadsTable();
    if (!table) {
      return;
    }
    for (const row of Array.from(table.rows)) {
      const title = row.querySelector(THREAD_TITLE_SELECTOR);
      if (!(title instanceof HTMLAnchorElement)) {
        row.removeAttribute(HIDDEN_THREAD_ATTRIBUTE);
        continue;
      }
      const threadId = getThreadId(new URL(title.href));
      if (threadId && hiddenThreadIds.has(threadId)) {
        row.setAttribute(HIDDEN_THREAD_ATTRIBUTE, "true");
      } else {
        row.removeAttribute(HIDDEN_THREAD_ATTRIBUTE);
      }
    }
  }

  // src/app/core/forumTagsController.ts
  function createForumTagsController(options) {
    function createTagChip(tag) {
      return TagChip({
        tag,
        onToggle: toggleTagFilter
      });
    }
    function renderTaggedTitle(title) {
      if (title.dataset.fcPremiumTagsRendered === "true") {
        return;
      }
      const originalTitle = normalizeText(title.textContent);
      if (findTagsInText(originalTitle).length === 0) {
        return;
      }
      title.dataset.fcPremiumTagsRendered = "true";
      title.title = originalTitle;
      title.textContent = "";
      for (const part of splitTextByTags(originalTitle)) {
        if (part.type === "text") {
          title.append(document.createTextNode(part.text));
        } else if (part.tag) {
          title.append(createTagChip(part.tag));
        }
      }
    }
    function enhanceThreadTitleTags() {
      options.ensureStyle();
      for (const title of document.querySelectorAll(THREAD_TITLE_SELECTOR)) {
        if (title instanceof HTMLAnchorElement) {
          renderTaggedTitle(title);
        }
      }
      if (isForumDisplayPage()) {
        renderTopTagBar();
      }
    }
    function renderVisibleForumThreadTitleTags2(root = document) {
      renderVisibleForumThreadTitleTags(root, renderTaggedTitle);
    }
    function toggleTagFilter(tag) {
      if (!isForumDisplayPage()) {
        return;
      }
      options.setActiveTagFilter(options.getActiveTagFilter() === tag ? null : tag);
      options.setActiveForumTagPage(1);
      options.syncForumTagUrl({ history: "push" });
      options.refreshForumTagUi();
    }
    function clearTagFilter() {
      if (!options.getActiveTagFilter()) {
        return;
      }
      options.setActiveTagFilter(null);
      options.setActiveForumTagPage(1);
      options.syncForumTagUrl({ history: "push" });
      options.refreshForumTagUi();
    }
    function getTopTitleTags() {
      const tagsByName = new Map;
      let titleIndex = 0;
      const forumRecords = options.getVisibleCachedForumThreadsForCurrentForum();
      if (forumRecords.length > 0) {
        for (const record of sortForumThreadRecords(forumRecords)) {
          for (const tag of record.tags) {
            const summary = tagsByName.get(tag);
            if (summary) {
              summary.count += 1;
            } else {
              tagsByName.set(tag, {
                tag,
                count: 1,
                firstIndex: titleIndex
              });
            }
          }
          titleIndex += 1;
        }
        return Array.from(tagsByName.values()).sort(compareTopTagSummary).slice(0, 12);
      }
      for (const title of document.querySelectorAll(THREAD_TITLE_SELECTOR)) {
        if (!(title instanceof HTMLAnchorElement)) {
          continue;
        }
        for (const tag of getTitleTags(title)) {
          const summary = tagsByName.get(tag);
          if (summary) {
            summary.count += 1;
          } else {
            tagsByName.set(tag, { tag, count: 1, firstIndex: titleIndex });
          }
        }
        titleIndex += 1;
      }
      return Array.from(tagsByName.values()).sort(compareTopTagSummary).slice(0, 12);
    }
    function compareTopTagSummary(left, right) {
      if (left.count !== right.count) {
        return right.count - left.count;
      }
      return left.firstIndex - right.firstIndex;
    }
    function renderTopTagBar() {
      document.getElementById(TOP_TAGS_ID)?.remove();
      if (!isForumDisplayPage()) {
        return;
      }
      const topTags = getTopTitleTags();
      if (topTags.length === 0) {
        return;
      }
      const table = getForumThreadsTable();
      if (!table?.parentElement) {
        return;
      }
      table.before(TopTagBar({
        tags: topTags,
        activeTag: options.getActiveTagFilter(),
        onToggle: toggleTagFilter
      }));
    }
    return {
      renderTaggedTitle,
      enhanceThreadTitleTags,
      renderVisibleForumThreadTitleTags: renderVisibleForumThreadTitleTags2,
      toggleTagFilter,
      clearTagFilter,
      renderTopTagBar
    };
  }

  // src/services/threadCache/db.ts
  var threadCacheDbPromise = null;
  function canUseThreadCache() {
    return typeof indexedDB !== "undefined";
  }
  function openThreadCacheDb() {
    if (threadCacheDbPromise) {
      return threadCacheDbPromise;
    }
    if (!canUseThreadCache()) {
      return Promise.reject(new Error("IndexedDB no esta disponible"));
    }
    threadCacheDbPromise = new Promise((resolve, reject) => {
      const request = indexedDB.open(THREAD_CACHE_DB_NAME, THREAD_CACHE_DB_VERSION);
      request.onupgradeneeded = () => {
        const db = request.result;
        const store = db.objectStoreNames.contains(THREAD_CACHE_STORE_NAME) ? request.transaction?.objectStore(THREAD_CACHE_STORE_NAME) : db.createObjectStore(THREAD_CACHE_STORE_NAME, {
          keyPath: "threadId"
        });
        if (store && !store.indexNames.contains("savedAt")) {
          store.createIndex("savedAt", "savedAt", { unique: false });
        }
        const forumStore = db.objectStoreNames.contains(FORUM_THREAD_CACHE_STORE_NAME) ? request.transaction?.objectStore(FORUM_THREAD_CACHE_STORE_NAME) : db.createObjectStore(FORUM_THREAD_CACHE_STORE_NAME, {
          keyPath: "id"
        });
        if (forumStore && !forumStore.indexNames.contains("forumId")) {
          forumStore.createIndex("forumId", "forumId", { unique: false });
        }
        if (forumStore && !forumStore.indexNames.contains("lastSeen")) {
          forumStore.createIndex("lastSeen", "lastSeen", { unique: false });
        }
        if (forumStore && !forumStore.indexNames.contains("isHidden")) {
          forumStore.createIndex("isHidden", "isHidden", { unique: false });
        }
        if (forumStore && !forumStore.indexNames.contains("hiddenAt")) {
          forumStore.createIndex("hiddenAt", "hiddenAt", { unique: false });
        }
      };
      request.onsuccess = () => {
        const db = request.result;
        db.onversionchange = () => db.close();
        resolve(db);
      };
      request.onerror = () => {
        threadCacheDbPromise = null;
        reject(request.error || new Error("No se pudo abrir IndexedDB"));
      };
      request.onblocked = () => {
        console.warn("Forocoches Premium: otra pestana esta bloqueando la cache");
      };
    });
    return threadCacheDbPromise;
  }
  function waitForIdbRequest(request) {
    return new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => {
        reject(request.error || new Error("Fallo una operacion de IndexedDB"));
      };
    });
  }
  function waitForIdbTransaction(transaction) {
    return new Promise((resolve, reject) => {
      transaction.oncomplete = () => resolve();
      transaction.onerror = () => {
        reject(transaction.error || new Error("Fallo una transaccion de IndexedDB"));
      };
      transaction.onabort = () => {
        reject(transaction.error || new Error("Se aborto una transaccion de IndexedDB"));
      };
    });
  }
  async function readAllFromStore(storeName) {
    const db = await openThreadCacheDb();
    const transaction = db.transaction(storeName, "readonly");
    const records = await waitForIdbRequest(transaction.objectStore(storeName).getAll());
    await waitForIdbTransaction(transaction);
    return Array.isArray(records) ? records : [];
  }
  async function deleteFromStore(storeName, ids) {
    if (!ids.length) {
      return;
    }
    const db = await openThreadCacheDb();
    const transaction = db.transaction(storeName, "readwrite");
    const store = transaction.objectStore(storeName);
    ids.forEach((id) => store.delete(id));
    await waitForIdbTransaction(transaction);
  }
  async function upsertInStore(storeName, records) {
    if (records.length === 0) {
      return;
    }
    const db = await openThreadCacheDb();
    const transaction = db.transaction(storeName, "readwrite");
    const store = transaction.objectStore(storeName);
    for (const record of records) {
      store.put(record);
    }
    await waitForIdbTransaction(transaction);
  }

  // src/services/threadCache/validation.ts
  var LEGACY_THREAD_CACHE_RECORD_VERSION = 2;
  function isCachedPostRecord(value) {
    if (!value || typeof value !== "object") {
      return false;
    }
    const post = value;
    return typeof post.id === "string" && typeof post.html === "string" && typeof post.author === "string" && typeof post.postNumber === "string" && Number.isFinite(post.pageNumber) && Number.isFinite(post.pageIndex) && Number.isFinite(post.originalIndex) && Array.isArray(post.quotedPostIds);
  }
  function normalizeCachedPostRecord(post) {
    return {
      id: post.id,
      html: post.html,
      author: post.author,
      postNumber: post.postNumber,
      pageNumber: Number(post.pageNumber),
      pageIndex: Number(post.pageIndex),
      originalIndex: Number(post.originalIndex),
      quotedPostIds: post.quotedPostIds.filter(Boolean),
      replyingPostIds: [],
      isOriginalPoster: false,
      replyCount: 0
    };
  }
  function estimateThreadCacheByteSize(value) {
    try {
      return getStringByteSize(JSON.stringify(value));
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo medir la cache", error);
      return 0;
    }
  }
  function getStringByteSize(value) {
    const text = String(value || "");
    if (typeof TextEncoder !== "undefined") {
      return new TextEncoder().encode(text).byteLength;
    }
    return text.length * 2;
  }
  function normalizeThreadCacheRecord(value) {
    if (!value || typeof value !== "object") {
      return null;
    }
    const record = value;
    const isSupportedVersion = record.version === THREAD_CACHE_RECORD_VERSION || record.version === LEGACY_THREAD_CACHE_RECORD_VERSION;
    if (!isSupportedVersion || typeof record.threadId !== "string" || !Number.isFinite(record.totalPages) || !Array.isArray(record.cachedPageNumbers) || !Array.isArray(record.posts)) {
      return null;
    }
    const posts = record.posts.filter(isCachedPostRecord).map(normalizeCachedPostRecord);
    if (posts.length === 0) {
      return null;
    }
    return {
      version: THREAD_CACHE_RECORD_VERSION,
      threadId: record.threadId,
      totalPages: Number(record.totalPages),
      lastSeenPageNumber: Number.isFinite(record.lastSeenPageNumber) ? Number(record.lastSeenPageNumber) : Number(record.totalPages),
      cachedPageNumbers: record.cachedPageNumbers.map(Number).filter((pageNumber) => Number.isFinite(pageNumber) && pageNumber > 0),
      savedAt: Number(record.savedAt) || 0,
      byteSize: Number(record.byteSize) || estimateThreadCacheByteSize(record),
      posts
    };
  }
  function getRawThreadCacheRecordId(value) {
    if (!value || typeof value !== "object") {
      return null;
    }
    const threadId = value.threadId;
    return typeof threadId === "string" ? threadId : null;
  }
  function normalizeForumThreadRecord(value) {
    if (!value || typeof value !== "object") {
      return null;
    }
    const record = value;
    if (record.version !== FORUM_THREAD_CACHE_RECORD_VERSION || typeof record.id !== "string" || typeof record.forumId !== "string" || typeof record.url !== "string" || typeof record.title !== "string" || typeof record.html !== "string" || !Array.isArray(record.tags)) {
      return null;
    }
    const title = normalizeText(record.title);
    return {
      version: record.version,
      id: record.id,
      forumId: record.forumId,
      url: record.url,
      title,
      tags: getTagsFromText(title),
      html: record.html,
      preview: normalizeText(record.preview),
      author: normalizeText(record.author),
      lastPostText: normalizeText(record.lastPostText),
      statsText: normalizeText(record.statsText),
      rowText: normalizeText(record.rowText),
      sourcePage: Number(record.sourcePage) || 1,
      sourceIndex: Number(record.sourceIndex) || 0,
      recentIndex: Number(record.recentIndex) || 0,
      lastSeen: Number(record.lastSeen) || 0,
      updatedAt: Number(record.updatedAt) || 0,
      isHidden: Boolean(record.isHidden),
      hiddenAt: Number(record.hiddenAt) || 0
    };
  }
  function isThreadCacheExpired(cache2) {
    return Date.now() - cache2.savedAt > THREAD_CACHE_MAX_AGE_MS;
  }

  // src/services/threadCache/threadCache.ts
  async function deleteThreadCacheRecord(threadId) {
    if (!canUseThreadCache()) {
      return;
    }
    const db = await openThreadCacheDb();
    const transaction = db.transaction(THREAD_CACHE_STORE_NAME, "readwrite");
    transaction.objectStore(THREAD_CACHE_STORE_NAME).delete(threadId);
    await waitForIdbTransaction(transaction);
  }
  async function getAllThreadCacheRecords() {
    return readAllFromStore(THREAD_CACHE_STORE_NAME);
  }
  async function deleteThreadCacheRecords(threadIds) {
    if (!threadIds.length) {
      return;
    }
    await deleteFromStore(THREAD_CACHE_STORE_NAME, threadIds);
  }
  function clearLegacyThreadCaches() {
    try {
      for (let index = localStorage.length - 1;index >= 0; index -= 1) {
        const key = localStorage.key(index);
        if (key?.startsWith(THREAD_CACHE_LEGACY_STORAGE_PREFIX)) {
          localStorage.removeItem(key);
        }
      }
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo limpiar la cache antigua", error);
    }
  }
  async function cleanupThreadCache() {
    if (!canUseThreadCache()) {
      return;
    }
    clearLegacyThreadCaches();
    try {
      const rawRecords = await getAllThreadCacheRecords();
      const records = [];
      const threadIdsToDelete = new Set;
      rawRecords.forEach((rawRecord) => {
        const record = normalizeThreadCacheRecord(rawRecord);
        const rawThreadId = getRawThreadCacheRecordId(rawRecord);
        if (!record) {
          if (rawThreadId) {
            threadIdsToDelete.add(rawThreadId);
          }
          return;
        }
        if (isThreadCacheExpired(record)) {
          threadIdsToDelete.add(record.threadId);
          return;
        }
        records.push(record);
      });
      let totalBytes = records.reduce((total, record) => total + (record.byteSize || estimateThreadCacheByteSize(record)), 0);
      records.slice().sort((left, right) => left.savedAt - right.savedAt).forEach((record) => {
        if (totalBytes <= THREAD_CACHE_MAX_BYTES) {
          return;
        }
        threadIdsToDelete.add(record.threadId);
        totalBytes -= record.byteSize || estimateThreadCacheByteSize(record);
      });
      await deleteThreadCacheRecords(Array.from(threadIdsToDelete));
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo limpiar la cache", error);
    }
  }
  async function readCurrentThreadCache() {
    const threadId = getThreadId(new URL(location.href));
    if (!threadId || !canUseThreadCache()) {
      return null;
    }
    try {
      const db = await openThreadCacheDb();
      const transaction = db.transaction(THREAD_CACHE_STORE_NAME, "readonly");
      const rawRecord = await waitForIdbRequest(transaction.objectStore(THREAD_CACHE_STORE_NAME).get(threadId));
      await waitForIdbTransaction(transaction);
      const record = normalizeThreadCacheRecord(rawRecord);
      if (!record) {
        return null;
      }
      if (isThreadCacheExpired(record)) {
        await deleteThreadCacheRecord(threadId);
        return null;
      }
      return record;
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo leer la cache", error);
      return null;
    }
  }
  function isCompleteThreadCache(cache2) {
    const cachedPages = new Set(cache2.cachedPageNumbers);
    return cache2.totalPages > 0 && cachedPages.size >= cache2.totalPages && cache2.posts.length > 0;
  }
  async function writeCurrentThreadCache(posts, totalPages, cachedPageNumbers, lastSeenPageNumber = totalPages) {
    const threadId = getThreadId(new URL(location.href));
    if (!threadId || !canUseThreadCache() || posts.length === 0 || cachedPageNumbers.size === 0) {
      return;
    }
    const record = {
      version: THREAD_CACHE_RECORD_VERSION,
      threadId,
      totalPages,
      lastSeenPageNumber,
      cachedPageNumbers: Array.from(cachedPageNumbers).sort((left, right) => left - right),
      savedAt: Date.now(),
      byteSize: 0,
      posts: posts.map(normalizeCachedPostRecord)
    };
    record.byteSize = estimateThreadCacheByteSize(record);
    if (record.byteSize > THREAD_CACHE_MAX_BYTES) {
      console.warn("Forocoches Premium: este hilo supera el limite de cache configurado");
      return;
    }
    try {
      await cleanupThreadCache();
      await upsertInStore(THREAD_CACHE_STORE_NAME, [record]);
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo guardar la cache", error);
    }
  }
  async function clearCurrentThreadCache() {
    const threadId = getThreadId(new URL(location.href));
    clearLegacyThreadCaches();
    if (!threadId) {
      return;
    }
    try {
      await deleteThreadCacheRecord(threadId);
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo borrar la cache", error);
    }
  }
  // src/services/threadCache/forumCache.ts
  async function readForumThreadCacheRecords() {
    if (!canUseThreadCache()) {
      return [];
    }
    try {
      const rawRecords = await readAllFromStore(FORUM_THREAD_CACHE_STORE_NAME);
      return Array.isArray(rawRecords) ? rawRecords.map(normalizeForumThreadRecord).filter((record) => record !== null) : [];
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo leer la cache del foro", error);
      return [];
    }
  }
  async function writeForumThreadCacheRecords(records) {
    if (!canUseThreadCache() || records.length === 0) {
      return;
    }
    try {
      await upsertInStore(FORUM_THREAD_CACHE_STORE_NAME, records);
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo guardar la cache del foro", error);
    }
  }
  async function deleteForumThreadCacheRecords(threadIds) {
    if (!canUseThreadCache() || threadIds.length === 0) {
      return;
    }
    await deleteFromStore(FORUM_THREAD_CACHE_STORE_NAME, threadIds);
  }
  async function cleanupForumThreadCache() {
    if (!canUseThreadCache()) {
      return;
    }
    try {
      const records = await readForumThreadCacheRecords();
      if (records.length <= FORUM_THREAD_CACHE_MAX_RECORDS) {
        return;
      }
      const idsToDelete = records.slice().sort((left, right) => {
        if (left.isHidden !== right.isHidden) {
          return left.isHidden ? 1 : -1;
        }
        const leftHasTags = left.tags.length > 0;
        const rightHasTags = right.tags.length > 0;
        if (leftHasTags !== rightHasTags) {
          return leftHasTags ? 1 : -1;
        }
        if (left.lastSeen !== right.lastSeen) {
          return left.lastSeen - right.lastSeen;
        }
        return left.recentIndex - right.recentIndex;
      }).slice(0, records.length - FORUM_THREAD_CACHE_MAX_RECORDS).map((record) => record.id);
      await deleteForumThreadCacheRecords(idsToDelete);
    } catch (error) {
      console.warn("Forocoches Premium: no se pudo limpiar la cache del foro", error);
    }
  }
  // src/adapters/forocoches/threadParser.ts
  var FETCH_THREAD_DOCUMENT_TIMEOUT_MS = 1e4;
  function getMaxThreadPage(doc) {
    const currentUrl = new URL(location.href);
    const currentThreadId = getThreadId(currentUrl) || getThreadIdFromDocument(doc);
    const lastPageFromLink = getLastThreadPageFromLink(doc, currentThreadId);
    if (lastPageFromLink) {
      return lastPageFromLink;
    }
    let maxPage = getPageNumber(currentUrl);
    for (const link of doc.querySelectorAll("a[href*='showthread.php']")) {
      if (!(link instanceof HTMLAnchorElement)) {
        continue;
      }
      const url = toUrl(link.getAttribute("href") || link.href);
      if (!url || getThreadId(url) !== currentThreadId) {
        continue;
      }
      maxPage = Math.max(maxPage, getPageNumber(url));
    }
    return maxPage;
  }
  function getLastThreadPageFromLink(doc, currentThreadId) {
    let lastPage = null;
    for (const link of doc.querySelectorAll("a[href*='showthread.php']")) {
      if (!(link instanceof HTMLAnchorElement)) {
        continue;
      }
      const label = normalizeLayoutText(`${link.textContent || ""} ${link.title || ""}`);
      if (!label.includes("ultimo") && !label.includes("ultima") && !label.includes("last")) {
        continue;
      }
      const url = toUrl(link.getAttribute("href") || link.href);
      if (!url || getThreadId(url) !== currentThreadId) {
        continue;
      }
      lastPage = Math.max(lastPage || 1, getPageNumber(url));
    }
    return lastPage;
  }
  function getThreadIdFromDocument(doc = document) {
    for (const link of doc.querySelectorAll("a[href*='showthread.php?t=']")) {
      if (!(link instanceof HTMLAnchorElement)) {
        continue;
      }
      const url = toUrl(link.getAttribute("href") || link.href);
      const threadId = url ? getThreadId(url) : null;
      if (threadId) {
        return threadId;
      }
    }
    return null;
  }
  function getPostWrapper(doc, postTable) {
    const posts = doc.querySelector(POSTS_SELECTOR);
    if (!posts) {
      return postTable;
    }
    let wrapper = postTable;
    while (wrapper.parentElement && wrapper.parentElement !== posts) {
      wrapper = wrapper.parentElement;
    }
    return posts.contains(wrapper) ? wrapper : postTable;
  }
  function getQuotedPostId(href) {
    const url = toUrl(href);
    if (!url) {
      return null;
    }
    const fromPostParam = url.searchParams.get("p");
    if (fromPostParam && /^\d+$/.test(fromPostParam)) {
      return fromPostParam;
    }
    const hashMatch = url.hash.match(/^#post(\d+)$/);
    return hashMatch?.[1] || null;
  }
  function getQuotedPostIds(doc, postId) {
    const message = doc.getElementById(`post_message_${postId}`);
    if (!message) {
      return [];
    }
    const quotedIds = new Set;
    for (const link of message.querySelectorAll("a[href*='showthread.php?p='][href*='#post']")) {
      if (!(link instanceof HTMLAnchorElement)) {
        continue;
      }
      const quotedPostId = getQuotedPostId(link.getAttribute("href") || link.href);
      if (quotedPostId && quotedPostId !== postId) {
        quotedIds.add(quotedPostId);
      }
    }
    return Array.from(quotedIds);
  }
  function collectPosts(doc, pageNumber, pageOffset) {
    const posts = [];
    const seenWrappers = new Set;
    for (const table of doc.querySelectorAll(POST_TABLE_SELECTOR)) {
      if (!(table instanceof HTMLTableElement) || !/^post\d+$/.test(table.id)) {
        continue;
      }
      const id = table.id.replace(/^post/, "");
      const wrapper = getPostWrapper(doc, table);
      if (seenWrappers.has(wrapper)) {
        continue;
      }
      seenWrappers.add(wrapper);
      const author = normalizeText(doc.querySelector(`#postmenu_${id} .bigusername`)?.textContent || doc.querySelector(`#postmenu_${id}`)?.textContent);
      const postNumber = normalizeText(doc.querySelector(`#postcount${id}`)?.textContent) || String(pageOffset + posts.length + 1);
      const postNumberValue = Number(postNumber);
      posts.push({
        id,
        html: wrapper.outerHTML,
        author,
        postNumber,
        pageNumber,
        pageIndex: posts.length,
        originalIndex: Number.isFinite(postNumberValue) && postNumberValue > 0 ? postNumberValue - 1 : pageOffset + posts.length,
        quotedPostIds: getQuotedPostIds(doc, id),
        replyingPostIds: [],
        isOriginalPoster: false,
        replyCount: 0
      });
    }
    return posts;
  }
  function parseHtml(html) {
    return new DOMParser().parseFromString(html, "text/html");
  }
  async function fetchThreadDocument(url) {
    const controller = new AbortController;
    const timeoutId = window.setTimeout(() => controller.abort(), FETCH_THREAD_DOCUMENT_TIMEOUT_MS);
    try {
      const response = await fetch(url, {
        cache: "no-cache",
        credentials: "same-origin",
        signal: controller.signal
      });
      if (!response.ok) {
        throw new Error(`Could not load ${url}: ${response.status}`);
      }
      return parseHtml(await response.text());
    } finally {
      window.clearTimeout(timeoutId);
    }
  }

  // src/app/core/cacheOperation.ts
  async function runCacheOperation(operation, fallback, label, timeoutMs = 3000) {
    let timeoutId = 0;
    try {
      return await Promise.race([
        operation,
        new Promise((resolve) => {
          timeoutId = window.setTimeout(() => resolve(fallback), timeoutMs);
        })
      ]);
    } catch (error) {
      console.warn(`Forocoches Premium: fallo en cache (${label})`, error);
      return fallback;
    } finally {
      window.clearTimeout(timeoutId);
    }
  }

  // src/app/core/forumThreadCacheController.ts
  function createForumThreadCacheController(options) {
    let forumThreadScrapeStarted = false;
    let cachedForumThreads = [];
    function getCachedForumThreadsForCurrentForum() {
      const forumId = getForumId();
      return cachedForumThreads.filter((record) => record.forumId === forumId);
    }
    function getVisibleCachedForumThreadsForCurrentForum() {
      return getVisibleForumThreadRecords(getCachedForumThreadsForCurrentForum());
    }
    function getHiddenForumThreadRecordsForCurrentForum() {
      return getHiddenForumThreadRecords(getCachedForumThreadsForCurrentForum());
    }
    function getForumThreadRecordsForTag(tag) {
      return filterForumThreadRecords(getCachedForumThreadsForCurrentForum(), {
        tag,
        searchQuery: options.getActiveForumSearchQuery()
      });
    }
    function getCachedForumThreadIdsForCurrentForum() {
      return new Set(getCachedForumThreadsForCurrentForum().map((record) => record.id));
    }
    function wereAllForumThreadRecordsAlreadyCached(records, cachedThreadIds) {
      return records.length > 0 && records.every((record) => cachedThreadIds.has(record.id));
    }
    function mergeCachedForumThreadRecords(records) {
      if (records.length === 0) {
        return;
      }
      const byId = new Map(cachedForumThreads.map((record) => [record.id, record]));
      for (const record of records) {
        const previous = byId.get(record.id);
        byId.set(record.id, {
          ...record,
          isHidden: Boolean(previous?.isHidden || record.isHidden),
          hiddenAt: previous?.hiddenAt || record.hiddenAt || 0
        });
      }
      cachedForumThreads = Array.from(byId.values());
    }
    function collectCurrentForumThreadRecords() {
      const forumId = getForumId();
      if (!forumId) {
        return [];
      }
      return collectForumThreadRecords(document, location.href, forumId, getPageNumber(new URL(location.href)), options.getForumThreadsPerPage(), Date.now());
    }
    function getCurrentForumThreadRecord(threadId) {
      return collectCurrentForumThreadRecords().find((record) => record.id === threadId) || null;
    }
    async function cacheCurrentForumThreadRows() {
      const records = collectCurrentForumThreadRecords();
      if (records.length === 0) {
        return;
      }
      mergeCachedForumThreadRecords(records);
      await runCacheOperation(writeForumThreadCacheRecords(records.map((record) => cachedForumThreads.find((cachedRecord) => cachedRecord.id === record.id)).filter((record) => record !== undefined)), undefined, "guardar pagina actual");
    }
    async function setForumThreadHiddenState(threadId, hidden) {
      if (!threadId) {
        return false;
      }
      const now = Date.now();
      let existing = cachedForumThreads.find((record2) => record2.id === threadId) || getCurrentForumThreadRecord(threadId);
      if (hidden && getCachedForumThreadsForCurrentForum().length === 0) {
        await cacheCurrentForumThreadRows();
        existing = cachedForumThreads.find((record2) => record2.id === threadId) || existing;
      }
      if (!existing) {
        return false;
      }
      const record = {
        ...existing,
        isHidden: hidden,
        hiddenAt: hidden ? now : 0,
        updatedAt: now
      };
      cachedForumThreads = cachedForumThreads.filter((cachedRecord) => cachedRecord.id !== threadId).concat(record);
      await runCacheOperation(writeForumThreadCacheRecords([record]), undefined, "guardar hilo oculto");
      options.refreshForumTagUi();
      if (options.isHiddenThreadsModalOpen()) {
        options.renderHiddenThreadsModalBody();
      }
      return true;
    }
    async function hideSelectedForumThread() {
      const selected = options.getSelectedNavigationItem();
      const link = selected?.link;
      const threadId = link ? getThreadId(new URL(link.href)) : null;
      if (!threadId) {
        return false;
      }
      const previousIndex = Math.max(options.getNavigationItems().findIndex((item) => item === selected), 0);
      const hidden = await setForumThreadHiddenState(threadId, true);
      if (hidden && options.getNavigationLength() > 0) {
        options.selectNavigationIndex(Math.min(previousIndex, options.getNavigationLength() - 1));
      }
      return hidden;
    }
    function getForumRecentPageUrl(pageNumber) {
      const url = new URL(location.href);
      clearForumStateQueryParams(url);
      url.hash = "";
      url.searchParams.delete("page");
      if (pageNumber > 1) {
        url.searchParams.set("page", String(pageNumber));
      }
      return url;
    }
    function replaceForumPagersFromDocument(doc) {
      const currentPagers = Array.from(document.querySelectorAll(".pagenav"));
      const nextPagers = Array.from(doc.querySelectorAll(".pagenav"));
      currentPagers.forEach((pager, index) => {
        const nextPager = nextPagers[index];
        if (pager instanceof HTMLElement && nextPager instanceof HTMLElement) {
          pager.innerHTML = nextPager.innerHTML;
        }
      });
    }
    async function loadForumDisplayPageWithJavascript(url) {
      const forumId = getForumId(url);
      if (!forumId) {
        location.href = url.href;
        return;
      }
      options.setForumThreadLoadState({ isLoading: true });
      try {
        const pageNumber = getPageNumber(url);
        const doc = pageNumber === getPageNumber(new URL(location.href)) && url.pathname === location.pathname ? parseHtml(document.documentElement.outerHTML) : await fetchThreadDocument(url.href);
        const records = collectForumThreadRecords(doc, url.href, forumId, pageNumber, options.getForumThreadsPerPage(), Date.now());
        if (records.length === 0) {
          location.href = url.href;
          return;
        }
        replaceForumPagersFromDocument(doc);
        options.setActiveTagFilter(null);
        options.setActiveForumSearchQuery("");
        options.setActiveForumTagPage(pageNumber);
        options.setNativeForumThreadRows(records.map((record) => record.html), records.length, `native-page-${pageNumber}`);
        options.applyHiddenForumThreadRows();
        options.updateBrowserHistory(url, "push");
        mergeCachedForumThreadRecords(records);
        await runCacheOperation(writeForumThreadCacheRecords(records.map((record) => cachedForumThreads.find((cachedRecord) => cachedRecord.id === record.id)).filter((record) => record !== undefined)), undefined, "guardar pagina navegada");
        options.renderTopTagBar();
        options.refreshNavigation({ reset: true });
        window.scrollTo({ top: 0, behavior: "auto" });
      } catch (error) {
        console.warn("Forocoches Premium: no se pudo cargar la pagina del foro con JavaScript", error);
        location.href = url.href;
      } finally {
        options.setForumThreadLoadState({ isLoading: false });
      }
    }
    async function scrapeForumThreadPage(pageNumber, scrapeStartedAt) {
      const forumId = getForumId();
      if (!forumId) {
        return [];
      }
      const url = getForumRecentPageUrl(pageNumber);
      const doc = pageNumber === 1 && getPageNumber(new URL(location.href)) === 1 ? document : await fetchThreadDocument(url.href);
      return collectForumThreadRecords(doc, url.href, forumId, pageNumber, options.getForumThreadsPerPage(), scrapeStartedAt);
    }
    async function saveScrapedForumThreadRecords(records) {
      mergeCachedForumThreadRecords(records);
      await runCacheOperation(writeForumThreadCacheRecords(records.map((record) => cachedForumThreads.find((cachedRecord) => cachedRecord.id === record.id)).filter((record) => record !== undefined)), undefined, "guardar scrape");
      options.refreshForumTagUi();
    }
    async function scrapeRecentForumThreadPages(startPage, scrapeStartedAt, cachedThreadIdsBeforeScrape) {
      if (forumThreadScrapeStarted) {
        return;
      }
      forumThreadScrapeStarted = true;
      options.setForumThreadLoadState({ isLoading: true });
      let lastScrapedPage = Math.max(startPage - 1, 0);
      for (let pageNumber = startPage;pageNumber <= FORUM_THREAD_CACHE_RECENT_PAGES; pageNumber += 1) {
        try {
          const records = await scrapeForumThreadPage(pageNumber, scrapeStartedAt);
          const pageWasAlreadyCached = wereAllForumThreadRecordsAlreadyCached(records, cachedThreadIdsBeforeScrape);
          lastScrapedPage = pageNumber;
          options.setForumThreadLoadState({
            loadedPages: Math.max(options.getForumThreadLoadState().loadedPages, pageNumber)
          });
          await saveScrapedForumThreadRecords(records);
          if (pageWasAlreadyCached) {
            break;
          }
        } catch (error) {
          console.warn(`Forocoches Premium: no se pudo cachear la pagina ${pageNumber} del foro`, error);
        } finally {
          options.setForumThreadLoadState({
            loadedPages: Math.max(options.getForumThreadLoadState().loadedPages, pageNumber)
          });
        }
        if (pageNumber < FORUM_THREAD_CACHE_RECENT_PAGES) {
          await sleep(PAGE_LOAD_DELAY_MS);
        }
      }
      try {
        await runCacheOperation(cleanupForumThreadCache(), undefined, "limpiar cache");
        cachedForumThreads = await runCacheOperation(readForumThreadCacheRecords(), cachedForumThreads, "leer cache final");
      } finally {
        const loadedPages = Math.max(options.getForumThreadLoadState().loadedPages, lastScrapedPage);
        options.setForumThreadLoadState({
          loadedPages,
          isLoading: false
        });
        options.refreshForumTagUi();
      }
    }
    async function initializeForumThreadCache() {
      cachedForumThreads = await runCacheOperation(readForumThreadCacheRecords(), [], "leer cache inicial");
      options.setForumThreadLoadState({
        loadedPages: 0,
        targetPages: FORUM_THREAD_CACHE_RECENT_PAGES,
        isLoading: false
      });
      const scrapeStartedAt = Date.now();
      const cachedThreadIdsBeforeScrape = getCachedForumThreadIdsForCurrentForum();
      let firstPageWasAlreadyCached = false;
      try {
        const firstPageRecords = await scrapeForumThreadPage(1, scrapeStartedAt);
        firstPageWasAlreadyCached = wereAllForumThreadRecordsAlreadyCached(firstPageRecords, cachedThreadIdsBeforeScrape);
        await saveScrapedForumThreadRecords(firstPageRecords);
      } catch (error) {
        console.warn("Forocoches Premium: no se pudo cachear la primera pagina del foro", error);
        options.refreshForumTagUi();
      } finally {
        options.setForumThreadLoadState({
          loadedPages: Math.max(options.getForumThreadLoadState().loadedPages, 1)
        });
      }
      if (firstPageWasAlreadyCached) {
        options.setForumThreadLoadState({ isLoading: false });
        return;
      }
      scrapeRecentForumThreadPages(2, scrapeStartedAt, cachedThreadIdsBeforeScrape);
    }
    return {
      getCachedForumThreadsForCurrentForum,
      getVisibleCachedForumThreadsForCurrentForum,
      getHiddenForumThreadRecordsForCurrentForum,
      getForumThreadRecordsForTag,
      loadForumDisplayPageWithJavascript,
      initializeForumThreadCache,
      setForumThreadHiddenState,
      hideSelectedForumThread
    };
  }

  // src/domain/pagination.ts
  function getVisiblePageNumbers(totalPages, currentPage) {
    if (totalPages <= 11) {
      return Array.from({ length: totalPages }, (_value, index) => index + 1);
    }
    const page = currentPage || 1;
    const maxVisible = 11;
    const halfWindow = Math.floor(maxVisible / 2);
    const start = Math.max(1, Math.min(page - halfWindow, totalPages - maxVisible + 1));
    return Array.from({ length: maxVisible }, (_value, index) => start + index);
  }

  // src/ui/components/ForumPager.tsx
  function ForumPager(props) {
    return renderElement(/* @__PURE__ */ u2("table", {
      className: "tborder",
      cellPadding: "3",
      cellSpacing: "1",
      ...{ border: "0" },
      children: /* @__PURE__ */ u2("tbody", {
        children: /* @__PURE__ */ u2("tr", {
          children: [
            /* @__PURE__ */ u2("td", {
              className: "vbmenu_control",
              style: "font-weight: normal",
              children: [
                "Pág ",
                props.currentPage,
                " de ",
                props.totalPages
              ]
            }, undefined, true, undefined, this),
            props.visiblePages.map((pageNumber) => pageNumber === props.currentPage ? /* @__PURE__ */ u2("td", {
              className: "alt2",
              children: /* @__PURE__ */ u2("span", {
                className: "mfont",
                title: "Mostrando resultados filtrados",
                children: /* @__PURE__ */ u2("strong", {
                  children: pageNumber
                }, undefined, false, undefined, this)
              }, undefined, false, undefined, this)
            }, undefined, false, undefined, this) : /* @__PURE__ */ u2(ForumPagerLinkCell, {
              pageNumber,
              label: String(pageNumber),
              href: props.hrefForPage(pageNumber),
              onPageClick: props.onPageClick
            }, undefined, false, undefined, this)),
            props.currentPage < props.totalPages ? /* @__PURE__ */ u2(ForumPagerLinkCell, {
              pageNumber: props.currentPage + 1,
              label: ">",
              href: props.hrefForPage(props.currentPage + 1),
              onPageClick: props.onPageClick
            }, undefined, false, undefined, this) : null,
            props.currentPage < props.totalPages ? /* @__PURE__ */ u2(ForumPagerLinkCell, {
              pageNumber: props.totalPages,
              label: "Último »",
              href: props.hrefForPage(props.totalPages),
              onPageClick: props.onPageClick
            }, undefined, false, undefined, this) : null
          ]
        }, undefined, true, undefined, this)
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this));
  }
  function ForumPagerLinkCell(props) {
    return /* @__PURE__ */ u2("td", {
      className: "alt1",
      children: /* @__PURE__ */ u2("a", {
        className: "mfont",
        href: props.href,
        onClick: (event) => {
          event.preventDefault();
          props.onPageClick(props.pageNumber);
        },
        children: props.label
      }, undefined, false, undefined, this)
    }, undefined, false, undefined, this);
  }

  // src/app/core/forumThreadListRenderer.ts
  function createForumThreadListRenderer(options) {
    let renderedForumThreadListSignature = null;
    let forumThreadsPerPage = FORUM_THREAD_FALLBACK_PAGE_SIZE;
    let nativeForumThreadRowHtml = [];
    let nativeForumThreadHeaderRowHtml = [];
    let nativeForumPagerHtml = [];
    function captureNativeForumThreadRows() {
      if (nativeForumThreadRowHtml.length > 0) {
        return;
      }
      const table = getForumThreadsTable();
      if (!table) {
        return;
      }
      const rows = Array.from(table.rows);
      const firstThreadIndex = rows.findIndex((row) => row.querySelector(THREAD_TITLE_SELECTOR));
      const threadRows = firstThreadIndex >= 0 ? rows.slice(firstThreadIndex) : rows;
      nativeForumThreadHeaderRowHtml = firstThreadIndex > 0 ? rows.slice(0, firstThreadIndex).map((row) => row.outerHTML) : [];
      nativeForumThreadRowHtml = threadRows.map((row) => row.outerHTML);
      forumThreadsPerPage = threadRows.filter((row) => row.querySelector(THREAD_TITLE_SELECTOR)).length || FORUM_THREAD_FALLBACK_PAGE_SIZE;
      renderedForumThreadListSignature = getForumThreadRowsSignature(nativeForumThreadRowHtml, "native");
      captureNativeForumPagers();
    }
    function captureNativeForumPagers() {
      if (nativeForumPagerHtml.length > 0) {
        return;
      }
      nativeForumPagerHtml = Array.from(document.querySelectorAll(".pagenav")).filter((pager) => pager instanceof HTMLElement).map((pager) => pager.innerHTML);
    }
    function replaceNativeForumPagers() {
      nativeForumPagerHtml = Array.from(document.querySelectorAll(".pagenav")).filter((pager) => pager instanceof HTMLElement).map((pager) => pager.innerHTML);
    }
    function restoreNativeForumPagers() {
      for (const [index, pager] of Array.from(document.querySelectorAll(".pagenav")).entries()) {
        if (!(pager instanceof HTMLElement) || !nativeForumPagerHtml[index]) {
          continue;
        }
        const container = pager.closest("table[width='100%']") || pager;
        if (container instanceof HTMLElement) {
          setForumLayoutElementHidden(container, false);
        }
        pager.innerHTML = nativeForumPagerHtml[index];
      }
    }
    function getForumThreadsPerPage() {
      return forumThreadsPerPage || FORUM_THREAD_FALLBACK_PAGE_SIZE;
    }
    function getForumDynamicPageUrl(pageNumber) {
      return options.getForumDynamicPageUrl(pageNumber);
    }
    function renderNativeForumPagers(total) {
      const pageSize = getForumThreadsPerPage();
      const totalPages = getForumThreadListTotalPages(total, pageSize);
      const currentPage = clampForumThreadListPage(options.getActiveForumTagPage(), totalPages);
      options.setActiveForumTagPage(currentPage);
      for (const pager of document.querySelectorAll(".pagenav")) {
        if (!(pager instanceof HTMLElement)) {
          continue;
        }
        const container = pager.closest("table[width='100%']") || pager;
        if (container instanceof HTMLElement) {
          setForumLayoutElementHidden(container, false);
        }
        pager.textContent = "";
        pager.append(ForumPager({
          currentPage,
          totalPages,
          visiblePages: getVisiblePageNumbers(totalPages, currentPage),
          hrefForPage: (pageNumber) => getForumDynamicPageUrl(pageNumber).href,
          onPageClick: options.setForumTagPage
        }));
      }
    }
    function renderForumThreadRows(rowHtmlList, signature) {
      const changed = renderForumThreadRowsFromHtml({
        headerRowHtml: nativeForumThreadHeaderRowHtml,
        rowHtmlList,
        signature,
        currentSignature: renderedForumThreadListSignature,
        renderTaggedTitle: options.renderTaggedTitle
      });
      if (!changed) {
        return false;
      }
      renderedForumThreadListSignature = signature;
      return true;
    }
    function setNativeForumThreadRows(rowHtmlList, pageSize, signatureKey) {
      nativeForumThreadRowHtml = rowHtmlList;
      forumThreadsPerPage = pageSize || FORUM_THREAD_FALLBACK_PAGE_SIZE;
      renderedForumThreadListSignature = null;
      replaceNativeForumPagers();
      return renderForumThreadRows(nativeForumThreadRowHtml, getForumThreadRowsSignature(nativeForumThreadRowHtml, signatureKey));
    }
    function restoreNativeForumThreadRows() {
      const nativeSignature = getForumThreadRowsSignature(nativeForumThreadRowHtml, "native");
      const changed = restoreForumThreadRowsFromHtml({
        headerRowHtml: nativeForumThreadHeaderRowHtml,
        nativeRowHtml: nativeForumThreadRowHtml,
        nativeSignature,
        currentSignature: renderedForumThreadListSignature,
        renderTaggedTitle: options.renderTaggedTitle
      });
      if (changed) {
        renderedForumThreadListSignature = nativeSignature;
      }
      restoreNativeForumPagers();
      options.renderVisibleForumThreadTitleTags();
      return changed;
    }
    function applyHiddenForumThreadRows2() {
      const hiddenThreadIds = new Set(options.getHiddenForumThreadRecordsForCurrentForum().map((record) => record.id));
      applyHiddenForumThreadRows(hiddenThreadIds);
    }
    function renderForumThreadList() {
      if (!isForumDisplayPage()) {
        return false;
      }
      captureNativeForumThreadRows();
      const activeTagFilter = options.getActiveTagFilter();
      const activeForumSearchQuery = options.getActiveForumSearchQuery();
      const cachedForumRecords = options.getCachedForumThreadsForCurrentForum();
      const records = options.getForumThreadRecordsForTag(activeTagFilter);
      if (!activeTagFilter && !activeForumSearchQuery) {
        const changed2 = restoreNativeForumThreadRows();
        applyHiddenForumThreadRows2();
        return changed2;
      }
      if (cachedForumRecords.length === 0) {
        const changed2 = restoreNativeForumThreadRows();
        applyHiddenForumThreadRows2();
        return changed2;
      }
      const page = getForumThreadListPage(records, options.getActiveForumTagPage(), getForumThreadsPerPage());
      options.setActiveForumTagPage(page.currentPage);
      const signature = getForumThreadRowsSignature(page.rowHtmlList, [
        "dynamic",
        activeTagFilter || "",
        activeForumSearchQuery,
        page.currentPage,
        page.pageSize
      ].join(":"));
      const changed = renderForumThreadRows(page.rowHtmlList, signature);
      renderNativeForumPagers(records.length);
      return changed;
    }
    return {
      captureNativeForumThreadRows,
      getForumThreadsPerPage,
      setNativeForumThreadRows,
      applyHiddenForumThreadRows: applyHiddenForumThreadRows2,
      renderForumThreadList
    };
  }

  // src/app/core/forumPageController.ts
  function createForumPageController() {
    const initialForumQueryState = readForumQueryState();
    let activeTagFilter = initialForumQueryState.tag;
    let activeForumTagPage = initialForumQueryState.page;
    let activeForumSearchQuery = "";
    let forumLiveSearchTimer = 0;
    let forumThreadLoadState = {
      loadedPages: 0,
      targetPages: FORUM_THREAD_CACHE_RECENT_PAGES,
      isLoading: false
    };
    function getPostsElement() {
      const posts = document.querySelector(POSTS_SELECTOR);
      return posts instanceof HTMLElement ? posts : null;
    }
    function updateBrowserHistory(url, historyMode) {
      if (historyMode === "push" && url.href !== location.href) {
        window.history.pushState(window.history.state, "", url.href);
        return;
      }
      window.history.replaceState(window.history.state, "", url.href);
    }
    function collectNavigationItems() {
      if (isForumDisplayPage()) {
        return getThreadTitleNavigationItems();
      }
      if (isThreadPage()) {
        return getPostNavigationItems(getPostsElement());
      }
      return [];
    }
    const navigationController = createNavigationController({
      collectNavigationItems,
      onRenderNavigationStatus: renderNavigationStatus,
      onUpdateSelectedThreadUrl: updateSelectedPostUrl,
      getPostsElement
    });
    function renderNavigationStatus() {
      document.getElementById(NAVIGATION_STATUS_ID)?.remove();
    }
    function updateSelectedPostUrl(selected) {
      const postId = getPostIdFromNavigationElement(selected.element);
      if (!postId) {
        return;
      }
      const threadId = getThreadId(new URL(location.href));
      if (!threadId) {
        return;
      }
      const url = new URL(location.href);
      url.searchParams.set("t", threadId);
      url.hash = `post${postId}`;
      window.history.replaceState(window.history.state, "", url.href);
    }
    const refreshNavigation = navigationController.refreshNavigation;
    const moveNavigation = navigationController.moveNavigation;
    const selectNavigationIndex = navigationController.selectNavigationIndex;
    const selectNavigationElement = navigationController.selectNavigationElement;
    const getSelectedNavigationItem = navigationController.getSelectedNavigationItem;
    const getNavigationLength = navigationController.getNavigationLength;
    const getNavigationItems = navigationController.getNavigationItems;
    function isOpenSelectedThreadInNewTabShortcut(event) {
      if (!isForumDisplayPage()) {
        return false;
      }
      return isOpenInNewTabKeyboardShortcut(event, KEY_OPEN_SELECTED_THREAD_IN_NEW_TAB);
    }
    function openSelectedForumThreadInNewTab() {
      if (!isForumDisplayPage()) {
        return false;
      }
      const selected = getSelectedNavigationItem();
      if (!selected?.link) {
        return false;
      }
      window.open(selected.link.href, "_blank", "noopener");
      return true;
    }
    function openSelectedNavigationItem() {
      const selected = getSelectedNavigationItem();
      if (!selected?.link) {
        return;
      }
      selected.link.click();
    }
    function navigateForumPage(direction) {
      if (!isForumDisplayPage()) {
        return false;
      }
      const currentPage = activeTagFilter || activeForumSearchQuery ? activeForumTagPage : getPageNumber(new URL(location.href));
      const targetPage = currentPage + direction;
      if (targetPage < 1) {
        return false;
      }
      if (activeTagFilter || activeForumSearchQuery) {
        const totalPages = getForumThreadListTotalPages(getForumThreadRecordsForTag(activeTagFilter).length, getForumThreadsPerPage());
        const clampedPage = clampForumThreadListPage(targetPage, totalPages);
        if (clampedPage === currentPage) {
          return false;
        }
        setForumTagPage(clampedPage);
        return true;
      }
      const targetUrl = Array.from(document.querySelectorAll(".pagenav a[href*='forumdisplay.php']")).filter((link) => link instanceof HTMLAnchorElement).map((link) => toUrl(link.getAttribute("href") || link.href)).find((url) => url && url.pathname === location.pathname && getForumId(url) === getForumId() && getPageNumber(url) === targetPage);
      if (!targetUrl) {
        return false;
      }
      loadForumDisplayPageWithJavascript(targetUrl);
      return true;
    }
    function renderShortcutHelpButton2() {
      renderShortcutHelpButton({ items: getShortcutHelpItems(), formatKey: formatShortcutHelpKey });
    }
    function installForumKeyboardNavigation() {
      window.addEventListener("keydown", forumPageKeyboard.handleNavigationKeyDown, true);
    }
    function ensureStyle() {
      const existing = document.getElementById(STYLE_ID);
      const style = existing instanceof HTMLStyleElement ? existing : document.createElement("style");
      style.id = STYLE_ID;
      style.textContent = `#fc-premium-thread-summary {
  align-items: center;
  background: #f7faff;
  border: 1px solid #b7d1ff;
  border-radius: 4px;
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-wrap: nowrap;
  font: 11px/1.3 Verdana, Arial, sans-serif;
  gap: 8px;
  margin: 6px auto;
  max-width: 100%;
  min-height: 27px;
  overflow: hidden;
  padding: 4px 6px;
}

#fc-premium-thread-summary[hidden] {
  display: none !important;
}

#fc-premium-thread-summary.fc-premium-summary-stuck {
  box-shadow: 0 4px 12px rgba(23, 50, 77, 0.16);
}

#fc-premium-thread-summary strong {
  color: #0b57d0;
}

#fc-premium-thread-progress {
  align-items: center;
  color: #3c4043;
  display: inline-flex;
  flex: 0 0 auto;
  gap: 5px;
  white-space: nowrap;
}

.fc-premium-spinner {
  animation: fc-premium-spin 720ms linear infinite;
  border: 2px solid #c7d8ff;
  border-radius: 999px;
  border-top-color: #0b57d0;
  display: inline-block;
  height: 12px;
  width: 12px;
}

@keyframes fc-premium-spin {
  to {
    transform: rotate(360deg);
  }
}

#fc-premium-shortcut-help-container {
  box-sizing: border-box;
  display: flex;
  justify-content: flex-end;
  margin: 4px 8px 0;
  min-height: 23px;
  position: relative;
  z-index: 50;
}

#fc-premium-shortcut-help-button {
  align-items: center;
  background: rgba(247, 250, 255, 0.78);
  border: 1px solid rgba(95, 143, 199, 0.45);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(23, 50, 77, 0.12);
  box-sizing: border-box;
  color: rgba(23, 50, 77, 0.72);
  cursor: pointer;
  display: inline-flex;
  font: 700 12px/1 Verdana, Arial, sans-serif;
  height: 21px;
  justify-content: center;
  opacity: 0.45;
  padding: 0;
  width: 21px;
}

#fc-premium-shortcut-help-button:hover,
#fc-premium-shortcut-help-button:focus-visible,
#fc-premium-shortcut-help-button[aria-expanded="true"] {
  background: #f7faff;
  border-color: #5f8fc7;
  color: #17324d;
  opacity: 0.95;
  outline: none;
}

#fc-premium-shortcut-help-popover {
  background: #f7faff;
  border: 1px solid #5f8fc7;
  box-shadow: 2px 2px 0 rgba(23, 50, 77, 0.18);
  box-sizing: border-box;
  color: #17324d;
  font: 11px/1.35 Verdana, Arial, sans-serif;
  max-width: calc(100vw - 16px);
  padding: 8px;
  position: absolute;
  right: 0;
  top: 25px;
  width: 310px;
  z-index: 51;
}

#fc-premium-shortcut-help-popover[hidden] {
  display: none !important;
}

.fc-premium-shortcut-help-title {
  border-bottom: 1px solid #b7d1ff;
  font-weight: 700;
  margin-bottom: 6px;
  padding-bottom: 5px;
}

.fc-premium-shortcut-help-row {
  align-items: center;
  display: flex;
  gap: 8px;
  justify-content: space-between;
  padding: 3px 0;
}

.fc-premium-shortcut-help-keys {
  display: inline-flex;
  flex: 0 0 auto;
  gap: 3px;
}

.fc-premium-shortcut-help-key {
  background: #fff;
  border: 1px solid #9db7e5;
  border-radius: 2px;
  box-shadow: inset 0 -1px 0 #d8e4fb;
  color: #17324d;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  min-width: 16px;
  padding: 3px 5px;
  text-align: center;
  white-space: nowrap;
}

.fc-premium-shortcut-help-description {
  min-width: 0;
  text-align: right;
}

#fc-premium-thread-controls {
  display: flex;
  flex: 0 0 auto;
  gap: 5px;
  margin-left: auto;
}

#fc-premium-thread-controls button {
  background: #fff;
  border: 1px solid #b7d1ff;
  border-radius: 4px;
  color: #17324d;
  cursor: pointer;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  padding: 4px 6px;
}

#fc-premium-thread-controls button:disabled {
  color: #80868b;
  cursor: default;
  opacity: 0.72;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls {
  display: table-cell;
  margin-left: 0;
  white-space: nowrap;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls button {
  background: transparent;
  border: 0;
  border-radius: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  padding: 0 3px;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls button:hover {
  text-decoration: underline;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls button:disabled {
  cursor: default;
  opacity: 0.45;
  text-decoration: none;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls #fc-premium-thread-progress {
  margin-left: 8px;
  vertical-align: middle;
}

.fc-premium-thread-header-cell {
  text-align: left;
}

.fc-premium-thread-header-layout {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: space-between;
  min-height: 20px;
  width: 100%;
}

.fc-premium-thread-header-breadcrumbs {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.fc-premium-thread-header-breadcrumbs table {
  width: auto !important;
}

.fc-premium-thread-header-breadcrumbs .navbar {
  font-size: 11px !important;
}

.fc-premium-thread-header-breadcrumbs strong {
  font-size: 11px;
  font-weight: 700;
}

.fc-premium-thread-header-search {
  align-items: center;
  display: flex;
  flex: 0 0 auto;
  gap: 6px;
  white-space: nowrap;
}

.fc-premium-thread-header-search-form {
  align-items: center;
  display: inline-flex;
  gap: 3px;
  margin: 0;
}

.fc-premium-thread-header-search-form .cfield {
  box-sizing: border-box;
  font: 11px Verdana, Arial, sans-serif;
  height: 19px;
  max-width: 24vw;
  width: 190px;
}

.fc-premium-thread-header-search-form .cbutton {
  font: 700 11px Verdana, Arial, sans-serif;
  height: 20px;
  padding: 0 6px;
}

.fc-premium-thread-header-message-search {
  flex: 1;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-panel {
  margin: 0;
  width: 100%;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-panel td {
  padding: 2px 4px;
}

.fc-premium-thread-header-message-search .fc-premium-thread-search-layout {
  gap: 3px 5px;
  grid-template-columns: minmax(120px, 1fr) minmax(100px, 0.85fr) auto auto;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-status {
  grid-column: 1 / -1;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-selected-authors {
  margin-top: 3px;
}

#fc-premium-thread-search-panel {
  margin: 0 0 8px;
  width: 100%;
}

#fc-premium-thread-search-panel .fc-premium-thread-search-cell {
  padding: 5px 6px;
}

.fc-premium-thread-search-layout {
  align-items: end;
  display: grid;
  gap: 5px 8px;
  grid-template-columns: minmax(180px, 1fr) minmax(170px, 250px) auto auto minmax(115px, auto);
}

.fc-premium-thread-search-field {
  color: #17324d;
  display: grid;
  font: 700 10px/1.25 Verdana, Arial, sans-serif;
  gap: 2px;
  min-width: 0;
}

.fc-premium-thread-search-field input {
  border: 1px solid #7f9db9;
  box-sizing: border-box;
  font: 11px Verdana, Arial, sans-serif;
  height: 20px;
  min-width: 0;
  padding: 2px 4px;
  width: 100%;
}

.fc-premium-thread-search-button {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  height: 20px;
  padding: 2px 7px 3px;
  white-space: nowrap;
}

.fc-premium-thread-search-button:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

.fc-premium-thread-search-button:disabled {
  color: #80868b;
  cursor: default;
  opacity: 0.65;
}

#fc-premium-thread-search-selected-authors {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 5px;
}

.fc-premium-thread-author-chip {
  align-items: center;
  background: #f7faff;
  border: 1px solid #9db7e5;
  border-radius: 2px;
  color: #17324d;

  display: inline-flex;
  font: 10px/1 Verdana, Arial, sans-serif;
  gap: 3px;
  padding: 2px 4px;
}

.fc-premium-thread-author-chip button {
  background: transparent;
  border: 0;
  color: #0b57d0;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 0 1px;
}

#fc-premium-thread-search-status {
  color: #3c4043;
  font: 10px/1.25 Verdana, Arial, sans-serif;
  min-width: 0;
  text-align: right;
  white-space: nowrap;
}

#fc-premium-thread-search-empty {
  background: #fff;
  border: 1px solid #b7d1ff;
  box-sizing: border-box;
  color: #3c4043;
  font: 11px/1.35 Verdana, Arial, sans-serif;
  margin: 0 0 8px;
  padding: 8px 10px;
  text-align: center;
}

#fc-premium-thread-search-empty[hidden] {
  display: none !important;
}

#fc-premium-top-tags {
  align-items: center;
  background: #f7faff;
  border: 1px solid #b7d1ff;
  border-radius: 6px;
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-wrap: wrap;
  font: 12px/1.4 Verdana, Arial, sans-serif;
  gap: 6px;
  margin: 10px auto;
  padding: 8px 10px;
}

#fc-premium-top-tags > span:first-child {
  font-weight: 700;
}

#fc-premium-top-tags .fc-premium-tag-chip[aria-pressed="true"] {
  box-shadow: 0 0 0 2px #0b57d0;
}

#fc-premium-top-tags button {
  background: #fff;
  border: 1px solid #9db7e5;
  border-radius: 3px;
  color: #17324d;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 4px 6px;
}

#fc-premium-top-tags button[aria-current="page"] {
  background: #5f8fc7;
  border-color: #3f70a8;
  color: #fff;
  cursor: default;
}

#fc-premium-forum-controls-row {
  margin-bottom: 3px !important;
}

#fc-premium-forum-controls-row td {
  vertical-align: middle;
}

#fc-premium-forum-controls-row .fc-premium-forum-sidebar-toggle-cell,
#fc-premium-forum-controls-row .fc-premium-forum-new-thread-cell {
  padding-right: 6px;
  white-space: nowrap;
  width: 1%;
}

#fc-premium-forum-controls-row .fc-premium-forum-search-cell {
  text-align: left;
  white-space: nowrap;
  width: 100%;
}

#fc-premium-forum-controls-row .fc-premium-forum-pager-cell {
  text-align: right;
  white-space: nowrap;
  width: 1%;
}

#fc-premium-forum-controls-row .fc-premium-thread-header-search-form .cfield {
  max-width: 24vw;
  width: 190px;
}

#fc-premium-forum-loading-status {
  align-items: center;
  color: #3c4043;
  display: inline-flex;
  font: 11px/1.2 Verdana, Arial, sans-serif;
  gap: 5px;
  margin-left: 8px;
  min-width: 138px;
  vertical-align: middle;
  white-space: nowrap;
}

#fc-premium-forum-loading-status[data-fc-premium-loading="false"] {
  visibility: hidden;
}

#fc-premium-forum-sidebar-toggle-bar {
  align-items: center;
  background: #f7faff;
  border: 1px solid #b7d1ff;
  border-radius: 6px;
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-wrap: wrap;
  font: 12px/1.4 Verdana, Arial, sans-serif;
  gap: 8px;
  margin: 0 0 10px;
  padding: 8px 10px;
}

#fc-premium-forum-sidebar-toggle {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 3px 7px 4px;
}

#fc-premium-forum-sidebar-toggle:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

html.fc-premium-modal-open,
body.fc-premium-modal-open {
  overflow: hidden !important;
  overscroll-behavior: none;
}

#fc-premium-hidden-threads-modal {
  align-items: center;
  background: rgba(0, 0, 0, 0.48);
  box-sizing: border-box;
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 20px;
  position: fixed;
  z-index: 2147483645;
}

#fc-premium-hidden-threads-modal[hidden] {
  display: none !important;
}

.fc-premium-hidden-threads-dialog {
  background: #f7faff;
  border: 1px solid #555576;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.28);
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-direction: column;
  font: 11px/1.35 Verdana, Arial, sans-serif;
  height: calc(100vh - 40px);
  max-width: calc(100vw - 40px);
  overflow: hidden;
  width: 860px;
}

.fc-premium-hidden-threads-header {
  align-items: center;
  background: #555576;
  color: #fff;
  display: flex;
  font-weight: 700;
  justify-content: space-between;
  padding: 6px 8px;
}

.fc-premium-hidden-threads-header button,
.fc-premium-hidden-thread-restore {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 3px 7px 4px;
}

.fc-premium-hidden-threads-header button:hover,
.fc-premium-hidden-thread-restore:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

#fc-premium-hidden-threads-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overscroll-behavior: contain;
  overflow: auto;
  padding: 8px;
}

.fc-premium-hidden-threads-table {
  background: #555576;
  border-collapse: separate;
  border-spacing: 1px;
  width: 100%;
}

.fc-premium-hidden-threads-table th {
  background: #d5e6ee;
  color: #17324d;
  font-weight: 700;
  padding: 5px;
  text-align: left;
  white-space: nowrap;
}

.fc-premium-hidden-threads-table td {
  background: #f1f1f1;
  padding: 5px;
  vertical-align: top;
}

.fc-premium-hidden-threads-table tr:nth-child(even) td {
  background: #fff;
}

.fc-premium-hidden-thread-title {
  font-weight: 700;
}

.fc-premium-hidden-thread-meta {
  color: #3c4043;
  font-size: 10px;
  margin-top: 3px;
}

.fc-premium-hidden-threads-empty {
  background: #fff;
  border: 1px solid #b7d1ff;
  padding: 14px;
  text-align: center;
}

.fc-premium-quote-actions {
  align-items: center;
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 3px;
  margin-left: 5px;
  vertical-align: middle;
  white-space: nowrap;
}

.fc-premium-quote-actions button {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 9px/1.1 Verdana, Arial, sans-serif;
  padding: 1px 5px 2px;
}

.fc-premium-quote-actions button:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

tr[data-fc-premium-tag-hidden] {
  display: none !important;
}

[data-fc-premium-layout-hidden] {
  display: none !important;
}

.fc-premium-post-wrapper[data-fc-premium-filter-hidden] {
  display: none !important;
}

.fc-premium-post-wrapper[data-fc-premium-page-hidden] {
  display: none !important;
}

.fc-premium-post-wrapper {
  border-radius: 6px;
  margin: 0 0 12px;
  position: relative;
  transition: transform 160ms ease;
}

.fc-premium-post-footer-row {
  display: none !important;
}

.fc-premium-post-reply-actions {
  align-items: center;
  bottom: 7px;
  display: flex;
  flex-wrap: nowrap;
  gap: 3px;
  line-height: 1;
  position: absolute;

  right: 9px;
  white-space: nowrap;
  z-index: 5;
}

.fc-premium-post-reply-actions a {
  display: inline-flex;
  margin: 0 !important;
}

.fc-premium-post-reply-actions img {
  display: block;
}

.fc-premium-post-wrapper[data-fc-premium-reply-indent] {
  margin-left: 28px;
}

.fc-premium-reply-badge {
  border-right: 1px solid #9aa0a6;
  color: #3c4043;
  display: inline-block;
  float: right;
  font: 700 9px/1 Verdana, Arial, sans-serif;
  margin-left: 8px;
  margin-right: 7px;
  padding-right: 7px;
  vertical-align: baseline;
  white-space: nowrap;
}

.fc-premium-author-filter-button {
  cursor: pointer;
}

.fc-premium-author-filter-button {
  background: #fff;
  border: 1px solid #b7d1ff;
  border-radius: 999px;
  color: #0b57d0;
  display: inline-block;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  margin-left: 5px;
  padding: 3px 6px;
  vertical-align: 1px;
}

.fc-premium-author-filter-button:hover {
  border-color: #0b57d0;
}

.fc-premium-reply-badge a {
  color: inherit;
  font-weight: 700;
  margin-left: 3px;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fc-premium-original-position {
  color: #17324d;
  font-weight: 400;
  margin-left: 6px;
  opacity: 0.8;
}

.fc-premium-reply-badge .fc-premium-original-position {
  color: inherit;
  margin-left: 4px;
  opacity: 0.72;
}

.fc-premium-reply-badge button {
  background: transparent;
  border: 0;
  border-radius: 0;
  color: inherit;
  cursor: pointer;
  font: 700 9px/1 Verdana, Arial, sans-serif;
  margin-left: 4px;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fc-premium-reply-badge button:hover {
  color: #0b57d0;
}

.fc-premium-post-date-cell {
  white-space: nowrap;
  width: 100%;
}

.fc-premium-post-number-cell {
  text-align: right !important;
  width: 1%;
  white-space: nowrap;
}

.fc-premium-header-author {
  color: #3c4043;
  display: none;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  margin-left: 6px;
  position: relative;
  white-space: nowrap;
}

.fc-premium-header-author::after {
  content: "";
  display: none;
  height: 9px;
  left: -8px;
  position: absolute;
  right: -8px;
  top: 100%;
}

.fc-premium-header-author:hover::after,
.fc-premium-header-author:focus-within::after {
  display: block;
}

.fc-premium-header-author > a {
  color: #0b57d0;
  text-decoration: none;
}

.fc-premium-header-author > a:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fc-premium-author-hover-card {
  background: #f0f0f0;
  border: 1px solid #8d98a3;
  border-left-color: #ffffff;
  border-radius: 2px;
  border-top-color: #ffffff;
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.22);
  color: #202124;
  display: none;
  font: 11px/1.4 Verdana, Arial, sans-serif;
  left: 0;
  max-width: min(320px, 80vw);
  min-width: 210px;
  padding: 8px 10px;
  position: absolute;
  text-align: left;
  top: calc(100% + 1px);
  white-space: normal;
  z-index: 9999;
}

.fc-premium-header-author:hover .fc-premium-author-hover-card,
.fc-premium-header-author:focus-within .fc-premium-author-hover-card {
  display: block;
}

.fc-premium-author-hover-card strong {
  color: #17324d;
  display: block;
  font-size: 12px;
  margin-bottom: 4px;
}

.fc-premium-author-hover-card .fc-premium-author-avatar {
  border: 1px solid #9aa3ad;
  border-radius: 2px;
  display: block;
  height: 58px;
  margin: 0 0 7px;
  max-width: 92px;
  object-fit: cover;
  width: auto;
}

.fc-premium-author-hover-card span {
  display: block;
  margin-top: 2px;
}

.fc-premium-author-card-actions {
  border-top: 1px solid #c0c6cc;
  display: flex !important;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 7px !important;
  padding-top: 6px;
}

.fc-premium-author-status,
.fc-premium-author-report-link {
  align-items: center;
  color: #202124;
  display: inline-flex !important;
  gap: 4px;
  margin: 0 !important;
  text-decoration: none;
}

.fc-premium-author-report-link:hover {
  color: #0b57d0;
  text-decoration: underline;
}

.fc-premium-author-status span,
.fc-premium-author-report-link span {
  display: inline;
  margin: 0;
}

a[data-fc-premium-quote-target] {
  outline-offset: 2px;
}

a[data-fc-premium-quote-target]:focus-visible {
  outline: 2px solid #1a73e8;
}

[data-fc-premium-quote-block] {
  transition: opacity 140ms ease;
}

.fc-premium-tag-chip {
  background: var(--fc-premium-tag-bg);
  border: 1px solid var(--fc-premium-tag-border);
  border-radius: 999px;
  color: var(--fc-premium-tag-color);
  display: inline-block;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  margin: 0 2px;
  padding: 3px 6px;
  text-transform: uppercase;
  vertical-align: 1px;
  white-space: nowrap;
}

.fc-premium-tag-chip[role="button"] {
  cursor: pointer;
}

[data-fc-premium-selected] {
  border-radius: 6px;
  outline: 2px solid #1a73e8 !important;
  outline-offset: 3px;
  transition: outline-offset 160ms ease;
}

tr[data-fc-premium-selected] > td {
  background: #eef5ff !important;
}

body #posts table[id^="post"] {
  table-layout: auto !important;
}

body #posts .fc-premium-author-cell {
  display: none !important;
}

body #posts table[id^="post"] td {
  padding-bottom: 4px !important;
  padding-top: 4px !important;
}

body .fc-premium-post-wrapper div[id^="edit"] > br,
body .fc-premium-post-wrapper div[id^="edit"] > table.cajasprin {
  display: none !important;
}

body .fc-premium-post-wrapper {
  margin-bottom: 6px;
}

body .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
  margin-left: 18px;
}

#\${POSTS_SELECTOR.slice(1)}[data-fc-premium-graph-view="quoted-by"] .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
  margin-left: 34px;
}

@media (max-width: 700px) {
  .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
    margin-left: 14px;
  }

  #\${POSTS_SELECTOR.slice(1)}[data-fc-premium-graph-view="quoted-by"] .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
    margin-left: 24px;
  }
}

body #fc-premium-thread-summary {
  font-size: 11px;
  margin: 6px auto;
  padding: 6px 8px;
}

body #fc-premium-top-tags,
body #fc-premium-forum-sidebar-toggle-bar {
  margin-bottom: 5px;
  margin-top: 5px;
  padding: 5px 8px;
}

body #fc-premium-thread-controls,
body #fc-premium-thread-progress {
  gap: 4px;
}

body #posts table[id^="post"] {
  font-size: 12px !important;
}

body #posts table[id^="post"] .alt1,
body #posts table[id^="post"] .alt2 {
  padding-left: 6px !important;
  padding-right: 6px !important;
}

body #threadslist td {
  padding-bottom: 2px !important;
  padding-top: 2px !important;
}

body #threadslist,
body #threadslist .mfont,
body #threadslist .smallfont {
  font-size: 12px !important;
}

body .fc-premium-header-author {
  display: inline-block;
}

body table.tborder:has(td.navbar),
body table.tborder:has(.navbar) {
  display: none !important;
}
`;
      if (!existing) {
        document.head.appendChild(style);
      }
    }
    const forumTagsController = createForumTagsController({
      ensureStyle,
      getActiveTagFilter: () => activeTagFilter,
      setActiveTagFilter: (tag) => {
        activeTagFilter = tag;
      },
      setActiveForumTagPage: (page) => {
        activeForumTagPage = page;
      },
      syncForumTagUrl,
      refreshForumTagUi: () => refreshForumTagUi(),
      getVisibleCachedForumThreadsForCurrentForum
    });
    const renderTaggedTitle = forumTagsController.renderTaggedTitle;
    const enhanceThreadTitleTags = forumTagsController.enhanceThreadTitleTags;
    const renderVisibleForumThreadTitleTags2 = forumTagsController.renderVisibleForumThreadTitleTags;
    const clearTagFilter = forumTagsController.clearTagFilter;
    const renderTopTagBar = forumTagsController.renderTopTagBar;
    const forumLayoutController = createForumLayoutController({
      ensureStyle,
      getPostsElement,
      getForumThreadLoadState: () => forumThreadLoadState,
      scheduleForumLiveSearch,
      getHiddenForumThreadRecordsForCurrentForum,
      setForumThreadHiddenState: (threadId, hidden) => setForumThreadHiddenState(threadId, hidden)
    });
    const renderForumControlsRow = forumLayoutController.renderForumControlsRow;
    const renderForumLoadingStatus = forumLayoutController.renderForumLoadingStatus;
    const enhanceForumDisplayPage = forumLayoutController.enhanceForumDisplayPage;
    const renderHiddenThreadsToolbarButton2 = forumLayoutController.renderHiddenThreadsToolbarButton;
    const isHiddenThreadsModalOpen2 = forumLayoutController.isHiddenThreadsModalOpen;
    const closeHiddenThreadsModal2 = forumLayoutController.closeHiddenThreadsModal;
    const renderHiddenThreadsModalBody2 = forumLayoutController.renderHiddenThreadsModalBody;
    function setForumThreadLoadState(state) {
      forumThreadLoadState = {
        ...forumThreadLoadState,
        ...state
      };
      renderForumLoadingStatus();
    }
    function applyForumLiveSearchQuery(query2) {
      const normalizedQuery = normalizeText(query2);
      if (normalizedQuery === activeForumSearchQuery) {
        return;
      }
      activeForumSearchQuery = normalizedQuery;
      activeForumTagPage = 1;
      refreshForumTagUi({ readUrlState: false });
    }
    function scheduleForumLiveSearch(query2) {
      window.clearTimeout(forumLiveSearchTimer);
      forumLiveSearchTimer = window.setTimeout(() => {
        applyForumLiveSearchQuery(query2);
      }, FORUM_LIVE_SEARCH_DEBOUNCE_MS);
    }
    function getCachedForumThreadsForCurrentForum() {
      return forumThreadCacheController.getCachedForumThreadsForCurrentForum();
    }
    function getVisibleCachedForumThreadsForCurrentForum() {
      return forumThreadCacheController.getVisibleCachedForumThreadsForCurrentForum();
    }
    function getHiddenForumThreadRecordsForCurrentForum() {
      return forumThreadCacheController.getHiddenForumThreadRecordsForCurrentForum();
    }
    function getForumThreadRecordsForTag(tag) {
      return forumThreadCacheController.getForumThreadRecordsForTag(tag);
    }
    function syncForumTagUrl(options = {}) {
      if (!isForumDisplayPage()) {
        return;
      }
      const url = new URL(location.href);
      clearForumStateQueryParams(url);
      url.searchParams.delete("page");
      if (activeTagFilter) {
        url.searchParams.set(FORUM_STATE_QUERY_PARAMS.tag, activeTagFilter);
      }
      if (activeForumTagPage > 1) {
        url.searchParams.set("page", String(activeForumTagPage));
      }
      updateBrowserHistory(url, options.history || "replace");
    }
    function getForumDynamicPageUrl(pageNumber) {
      const url = new URL(location.href);
      clearForumStateQueryParams(url);
      url.searchParams.delete("page");
      url.hash = "";
      if (activeTagFilter) {
        url.searchParams.set(FORUM_STATE_QUERY_PARAMS.tag, activeTagFilter);
      }
      if (pageNumber > 1) {
        url.searchParams.set("page", String(pageNumber));
      }
      return url;
    }
    function setForumTagPage(pageNumber) {
      activeForumTagPage = pageNumber;
      if (!activeForumSearchQuery) {
        syncForumTagUrl({ history: "push" });
      }
      refreshForumTagUi({ readUrlState: !activeForumSearchQuery });
      window.scrollTo({ top: 0, behavior: "auto" });
    }
    const forumThreadCacheController = createForumThreadCacheController({
      getActiveForumSearchQuery: () => activeForumSearchQuery,
      setActiveForumSearchQuery: (query2) => {
        activeForumSearchQuery = query2;
      },
      setActiveTagFilter: (tag) => {
        activeTagFilter = tag;
      },
      setActiveForumTagPage: (page) => {
        activeForumTagPage = page;
      },
      getForumThreadsPerPage: () => getForumThreadsPerPage(),
      getForumThreadLoadState: () => forumThreadLoadState,
      setForumThreadLoadState,
      setNativeForumThreadRows: (rows, pageSize, signatureKey) => setNativeForumThreadRows(rows, pageSize, signatureKey),
      applyHiddenForumThreadRows: () => applyHiddenForumThreadRows2(),
      refreshForumTagUi: () => refreshForumTagUi(),
      renderTopTagBar: () => renderTopTagBar(),
      updateBrowserHistory,
      refreshNavigation,
      getSelectedNavigationItem,
      getNavigationItems,
      getNavigationLength,
      selectNavigationIndex,
      isHiddenThreadsModalOpen: isHiddenThreadsModalOpen2,
      renderHiddenThreadsModalBody: renderHiddenThreadsModalBody2
    });
    const loadForumDisplayPageWithJavascript = forumThreadCacheController.loadForumDisplayPageWithJavascript;
    const initializeForumThreadCache = forumThreadCacheController.initializeForumThreadCache;
    const setForumThreadHiddenState = forumThreadCacheController.setForumThreadHiddenState;
    const hideSelectedForumThread = forumThreadCacheController.hideSelectedForumThread;
    const forumThreadListRenderer = createForumThreadListRenderer({
      getActiveTagFilter: () => activeTagFilter,
      getActiveForumTagPage: () => activeForumTagPage,
      setActiveForumTagPage: (page) => {
        activeForumTagPage = page;
      },
      getActiveForumSearchQuery: () => activeForumSearchQuery,
      getCachedForumThreadsForCurrentForum,
      getHiddenForumThreadRecordsForCurrentForum,
      getForumThreadRecordsForTag,
      getForumDynamicPageUrl,
      setForumTagPage,
      renderTaggedTitle,
      renderVisibleForumThreadTitleTags: () => renderVisibleForumThreadTitleTags2()
    });
    const captureNativeForumThreadRows = forumThreadListRenderer.captureNativeForumThreadRows;
    const getForumThreadsPerPage = forumThreadListRenderer.getForumThreadsPerPage;
    const setNativeForumThreadRows = forumThreadListRenderer.setNativeForumThreadRows;
    const applyHiddenForumThreadRows2 = forumThreadListRenderer.applyHiddenForumThreadRows;
    const renderForumThreadList = forumThreadListRenderer.renderForumThreadList;
    function handleForumPageNavigationClick(event) {
      if (event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
        return;
      }
      const link = event.target instanceof Element ? event.target.closest(".pagenav a[href*='forumdisplay.php']") : null;
      if (!(link instanceof HTMLAnchorElement)) {
        return;
      }
      const url = toUrl(link.getAttribute("href") || link.href);
      if (!url || url.pathname !== location.pathname || getForumId(url) !== getForumId()) {
        return;
      }
      event.preventDefault();
      if (activeTagFilter || activeForumSearchQuery) {
        setForumTagPage(getPageNumber(url));
        return;
      }
      loadForumDisplayPageWithJavascript(url);
    }
    function installForumPageNavigation() {
      document.addEventListener("click", handleForumPageNavigationClick, true);
    }
    function refreshForumTagUi(options = {}) {
      if (options.readUrlState !== false) {
        const queryState = readForumQueryState();
        activeTagFilter = queryState.tag;
        if (!activeForumSearchQuery) {
          activeForumTagPage = queryState.page;
        }
      }
      const threadListChanged = renderForumThreadList();
      renderTopTagBar();
      renderForumControlsRow();
      renderHiddenThreadsToolbarButton2();
      refreshNavigation({ reset: threadListChanged });
    }
    function applyForumUrlState() {
      if (!isForumDisplayPage()) {
        return;
      }
      const queryState = readForumQueryState();
      activeTagFilter = queryState.tag;
      activeForumTagPage = queryState.page;
      refreshForumTagUi();
    }
    function installForumHistoryNavigation() {
      window.addEventListener("popstate", applyForumUrlState);
    }
    function initForumPage() {
      if (!isForumDisplayPage()) {
        return;
      }
      enhanceForumDisplayPage();
      installForumHistoryNavigation();
      installForumPageNavigation();
      installForumKeyboardNavigation();
      initializeForumThreadCache();
      refreshNavigation({ reset: true });
      renderShortcutHelpButton2();
    }
    const forumPageKeyboard = createForumPageKeyboardController({
      moveNavigation,
      isOpenSelectedThreadInNewTabShortcut,
      openSelectedForumThreadInNewTab,
      isHiddenThreadsModalOpen: isHiddenThreadsModalOpen2,
      closeHiddenThreadsModal: closeHiddenThreadsModal2,
      activeTagFilterExists: () => Boolean(activeTagFilter),
      clearTagFilter,
      hideSelectedForumThread,
      openSelectedNavigationItem,
      navigateForumPage,
      isThreadPage
    });
    return {
      init: async () => {
        initForumPage();
      },
      handleNavigationKeyDown: forumPageKeyboard.handleNavigationKeyDown,
      refreshNavigation,
      renderTopTagBar,
      renderForumControlsRow
    };
  }

  // src/ui/threadSummaryDom.ts
  function ensureThreadSummary(posts) {
    if (!posts) {
      return null;
    }
    const existing = document.getElementById(THREAD_SUMMARY_ID);
    if (existing instanceof HTMLElement) {
      installStickySummaryShadow(existing);
      return existing;
    }
    const summary = document.createElement("div");
    summary.id = THREAD_SUMMARY_ID;
    posts.before(summary);
    installStickySummaryShadow(summary);
    return summary;
  }
  function renderThreadSummaryMenu(options) {
    const summary = options.summary;
    if (!(summary instanceof HTMLElement)) {
      return;
    }
    summary.textContent = "";
    summary.hidden = true;
    const controlsTarget = renderThreadControls({
      summary,
      state: options.state,
      onRefreshCache: options.onRefreshCache
    });
    if (controlsTarget === summary) {
      summary.hidden = !options.state.isLoading;
      renderThreadProgress(summary, options.state);
    }
  }
  function renderThreadControls(options) {
    document.getElementById(THREAD_CONTROLS_ID)?.remove();
    const threadToolsCell = document.getElementById("threadtools");
    const toolbarRow = threadToolsCell?.parentElement;
    if (!options.summary && !(toolbarRow instanceof HTMLTableRowElement)) {
      return null;
    }
    const controls = toolbarRow instanceof HTMLTableRowElement ? document.createElement("td") : document.createElement("div");
    controls.id = THREAD_CONTROLS_ID;
    if (controls instanceof HTMLTableCellElement) {
      controls.className = "vbmenu_control fc-premium-thread-toolbar-controls";
      controls.noWrap = true;
    }
    const cacheButton = document.createElement("button");
    cacheButton.type = "button";
    cacheButton.textContent = "Actualizar cache";
    cacheButton.title = "Borrar la cache de este hilo y volver a cargar paginas";
    cacheButton.addEventListener("click", () => {
      options.onRefreshCache();
    });
    controls.append(cacheButton);
    renderThreadProgress(controls, options.state);
    if (toolbarRow instanceof HTMLTableRowElement && threadToolsCell instanceof HTMLTableCellElement) {
      toolbarRow.insertBefore(controls, threadToolsCell);
      return controls;
    }
    options.summary?.append(controls);
    return options.summary || null;
  }
  function renderThreadProgress(summary, state) {
    document.getElementById(THREAD_PROGRESS_ID)?.remove();
    if (!(summary instanceof HTMLElement) || !state.isLoading) {
      return;
    }
    const progress = document.createElement("span");
    progress.id = THREAD_PROGRESS_ID;
    const spinner = document.createElement("span");
    spinner.className = "fc-premium-spinner";
    spinner.setAttribute("aria-hidden", "true");
    progress.append(spinner);
    const text = document.createElement("span");
    const pageLabel = `${state.loadedPages}/${state.targetPages}`;
    text.textContent = `paginas ${pageLabel}`;
    progress.append(text);
    summary.append(progress);
  }
  function installStickySummaryShadow(summary) {
    if (!summary || summary.dataset.fcPremiumStickyInstalled === "true") {
      return;
    }
    summary.dataset.fcPremiumStickyInstalled = "true";
    const updateShadow = () => {
      summary.classList.toggle("fc-premium-summary-stuck", summary.getBoundingClientRect().top <= 0);
    };
    window.addEventListener("scroll", updateShadow, { passive: true });
    window.addEventListener("resize", updateShadow);
    updateShadow();
  }

  // src/domain/threadPosts.ts
  function applyReplyCounts(posts) {
    const repliesByPostId = new Map;
    for (const post of posts) {
      for (const quotedPostId of post.quotedPostIds) {
        if (!repliesByPostId.has(quotedPostId)) {
          repliesByPostId.set(quotedPostId, new Set);
        }
        repliesByPostId.get(quotedPostId).add(post.id);
      }
    }
    for (const post of posts) {
      post.replyingPostIds = Array.from(repliesByPostId.get(post.id) || []);
      post.replyCount = post.replyingPostIds.length;
    }
  }
  function createEmptyThreadGraph() {
    return {
      postById: new Map,
      quotedByPostId: new Map,
      quotingByPostId: new Map,
      neighborsByPostId: new Map,
      chronologicalNextByPostId: new Map
    };
  }
  function sortPosts(posts) {
    return posts.slice().sort((left, right) => {
      if (left.replyCount !== right.replyCount) {
        return right.replyCount - left.replyCount;
      }
      return left.originalIndex - right.originalIndex;
    });
  }
  function sortPostsChronologically(posts) {
    return posts.slice().sort((left, right) => left.originalIndex - right.originalIndex);
  }
  function applyOriginalPosterFlags(posts) {
    const firstPost = sortPostsChronologically(posts)[0];
    const originalPoster = firstPost?.author.toLowerCase();
    if (!originalPoster) {
      return;
    }
    for (const post of posts) {
      post.isOriginalPoster = post.author.toLowerCase() === originalPoster;
    }
  }
  function getPromotedCitedPosts(posts, limit) {
    const firstPost = sortPostsChronologically(posts)[0];
    return sortPosts(posts).filter((post) => post.replyCount > 0).slice(0, limit).filter((post) => post.id !== firstPost?.id);
  }
  function getFeaturedChronologicalPosts(posts, options) {
    const chronologicalPosts = sortPostsChronologically(posts);
    if (!options.shouldPromoteCitedPosts) {
      return chronologicalPosts;
    }
    const firstPost = chronologicalPosts[0];
    const promotedPosts = getPromotedCitedPosts(posts, 3);
    if (!firstPost || promotedPosts.length === 0) {
      return chronologicalPosts;
    }
    const promotedPostIds = new Set(promotedPosts.map((post) => post.id));
    return [
      firstPost,
      ...promotedPosts,
      ...chronologicalPosts.filter((post) => post.id !== firstPost.id && !promotedPostIds.has(post.id))
    ];
  }
  function getReplyRankByPostId(posts) {
    const rankByPostId = new Map;
    let rank = 0;
    for (const post of sortPosts(posts)) {
      if (post.replyCount <= 0) {
        continue;
      }
      rank += 1;
      rankByPostId.set(post.id, rank);
    }
    return rankByPostId;
  }
  function buildThreadGraph(posts) {
    const graph = createEmptyThreadGraph();
    const chronologicalPosts = sortPostsChronologically(posts);
    for (const post of chronologicalPosts) {
      graph.postById.set(post.id, post);
      ensureGraphSet(graph.quotedByPostId, post.id);
      ensureGraphSet(graph.quotingByPostId, post.id);
      ensureGraphSet(graph.neighborsByPostId, post.id);
    }
    for (let index = 0;index < chronologicalPosts.length; index += 1) {
      const post = chronologicalPosts[index];
      const nextPost = chronologicalPosts[index + 1] || null;
      if (post) {
        graph.chronologicalNextByPostId.set(post.id, nextPost?.id || null);
      }
    }
    for (const post of chronologicalPosts) {
      for (const quotedPostId of post.quotedPostIds) {
        if (!graph.postById.has(quotedPostId)) {
          continue;
        }
        ensureGraphSet(graph.quotedByPostId, quotedPostId).add(post.id);
        ensureGraphSet(graph.quotingByPostId, post.id).add(quotedPostId);
        ensureGraphSet(graph.neighborsByPostId, quotedPostId).add(post.id);
        ensureGraphSet(graph.neighborsByPostId, post.id).add(quotedPostId);
      }
    }
    return graph;
  }
  function getPostsForGraphView(view, graph, posts) {
    const root = graph.postById.get(view.rootPostId);
    if (!root) {
      return [];
    }
    if (view.type === "quoted-sources") {
      return getChronologicalGraphPosts([...root.quotedPostIds, root.id], posts);
    }
    if (view.type === "quoted-by") {
      const replyPosts = getChronologicalGraphPosts(Array.from(graph.quotedByPostId.get(root.id) || []), posts).filter((post) => post.id !== root.id);
      return [root, ...replyPosts];
    }
    return getConversationChainPosts(view, graph);
  }
  function getValidGraphView(view, graph) {
    if (view && graph.postById.has(view.rootPostId)) {
      return view;
    }
    return null;
  }
  function getThreadViewPosts(options) {
    if (options.activeGraphView) {
      return getPostsForGraphView(options.activeGraphView, options.graph, options.posts);
    }
    return getFeaturedChronologicalPosts(options.posts, {
      shouldPromoteCitedPosts: options.shouldPromoteCitedPosts
    });
  }
  function getReplyIndentDepth(options) {
    if (!options.activeGraphView) {
      return 0;
    }
    if (options.activeGraphView.type === "quoted-by") {
      return options.post.id === options.activeGraphView.rootPostId ? 0 : 1;
    }
    if (options.activeGraphView.type === "conversation") {
      return options.index === 0 ? 0 : 1;
    }
    if (options.activeGraphView.type === "quoted-sources") {
      return options.post.id === options.activeGraphView.rootPostId ? 1 : 0;
    }
    return 0;
  }
  function ensureGraphSet(map, key) {
    if (!map.has(key)) {
      map.set(key, new Set);
    }
    return map.get(key);
  }
  function getChronologicalGraphPosts(postIds, posts) {
    const ids = new Set(postIds);
    return sortPostsChronologically(posts).filter((post) => ids.has(post.id));
  }
  function getConversationParentPostId(post, preferredPostId, graph) {
    if (preferredPostId && post.quotedPostIds.includes(preferredPostId) && graph.postById.has(preferredPostId)) {
      return preferredPostId;
    }
    return post.quotedPostIds.find((postId) => graph.postById.has(postId)) || null;
  }
  function getConversationChainPosts(view, graph) {
    const chain = [];
    const seen = new Set;
    let currentPost = graph.postById.get(view.rootPostId) || null;
    let preferredParentPostId = view.relatedPostId;
    while (currentPost && !seen.has(currentPost.id)) {
      chain.push(currentPost);
      seen.add(currentPost.id);
      const parentPostId = getConversationParentPostId(currentPost, preferredParentPostId, graph);
      preferredParentPostId = null;
      currentPost = parentPostId ? graph.postById.get(parentPostId) || null : null;
    }
    return chain.reverse();
  }

  // src/adapters/forocoches/postReplyActions.ts
  function clickPostQuoteAction(wrapper) {
    const link = getPostReplyActionLink(wrapper, "quote");
    if (!link) {
      return false;
    }
    link.click();
    return true;
  }
  function togglePostMultiquote(wrapper, postId) {
    const link = getPostReplyActionLink(wrapper, "multiquote");
    const target = link?.querySelector("img[id^='mq_']");
    const multiquotePostId = target?.id.replace(/^mq_/, "") || postId;
    if (multiquotePostId && typeof window.mq_click === "function") {
      window.mq_click(multiquotePostId);
      return true;
    }
    if (target instanceof HTMLElement) {
      target.click();
      return true;
    }
    if (!link) {
      return false;
    }
    link.click();
    return true;
  }
  function openThreadReplyWithoutQuote(threadId) {
    const link = getThreadReplyWithoutQuoteLink();
    if (link) {
      link.click();
      return true;
    }
    if (!threadId) {
      return false;
    }
    location.href = new URL(`newreply.php?do=newreply&t=${threadId}`, location.href).href;
    return true;
  }
  function isQuickReplyLink(link) {
    const image = link.querySelector("img");
    const label = `${link.id} ${image?.alt || ""} ${image?.title || ""} ${image?.getAttribute("src") || ""}`;
    return /quickreply|respuesta rapida|qr_\d+/i.test(label);
  }
  function isQuoteReplyLink(link) {
    const image = link.querySelector("img");
    const label = `${image?.alt || ""} ${image?.title || ""} ${image?.getAttribute("src") || ""}`;
    return /quote\.gif|multiquote|multi-cita|responder con cita/i.test(label);
  }
  function getPostReplyActionLink(wrapper, action) {
    const links = Array.from(wrapper.querySelectorAll(".fc-premium-post-reply-actions a[href*='newreply.php?do=newreply']")).filter((link) => link instanceof HTMLAnchorElement);
    return links.find((link) => action === "quote" ? isSingleQuoteReplyLink(link) : isMultiQuoteReplyLink(link)) || null;
  }
  function isMultiQuoteReplyLink(link) {
    const image = link.querySelector("img");
    const label = `${image?.id || ""} ${image?.alt || ""} ${image?.title || ""} ${image?.getAttribute("src") || ""}`;
    return /mq_\d+|multiquote|multi-cita/i.test(label);
  }
  function isSingleQuoteReplyLink(link) {
    return isQuoteReplyLink(link) && !isMultiQuoteReplyLink(link);
  }
  function isThreadReplyWithoutQuoteLink(link) {
    const image = link.querySelector("img");
    const label = `${image?.alt || ""} ${image?.title || ""} ${image?.getAttribute("src") || ""}`;
    return link.href.includes("newreply.php") && link.href.includes("do=newreply") && link.href.includes("noquote=1") && /reply\.gif|respuesta/i.test(label);
  }
  function getThreadReplyWithoutQuoteLink() {
    return Array.from(document.querySelectorAll("a[href*='newreply.php'][href*='noquote=1']")).filter((link) => link instanceof HTMLAnchorElement).find(isThreadReplyWithoutQuoteLink) || null;
  }

  // src/app/core/threadGraphViewController.ts
  function createThreadGraphViewController(options) {
    function setActiveGraphView(type, rootPostId, relatedPostId = null, viewOptions = {}) {
      const threadGraph = options.getThreadGraph();
      if (!threadGraph.postById.has(rootPostId)) {
        return;
      }
      const activeGraphView = {
        type,
        rootPostId,
        relatedPostId
      };
      options.setActiveGraphViewState(activeGraphView);
      options.setPendingGraphView(null);
      options.setActivePageFilter(null);
      options.syncThreadStateUrl({ history: viewOptions.history || "push" });
      options.renderThreadPosts();
      options.renderThreadSummaryMenu();
      if (viewOptions.scrollToFirstPost || viewOptions.scrollToFirstReply) {
        const viewPosts = getPostsForGraphView(activeGraphView, threadGraph, options.getLoadedThreadPosts());
        const targetPost = viewOptions.scrollToFirstPost ? viewPosts[0] || null : viewPosts.find((post) => post.id !== rootPostId) || null;
        if (targetPost) {
          if (viewOptions.scrollToFirstReply && activeGraphView.type === "quoted-by") {
            options.selectPostById(targetPost.id, { scroll: false, updateUrl: true });
            options.selectPostById(rootPostId, { scroll: true, updateUrl: false });
            options.selectPostById(targetPost.id, { scroll: false, updateUrl: false });
            return;
          }
          options.selectPostById(targetPost.id, { scroll: true, updateUrl: true });
        }
      }
    }
    function activatePendingGraphView() {
      const pendingGraphView = options.getPendingGraphView();
      const threadGraph = options.getThreadGraph();
      if (!pendingGraphView || options.getActiveGraphView() || !threadGraph.postById.has(pendingGraphView.rootPostId)) {
        return;
      }
      options.setActiveGraphViewState(pendingGraphView);
      options.setPendingGraphView(null);
      options.setActivePageFilter(null);
    }
    function applyThreadUrlState(url = new URL(location.href)) {
      if (!isThreadPage() || options.getLoadedThreadPosts().length === 0) {
        return;
      }
      const queryState = readThreadQueryState(url);
      const activeGraphView = getValidGraphView(queryState.graphView, options.getThreadGraph());
      options.setActiveGraphViewState(activeGraphView);
      options.setPendingGraphView(null);
      options.setActivePageFilter(activeGraphView ? null : queryState.authorFilters.length > 0 || queryState.searchQuery ? null : queryState.pageFilter || getPageNumber(url));
      options.setActiveAuthorFilters(new Set(queryState.authorFilters));
      options.setActiveThreadSearchQuery(queryState.searchQuery);
      options.updateOriginalThreadPageMenus();
      options.renderThreadPosts();
      options.renderThreadSummaryMenu();
      const hashPostId = getLocationPostHashId(url);
      if (hashPostId) {
        options.selectPostById(hashPostId, { scroll: true, updateUrl: false });
      } else {
        window.scrollTo({ top: 0, behavior: "auto" });
      }
    }
    function installThreadHistoryNavigation() {
      window.addEventListener("popstate", () => applyThreadUrlState());
    }
    function getThreadViewPosts2(posts) {
      return getThreadViewPosts({
        posts,
        activeGraphView: options.getActiveGraphView(),
        graph: options.getThreadGraph(),
        shouldPromoteCitedPosts: !options.getActivePageFilter() && !options.hasActiveThreadPostFilters()
      });
    }
    function getReplyIndentDepth2(post, index) {
      return getReplyIndentDepth({
        post,
        index,
        activeGraphView: options.getActiveGraphView()
      });
    }
    return {
      setActiveGraphView,
      activatePendingGraphView,
      applyThreadUrlState,
      installThreadHistoryNavigation,
      getThreadViewPosts: getThreadViewPosts2,
      getReplyIndentDepth: getReplyIndentDepth2
    };
  }

  // src/app/core/threadPageHeaderController.ts
  function createThreadPageHeaderController() {
    function ensureStyle() {
      const existing = document.getElementById(STYLE_ID);
      const style = existing instanceof HTMLStyleElement ? existing : document.createElement("style");
      style.id = STYLE_ID;
      style.textContent = `#fc-premium-thread-summary {
  align-items: center;
  background: #f7faff;
  border: 1px solid #b7d1ff;
  border-radius: 4px;
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-wrap: nowrap;
  font: 11px/1.3 Verdana, Arial, sans-serif;
  gap: 8px;
  margin: 6px auto;
  max-width: 100%;
  min-height: 27px;
  overflow: hidden;
  padding: 4px 6px;
}

#fc-premium-thread-summary[hidden] {
  display: none !important;
}

#fc-premium-thread-summary.fc-premium-summary-stuck {
  box-shadow: 0 4px 12px rgba(23, 50, 77, 0.16);
}

#fc-premium-thread-summary strong {
  color: #0b57d0;
}

#fc-premium-thread-progress {
  align-items: center;
  color: #3c4043;
  display: inline-flex;
  flex: 0 0 auto;
  gap: 5px;
  white-space: nowrap;
}

.fc-premium-spinner {
  animation: fc-premium-spin 720ms linear infinite;
  border: 2px solid #c7d8ff;
  border-radius: 999px;
  border-top-color: #0b57d0;
  display: inline-block;
  height: 12px;
  width: 12px;
}

@keyframes fc-premium-spin {
  to {
    transform: rotate(360deg);
  }
}

#fc-premium-shortcut-help-container {
  box-sizing: border-box;
  display: flex;
  justify-content: flex-end;
  margin: 4px 8px 0;
  min-height: 23px;
  position: relative;
  z-index: 50;
}

#fc-premium-shortcut-help-button {
  align-items: center;
  background: rgba(247, 250, 255, 0.78);
  border: 1px solid rgba(95, 143, 199, 0.45);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(23, 50, 77, 0.12);
  box-sizing: border-box;
  color: rgba(23, 50, 77, 0.72);
  cursor: pointer;
  display: inline-flex;
  font: 700 12px/1 Verdana, Arial, sans-serif;
  height: 21px;
  justify-content: center;
  opacity: 0.45;
  padding: 0;
  width: 21px;
}

#fc-premium-shortcut-help-button:hover,
#fc-premium-shortcut-help-button:focus-visible,
#fc-premium-shortcut-help-button[aria-expanded="true"] {
  background: #f7faff;
  border-color: #5f8fc7;
  color: #17324d;
  opacity: 0.95;
  outline: none;
}

#fc-premium-shortcut-help-popover {
  background: #f7faff;
  border: 1px solid #5f8fc7;
  box-shadow: 2px 2px 0 rgba(23, 50, 77, 0.18);
  box-sizing: border-box;
  color: #17324d;
  font: 11px/1.35 Verdana, Arial, sans-serif;
  max-width: calc(100vw - 16px);
  padding: 8px;
  position: absolute;
  right: 0;
  top: 25px;
  width: 310px;
  z-index: 51;
}

#fc-premium-shortcut-help-popover[hidden] {
  display: none !important;
}

.fc-premium-shortcut-help-title {
  border-bottom: 1px solid #b7d1ff;
  font-weight: 700;
  margin-bottom: 6px;
  padding-bottom: 5px;
}

.fc-premium-shortcut-help-row {
  align-items: center;
  display: flex;
  gap: 8px;
  justify-content: space-between;
  padding: 3px 0;
}

.fc-premium-shortcut-help-keys {
  display: inline-flex;
  flex: 0 0 auto;
  gap: 3px;
}

.fc-premium-shortcut-help-key {
  background: #fff;
  border: 1px solid #9db7e5;
  border-radius: 2px;
  box-shadow: inset 0 -1px 0 #d8e4fb;
  color: #17324d;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  min-width: 16px;
  padding: 3px 5px;
  text-align: center;
  white-space: nowrap;
}

.fc-premium-shortcut-help-description {
  min-width: 0;
  text-align: right;
}

#fc-premium-thread-controls {
  display: flex;
  flex: 0 0 auto;
  gap: 5px;
  margin-left: auto;
}

#fc-premium-thread-controls button {
  background: #fff;
  border: 1px solid #b7d1ff;
  border-radius: 4px;
  color: #17324d;
  cursor: pointer;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  padding: 4px 6px;
}

#fc-premium-thread-controls button:disabled {
  color: #80868b;
  cursor: default;
  opacity: 0.72;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls {
  display: table-cell;
  margin-left: 0;
  white-space: nowrap;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls button {
  background: transparent;
  border: 0;
  border-radius: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  padding: 0 3px;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls button:hover {
  text-decoration: underline;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls button:disabled {
  cursor: default;
  opacity: 0.45;
  text-decoration: none;
}

#fc-premium-thread-controls.fc-premium-thread-toolbar-controls #fc-premium-thread-progress {
  margin-left: 8px;
  vertical-align: middle;
}

.fc-premium-thread-header-cell {
  text-align: left;
}

.fc-premium-thread-header-layout {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: space-between;
  min-height: 20px;
  width: 100%;
}

.fc-premium-thread-header-breadcrumbs {
  flex: 1;
  min-width: 0;
  overflow: hidden;
}

.fc-premium-thread-header-breadcrumbs table {
  width: auto !important;
}

.fc-premium-thread-header-breadcrumbs .navbar {
  font-size: 11px !important;
}

.fc-premium-thread-header-breadcrumbs strong {
  font-size: 11px;
  font-weight: 700;
}

.fc-premium-thread-header-search {
  align-items: center;
  display: flex;
  flex: 0 0 auto;
  gap: 6px;
  white-space: nowrap;
}

.fc-premium-thread-header-search-form {
  align-items: center;
  display: inline-flex;
  gap: 3px;
  margin: 0;
}

.fc-premium-thread-header-search-form .cfield {
  box-sizing: border-box;
  font: 11px Verdana, Arial, sans-serif;
  height: 19px;
  max-width: 24vw;
  width: 190px;
}

.fc-premium-thread-header-search-form .cbutton {
  font: 700 11px Verdana, Arial, sans-serif;
  height: 20px;
  padding: 0 6px;
}

.fc-premium-thread-header-message-search {
  flex: 1;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-panel {
  margin: 0;
  width: 100%;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-panel td {
  padding: 2px 4px;
}

.fc-premium-thread-header-message-search .fc-premium-thread-search-layout {
  gap: 3px 5px;
  grid-template-columns: minmax(120px, 1fr) minmax(100px, 0.85fr) auto auto;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-status {
  grid-column: 1 / -1;
}

.fc-premium-thread-header-message-search #fc-premium-thread-search-selected-authors {
  margin-top: 3px;
}

#fc-premium-thread-search-panel {
  margin: 0 0 8px;
  width: 100%;
}

#fc-premium-thread-search-panel .fc-premium-thread-search-cell {
  padding: 5px 6px;
}

.fc-premium-thread-search-layout {
  align-items: end;
  display: grid;
  gap: 5px 8px;
  grid-template-columns: minmax(180px, 1fr) minmax(170px, 250px) auto auto minmax(115px, auto);
}

.fc-premium-thread-search-field {
  color: #17324d;
  display: grid;
  font: 700 10px/1.25 Verdana, Arial, sans-serif;
  gap: 2px;
  min-width: 0;
}

.fc-premium-thread-search-field input {
  border: 1px solid #7f9db9;
  box-sizing: border-box;
  font: 11px Verdana, Arial, sans-serif;
  height: 20px;
  min-width: 0;
  padding: 2px 4px;
  width: 100%;
}

.fc-premium-thread-search-button {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  height: 20px;
  padding: 2px 7px 3px;
  white-space: nowrap;
}

.fc-premium-thread-search-button:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

.fc-premium-thread-search-button:disabled {
  color: #80868b;
  cursor: default;
  opacity: 0.65;
}

#fc-premium-thread-search-selected-authors {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 5px;
}

.fc-premium-thread-author-chip {
  align-items: center;
  background: #f7faff;
  border: 1px solid #9db7e5;
  border-radius: 2px;
  color: #17324d;

  display: inline-flex;
  font: 10px/1 Verdana, Arial, sans-serif;
  gap: 3px;
  padding: 2px 4px;
}

.fc-premium-thread-author-chip button {
  background: transparent;
  border: 0;
  color: #0b57d0;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 0 1px;
}

#fc-premium-thread-search-status {
  color: #3c4043;
  font: 10px/1.25 Verdana, Arial, sans-serif;
  min-width: 0;
  text-align: right;
  white-space: nowrap;
}

#fc-premium-thread-search-empty {
  background: #fff;
  border: 1px solid #b7d1ff;
  box-sizing: border-box;
  color: #3c4043;
  font: 11px/1.35 Verdana, Arial, sans-serif;
  margin: 0 0 8px;
  padding: 8px 10px;
  text-align: center;
}

#fc-premium-thread-search-empty[hidden] {
  display: none !important;
}

#fc-premium-top-tags {
  align-items: center;
  background: #f7faff;
  border: 1px solid #b7d1ff;
  border-radius: 6px;
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-wrap: wrap;
  font: 12px/1.4 Verdana, Arial, sans-serif;
  gap: 6px;
  margin: 10px auto;
  padding: 8px 10px;
}

#fc-premium-top-tags > span:first-child {
  font-weight: 700;
}

#fc-premium-top-tags .fc-premium-tag-chip[aria-pressed="true"] {
  box-shadow: 0 0 0 2px #0b57d0;
}

#fc-premium-top-tags button {
  background: #fff;
  border: 1px solid #9db7e5;
  border-radius: 3px;
  color: #17324d;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 4px 6px;
}

#fc-premium-top-tags button[aria-current="page"] {
  background: #5f8fc7;
  border-color: #3f70a8;
  color: #fff;
  cursor: default;
}

#fc-premium-forum-controls-row {
  margin-bottom: 3px !important;
}

#fc-premium-forum-controls-row td {
  vertical-align: middle;
}

#fc-premium-forum-controls-row .fc-premium-forum-sidebar-toggle-cell,
#fc-premium-forum-controls-row .fc-premium-forum-new-thread-cell {
  padding-right: 6px;
  white-space: nowrap;
  width: 1%;
}

#fc-premium-forum-controls-row .fc-premium-forum-search-cell {
  text-align: left;
  white-space: nowrap;
  width: 100%;
}

#fc-premium-forum-controls-row .fc-premium-forum-pager-cell {
  text-align: right;
  white-space: nowrap;
  width: 1%;
}

#fc-premium-forum-controls-row .fc-premium-thread-header-search-form .cfield {
  max-width: 24vw;
  width: 190px;
}

#fc-premium-forum-loading-status {
  align-items: center;
  color: #3c4043;
  display: inline-flex;
  font: 11px/1.2 Verdana, Arial, sans-serif;
  gap: 5px;
  margin-left: 8px;
  min-width: 138px;
  vertical-align: middle;
  white-space: nowrap;
}

#fc-premium-forum-loading-status[data-fc-premium-loading="false"] {
  visibility: hidden;
}

#fc-premium-forum-sidebar-toggle-bar {
  align-items: center;
  background: #f7faff;
  border: 1px solid #b7d1ff;
  border-radius: 6px;
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-wrap: wrap;
  font: 12px/1.4 Verdana, Arial, sans-serif;
  gap: 8px;
  margin: 0 0 10px;
  padding: 8px 10px;
}

#fc-premium-forum-sidebar-toggle {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 3px 7px 4px;
}

#fc-premium-forum-sidebar-toggle:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

html.fc-premium-modal-open,
body.fc-premium-modal-open {
  overflow: hidden !important;
  overscroll-behavior: none;
}

#fc-premium-hidden-threads-modal {
  align-items: center;
  background: rgba(0, 0, 0, 0.48);
  box-sizing: border-box;
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 20px;
  position: fixed;
  z-index: 2147483645;
}

#fc-premium-hidden-threads-modal[hidden] {
  display: none !important;
}

.fc-premium-hidden-threads-dialog {
  background: #f7faff;
  border: 1px solid #555576;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.28);
  box-sizing: border-box;
  color: #17324d;
  display: flex;
  flex-direction: column;
  font: 11px/1.35 Verdana, Arial, sans-serif;
  height: calc(100vh - 40px);
  max-width: calc(100vw - 40px);
  overflow: hidden;
  width: 860px;
}

.fc-premium-hidden-threads-header {
  align-items: center;
  background: #555576;
  color: #fff;
  display: flex;
  font-weight: 700;
  justify-content: space-between;
  padding: 6px 8px;
}

.fc-premium-hidden-threads-header button,
.fc-premium-hidden-thread-restore {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 11px/1 Verdana, Arial, sans-serif;
  padding: 3px 7px 4px;
}

.fc-premium-hidden-threads-header button:hover,
.fc-premium-hidden-thread-restore:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

#fc-premium-hidden-threads-modal-body {
  flex: 1 1 auto;
  min-height: 0;
  overscroll-behavior: contain;
  overflow: auto;
  padding: 8px;
}

.fc-premium-hidden-threads-table {
  background: #555576;
  border-collapse: separate;
  border-spacing: 1px;
  width: 100%;
}

.fc-premium-hidden-threads-table th {
  background: #d5e6ee;
  color: #17324d;
  font-weight: 700;
  padding: 5px;
  text-align: left;
  white-space: nowrap;
}

.fc-premium-hidden-threads-table td {
  background: #f1f1f1;
  padding: 5px;
  vertical-align: top;
}

.fc-premium-hidden-threads-table tr:nth-child(even) td {
  background: #fff;
}

.fc-premium-hidden-thread-title {
  font-weight: 700;
}

.fc-premium-hidden-thread-meta {
  color: #3c4043;
  font-size: 10px;
  margin-top: 3px;
}

.fc-premium-hidden-threads-empty {
  background: #fff;
  border: 1px solid #b7d1ff;
  padding: 14px;
  text-align: center;
}

.fc-premium-quote-actions {
  align-items: center;
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 3px;
  margin-left: 5px;
  vertical-align: middle;
  white-space: nowrap;
}

.fc-premium-quote-actions button {
  background: #e6e9ed;
  border: 1px solid #7f8c99;
  border-left-color: #f8f8f8;
  border-radius: 2px;
  border-top-color: #f8f8f8;
  box-shadow: inset -1px -1px 0 #bcc3ca;
  color: #1f3550;
  cursor: pointer;
  font: 700 9px/1.1 Verdana, Arial, sans-serif;
  padding: 1px 5px 2px;
}

.fc-premium-quote-actions button:hover {
  background: #f2f5f8;
  color: #0b57d0;
}

tr[data-fc-premium-tag-hidden] {
  display: none !important;
}

[data-fc-premium-layout-hidden] {
  display: none !important;
}

.fc-premium-post-wrapper[data-fc-premium-filter-hidden] {
  display: none !important;
}

.fc-premium-post-wrapper[data-fc-premium-page-hidden] {
  display: none !important;
}

.fc-premium-post-wrapper {
  border-radius: 6px;
  margin: 0 0 12px;
  position: relative;
  transition: transform 160ms ease;
}

.fc-premium-post-footer-row {
  display: none !important;
}

.fc-premium-post-reply-actions {
  align-items: center;
  bottom: 7px;
  display: flex;
  flex-wrap: nowrap;
  gap: 3px;
  line-height: 1;
  position: absolute;

  right: 9px;
  white-space: nowrap;
  z-index: 5;
}

.fc-premium-post-reply-actions a {
  display: inline-flex;
  margin: 0 !important;
}

.fc-premium-post-reply-actions img {
  display: block;
}

.fc-premium-post-wrapper[data-fc-premium-reply-indent] {
  margin-left: 28px;
}

.fc-premium-reply-badge {
  border-right: 1px solid #9aa0a6;
  color: #3c4043;
  display: inline-block;
  float: right;
  font: 700 9px/1 Verdana, Arial, sans-serif;
  margin-left: 8px;
  margin-right: 7px;
  padding-right: 7px;
  vertical-align: baseline;
  white-space: nowrap;
}

.fc-premium-author-filter-button {
  cursor: pointer;
}

.fc-premium-author-filter-button {
  background: #fff;
  border: 1px solid #b7d1ff;
  border-radius: 999px;
  color: #0b57d0;
  display: inline-block;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  margin-left: 5px;
  padding: 3px 6px;
  vertical-align: 1px;
}

.fc-premium-author-filter-button:hover {
  border-color: #0b57d0;
}

.fc-premium-reply-badge a {
  color: inherit;
  font-weight: 700;
  margin-left: 3px;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fc-premium-original-position {
  color: #17324d;
  font-weight: 400;
  margin-left: 6px;
  opacity: 0.8;
}

.fc-premium-reply-badge .fc-premium-original-position {
  color: inherit;
  margin-left: 4px;
  opacity: 0.72;
}

.fc-premium-reply-badge button {
  background: transparent;
  border: 0;
  border-radius: 0;
  color: inherit;
  cursor: pointer;
  font: 700 9px/1 Verdana, Arial, sans-serif;
  margin-left: 4px;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fc-premium-reply-badge button:hover {
  color: #0b57d0;
}

.fc-premium-post-date-cell {
  white-space: nowrap;
  width: 100%;
}

.fc-premium-post-number-cell {
  text-align: right !important;
  width: 1%;
  white-space: nowrap;
}

.fc-premium-header-author {
  color: #3c4043;
  display: none;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  margin-left: 6px;
  position: relative;
  white-space: nowrap;
}

.fc-premium-header-author::after {
  content: "";
  display: none;
  height: 9px;
  left: -8px;
  position: absolute;
  right: -8px;
  top: 100%;
}

.fc-premium-header-author:hover::after,
.fc-premium-header-author:focus-within::after {
  display: block;
}

.fc-premium-header-author > a {
  color: #0b57d0;
  text-decoration: none;
}

.fc-premium-header-author > a:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.fc-premium-author-hover-card {
  background: #f0f0f0;
  border: 1px solid #8d98a3;
  border-left-color: #ffffff;
  border-radius: 2px;
  border-top-color: #ffffff;
  box-shadow: 2px 2px 0 rgba(0, 0, 0, 0.22);
  color: #202124;
  display: none;
  font: 11px/1.4 Verdana, Arial, sans-serif;
  left: 0;
  max-width: min(320px, 80vw);
  min-width: 210px;
  padding: 8px 10px;
  position: absolute;
  text-align: left;
  top: calc(100% + 1px);
  white-space: normal;
  z-index: 9999;
}

.fc-premium-header-author:hover .fc-premium-author-hover-card,
.fc-premium-header-author:focus-within .fc-premium-author-hover-card {
  display: block;
}

.fc-premium-author-hover-card strong {
  color: #17324d;
  display: block;
  font-size: 12px;
  margin-bottom: 4px;
}

.fc-premium-author-hover-card .fc-premium-author-avatar {
  border: 1px solid #9aa3ad;
  border-radius: 2px;
  display: block;
  height: 58px;
  margin: 0 0 7px;
  max-width: 92px;
  object-fit: cover;
  width: auto;
}

.fc-premium-author-hover-card span {
  display: block;
  margin-top: 2px;
}

.fc-premium-author-card-actions {
  border-top: 1px solid #c0c6cc;
  display: flex !important;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 7px !important;
  padding-top: 6px;
}

.fc-premium-author-status,
.fc-premium-author-report-link {
  align-items: center;
  color: #202124;
  display: inline-flex !important;
  gap: 4px;
  margin: 0 !important;
  text-decoration: none;
}

.fc-premium-author-report-link:hover {
  color: #0b57d0;
  text-decoration: underline;
}

.fc-premium-author-status span,
.fc-premium-author-report-link span {
  display: inline;
  margin: 0;
}

a[data-fc-premium-quote-target] {
  outline-offset: 2px;
}

a[data-fc-premium-quote-target]:focus-visible {
  outline: 2px solid #1a73e8;
}

[data-fc-premium-quote-block] {
  transition: opacity 140ms ease;
}

.fc-premium-tag-chip {
  background: var(--fc-premium-tag-bg);
  border: 1px solid var(--fc-premium-tag-border);
  border-radius: 999px;
  color: var(--fc-premium-tag-color);
  display: inline-block;
  font: 700 10px/1 Verdana, Arial, sans-serif;
  margin: 0 2px;
  padding: 3px 6px;
  text-transform: uppercase;
  vertical-align: 1px;
  white-space: nowrap;
}

.fc-premium-tag-chip[role="button"] {
  cursor: pointer;
}

[data-fc-premium-selected] {
  border-radius: 6px;
  outline: 2px solid #1a73e8 !important;
  outline-offset: 3px;
  transition: outline-offset 160ms ease;
}

tr[data-fc-premium-selected] > td {
  background: #eef5ff !important;
}

body #posts table[id^="post"] {
  table-layout: auto !important;
}

body #posts .fc-premium-author-cell {
  display: none !important;
}

body #posts table[id^="post"] td {
  padding-bottom: 4px !important;
  padding-top: 4px !important;
}

body .fc-premium-post-wrapper div[id^="edit"] > br,
body .fc-premium-post-wrapper div[id^="edit"] > table.cajasprin {
  display: none !important;
}

body .fc-premium-post-wrapper {
  margin-bottom: 6px;
}

body .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
  margin-left: 18px;
}

#\${POSTS_SELECTOR.slice(1)}[data-fc-premium-graph-view="quoted-by"] .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
  margin-left: 34px;
}

@media (max-width: 700px) {
  .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
    margin-left: 14px;
  }

  #\${POSTS_SELECTOR.slice(1)}[data-fc-premium-graph-view="quoted-by"] .fc-premium-post-wrapper[data-fc-premium-reply-indent] {
    margin-left: 24px;
  }
}

body #fc-premium-thread-summary {
  font-size: 11px;
  margin: 6px auto;
  padding: 6px 8px;
}

body #fc-premium-top-tags,
body #fc-premium-forum-sidebar-toggle-bar {
  margin-bottom: 5px;
  margin-top: 5px;
  padding: 5px 8px;
}

body #fc-premium-thread-controls,
body #fc-premium-thread-progress {
  gap: 4px;
}

body #posts table[id^="post"] {
  font-size: 12px !important;
}

body #posts table[id^="post"] .alt1,
body #posts table[id^="post"] .alt2 {
  padding-left: 6px !important;
  padding-right: 6px !important;
}

body #threadslist td {
  padding-bottom: 2px !important;
  padding-top: 2px !important;
}

body #threadslist,
body #threadslist .mfont,
body #threadslist .smallfont {
  font-size: 12px !important;
}

body .fc-premium-header-author {
  display: inline-block;
}

body table.tborder:has(td.navbar),
body table.tborder:has(.navbar) {
  display: none !important;
}
`;
      if (!existing) {
        document.head.appendChild(style);
      }
    }
    function enhanceThreadHeader() {
      const titleTable = getThreadTitleTable();
      if (!(titleTable instanceof HTMLTableElement) || titleTable.dataset.fcPremiumThreadHeaderEnhanced === "true") {
        return;
      }
      const breadcrumbs = getThreadBreadcrumbContentTable();
      const searchLink = getNavbarSearchLink();
      if (!breadcrumbs && !searchLink) {
        return;
      }
      titleTable.dataset.fcPremiumThreadHeaderEnhanced = "true";
      const searchParentCell = searchLink?.closest("td.vbmenu_control");
      const breadcrumbOuterTable = getThreadBreadcrumbOuterTable();
      const body = titleTable.tBodies[0] || titleTable.createTBody();
      body.textContent = "";
      const row = body.insertRow();
      const cell = row.insertCell();
      cell.className = "thead fc-premium-thread-header-cell";
      cell.colSpan = 3;
      const layout = document.createElement("div");
      layout.className = "fc-premium-thread-header-layout";
      const breadcrumbSlot = document.createElement("div");
      breadcrumbSlot.className = "fc-premium-thread-header-breadcrumbs";
      if (breadcrumbs) {
        breadcrumbSlot.append(breadcrumbs);
      }
      layout.append(breadcrumbSlot);
      const searchSlot = document.createElement("div");
      searchSlot.id = THREAD_SEARCH_HEADER_SLOT_ID;
      searchSlot.className = "fc-premium-thread-header-message-search";
      layout.append(searchSlot);
      cell.append(layout);
      hideForumHeaderSearchForm();
      if (searchParentCell instanceof HTMLElement) {
        searchParentCell.setAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE, "true");
      }
      if (breadcrumbOuterTable instanceof HTMLElement) {
        breadcrumbOuterTable.setAttribute(FORUM_LAYOUT_HIDDEN_ATTRIBUTE, "true");
      }
    }
    function prepareThreadPage() {
      ensureStyle();
      hideTopShortcutBarsBefore(getThreadTitleTable());
      hideNativeThreadSearchMenu();
      enhanceThreadHeader();
      hideNativeThreadSearchMenu();
    }
    function renderShortcutHelpButton2() {
      renderShortcutHelpButton({
        items: getShortcutHelpItems(),
        formatKey: formatShortcutHelpKey
      });
    }
    return {
      prepareThreadPage,
      renderShortcutHelpButton: renderShortcutHelpButton2
    };
  }

  // src/app/core/threadPageLoader.ts
  function getUniqueThreadPages(pages) {
    const seenPageNumbers = new Set;
    const uniquePages = [];
    for (const page of pages) {
      if (seenPageNumbers.has(page.pageNumber)) {
        continue;
      }
      seenPageNumbers.add(page.pageNumber);
      uniquePages.push(page);
    }
    return uniquePages;
  }
  function getThreadPagesToRefresh(options) {
    const firstPage = options.allPages.find((page) => page.pageNumber === 1);
    if (!options.cachedThread || !isCompleteThreadCache(options.cachedThread)) {
      return options.allPages;
    }
    const currentLastPageNumber = options.allPages.length;
    const previousLastPageNumber = Math.min(Math.max(options.cachedThread.lastSeenPageNumber, 1), currentLastPageNumber);
    const tailStartPageNumber = currentLastPageNumber > previousLastPageNumber ? previousLastPageNumber : currentLastPageNumber;
    const tailPages = options.allPages.filter((page) => page.pageNumber >= tailStartPageNumber);
    return getUniqueThreadPages([
      ...firstPage ? [firstPage] : [],
      ...tailPages
    ]);
  }
  function getPageOffset(posts, pageNumber) {
    return posts.filter((post) => post.pageNumber < pageNumber).length;
  }
  function replaceThreadPagePosts(posts, pageNumber, pagePosts) {
    return posts.filter((post) => post.pageNumber !== pageNumber).concat(pagePosts).sort((left, right) => {
      if (left.originalIndex !== right.originalIndex) {
        return left.originalIndex - right.originalIndex;
      }
      if (left.pageNumber !== right.pageNumber) {
        return left.pageNumber - right.pageNumber;
      }
      return left.pageIndex - right.pageIndex;
    });
  }
  async function enhanceThreadPage(options) {
    options.prepareThreadPage();
    const summary = options.ensureThreadSummary();
    const queryState = readThreadQueryState();
    const allPages = options.getThreadPages();
    const currentPageNumber = getPageNumber(new URL(location.href));
    const cachedThread = await readCurrentThreadCache();
    const pages = getThreadPagesToRefresh({ allPages, cachedThread });
    const allPosts = [];
    let loadedPosts = cachedThread && isCompleteThreadCache(cachedThread) ? cachedThread.posts.filter((post) => post.pageNumber <= allPages.length) : [];
    options.setThreadPages(allPages);
    options.setLoadedThreadPosts([]);
    options.setLoadedThreadPageNumbers(new Set);
    options.setThreadGraphEmpty();
    options.setActiveGraphView(null);
    options.setPendingGraphView(queryState.graphView);
    options.setActivePageFilter(queryState.graphView ? null : queryState.authorFilters.length > 0 || queryState.searchQuery ? null : queryState.pageFilter || currentPageNumber);
    options.setActiveAuthorFilters(new Set(queryState.authorFilters));
    options.setActiveThreadSearchQuery(queryState.searchQuery);
    const activePageFilter = queryState.graphView ? null : queryState.authorFilters.length > 0 || queryState.searchQuery ? null : queryState.pageFilter || currentPageNumber;
    if (activePageFilter) {
      options.updateThreadPageUrl(activePageFilter, {
        preserveHash: Boolean(options.getPendingInitialHashPostId())
      });
    } else {
      options.syncThreadStateUrl();
    }
    if (!options.getPendingInitialHashPostId()) {
      window.scrollTo({ top: 0, behavior: "auto" });
    }
    options.setThreadLoadState({
      loadedPages: 0,
      targetPages: pages.length,
      totalPages: allPages.length,
      loadedPosts: 0,
      isLoading: pages.length > 0
    });
    if (summary) {
      summary.textContent = "";
    }
    options.renderThreadSummaryMenu(summary);
    options.renderThreadSearchPanel();
    const currentPageDocument = parseHtml(document.documentElement.outerHTML);
    const loadedPageNumbers = new Set(loadedPosts.map((post) => post.pageNumber));
    for (const page of pages) {
      const doc = page.pageNumber === currentPageNumber ? currentPageDocument : await fetchThreadDocument(page.url);
      const pagePosts = collectPosts(doc, page.pageNumber, getPageOffset(loadedPosts, page.pageNumber));
      loadedPosts = replaceThreadPagePosts(loadedPosts, page.pageNumber, pagePosts);
      allPosts.length = 0;
      allPosts.push(...loadedPosts);
      loadedPageNumbers.add(page.pageNumber);
      options.setLoadedThreadPageNumbers(new Set(loadedPageNumbers));
      options.hydrateThreadPosts(allPosts);
      options.setThreadLoadState({
        ...options.getThreadLoadState(),
        loadedPages: loadedPageNumbers.size,
        loadedPosts: allPosts.length,
        isLoading: true
      });
      options.renderThreadPosts();
      options.renderThreadSummaryMenu(summary);
      const lastPage = pages[pages.length - 1];
      if (lastPage && page.pageNumber !== lastPage.pageNumber) {
        await sleep(PAGE_LOAD_DELAY_MS);
      }
    }
    options.setThreadLoadState({
      ...options.getThreadLoadState(),
      loadedPages: loadedPageNumbers.size,
      loadedPosts: allPosts.length,
      isLoading: false
    });
    options.renderThreadPosts();
    options.renderThreadSummaryMenu(summary);
    if (allPages.every((page) => loadedPageNumbers.has(page.pageNumber))) {
      await writeCurrentThreadCache(allPosts, allPages.length, loadedPageNumbers, allPages.length);
    }
  }

  // src/app/core/threadPageKeyboardController.ts
  function createThreadPageKeyboardController(handlers) {
    function hasActiveGraphView() {
      return Boolean(handlers.getActiveGraphView());
    }
    function hasActiveFiltersOrView() {
      return handlers.hasActiveThreadPostFilters() || hasActiveGraphView();
    }
    function handleSelectedPostActionShortcut(event) {
      if (!handlers.getSelectedPostWrapper) {
        return false;
      }
      if (hasKeyboardModifier(event)) {
        return false;
      }
      if (event.key === KEY_NEW_THREAD_REPLY) {
        event.preventDefault();
        return handlers.openThreadReplyWithoutQuote();
      }
      const selected = handlers.getSelectedPostWrapper();
      if (!selected) {
        return false;
      }
      if (event.key === KEY_QUOTE_SELECTED_POST) {
        event.preventDefault();
        return handlers.quoteSelectedPost(selected);
      }
      if (event.key === KEY_MULTIQUOTE_SELECTED_POST) {
        event.preventDefault();
        return handlers.toggleSelectedPostMultiquote(selected);
      }
      return false;
    }
    function handleNavigationKeyDown(event) {
      if (isEditableTarget(event.target)) {
        return false;
      }
      if ((event.key === KEY_NAV_NEXT_POST || event.key === KEY_NAV_PREVIOUS_POST || event.key === KEY_NAV_NEXT_PAGE || event.key === KEY_NAV_PREVIOUS_PAGE) && hasKeyboardModifier(event)) {
        return false;
      }
      if (event.key === KEY_OPEN_SHORTCUT_HELP) {
        event.preventDefault();
        setShortcutHelpPopoverOpen(true);
        return true;
      }
      if (event.key === KEY_CLEAR_ACTIVE_VIEW && isShortcutHelpPopoverOpen()) {
        event.preventDefault();
        closeShortcutHelpPopover();
        return true;
      }
      if (event.key === KEY_NAV_NEXT_POST) {
        event.preventDefault();
        handlers.moveNavigation(1);
        return true;
      }
      if (event.key === KEY_NAV_PREVIOUS_POST) {
        event.preventDefault();
        handlers.moveNavigation(-1);
        return true;
      }
      if (event.key === KEY_NAV_PREVIOUS_PAGE) {
        event.preventDefault();
        return handlers.navigatePage(-1);
      }
      if (event.key === KEY_NAV_NEXT_PAGE) {
        event.preventDefault();
        return handlers.navigatePage(1);
      }
      if (!hasKeyboardModifier(event) && keyboardShortcutMatches(event, KEY_RETURN_TO_THREAD_LIST)) {
        event.preventDefault();
        return handlers.returnToThreadList();
      }
      if (handleSelectedPostActionShortcut(event)) {
        return true;
      }
      if (event.key === KEY_CLEAR_ACTIVE_VIEW && hasActiveFiltersOrView()) {
        event.preventDefault();
        handlers.clearThreadFilters();
        return true;
      }
      if (event.key === KEY_QUOTE_SELECTED_POST && !hasKeyboardModifier(event)) {
        event.preventDefault();
        const selected = handlers.getSelectedPostWrapper();
        if (!selected) {
          return false;
        }
        return handlers.quoteSelectedPost(selected);
      }
      return false;
    }
    return { handleNavigationKeyDown };
  }

  // src/ui/threadPostFiltersDom.ts
  function applyPageFilterToRenderedPosts(posts, activePageFilter) {
    let total = 0;
    let visible = 0;
    if (!posts) {
      return { total, visible };
    }
    for (const wrapper of getRenderedPostWrappers(posts)) {
      const pageNumber = Number(wrapper.dataset.fcPremiumOriginalPage || "0");
      const matches = !activePageFilter || pageNumber === activePageFilter;
      total += 1;
      if (matches) {
        visible += 1;
        wrapper.removeAttribute(HIDDEN_POST_PAGE_ATTRIBUTE);
      } else {
        wrapper.setAttribute(HIDDEN_POST_PAGE_ATTRIBUTE, "true");
      }
    }
    return { total, visible };
  }
  function applyThreadPostFiltersToRenderedPosts(options) {
    let total = 0;
    let visible = 0;
    if (!options.posts) {
      return { total, visible };
    }
    for (const wrapper of getRenderedPostWrappers(options.posts)) {
      const authorKey = wrapper.dataset.fcPremiumAuthor || "";
      const postId = getPostIdFromWrapper(wrapper);
      const post = postId ? options.postById.get(postId) : null;
      const matchesAuthor = options.activeAuthorFilters.size === 0 || options.activeAuthorFilters.has(authorKey);
      const matchesText = !options.query || (post ? options.getPostSearchText(post).includes(options.query) : false);
      const matches = matchesAuthor && matchesText;
      total += 1;
      if (matches) {
        visible += 1;
        wrapper.removeAttribute(HIDDEN_POST_FILTER_ATTRIBUTE);
      } else {
        wrapper.setAttribute(HIDDEN_POST_FILTER_ATTRIBUTE, "true");
      }
    }
    return { total, visible };
  }
  function enhanceAuthorFilterButton(wrapper, author, onToggleAuthor) {
    const authorKey = normalizeAuthorName(author);
    if (!authorKey) {
      return;
    }
    wrapper.dataset.fcPremiumAuthor = authorKey;
    const username = wrapper.querySelector(".bigusername");
    if (!(username instanceof HTMLElement)) {
      return;
    }
    const existingButton = username.parentElement?.querySelector(".fc-premium-author-filter-button");
    if (existingButton) {
      return;
    }
    const button = document.createElement("button");
    button.type = "button";
    button.className = "fc-premium-author-filter-button";
    button.textContent = "filtrar";
    button.title = `Filtrar mensajes de ${author}`;
    button.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      onToggleAuthor(author);
    });
    username.after(button);
  }
  function getRenderedPostWrappers(posts) {
    return Array.from(posts.querySelectorAll(".fc-premium-post-wrapper")).filter((wrapper) => wrapper instanceof HTMLElement);
  }
  function getPostIdFromWrapper(wrapper) {
    const postTable = wrapper.querySelector(POST_TABLE_SELECTOR);
    const postId = postTable?.id.match(/^post(\d+)$/)?.[1];
    return postId || null;
  }

  // src/adapters/forocoches/threadPageNavigation.ts
  function updateOriginalThreadPageMenus(options) {
    for (const table of getOriginalThreadPageNavTables()) {
      const body = table.tBodies[0] || table.createTBody();
      const row = document.createElement("tr");
      const statusCell = document.createElement("td");
      statusCell.className = "vbmenu_control";
      statusCell.style.fontWeight = "normal";
      statusCell.textContent = `Pág ${options.currentPage} de ${options.totalPages}`;
      row.append(statusCell);
      for (const pageNumber of options.visiblePages) {
        row.append(createOriginalThreadPageCell(pageNumber, options.currentPage, options.hrefForPage));
      }
      if (options.currentPage < options.totalPages) {
        row.append(createOriginalThreadPageActionCell(">", options.currentPage + 1, options.hrefForPage));
      }
      if (options.currentPage !== options.totalPages) {
        row.append(createOriginalThreadPageActionCell("Último »", options.totalPages, options.hrefForPage));
      }
      body.textContent = "";
      body.append(row);
    }
  }
  function getOriginalThreadPageLinkNumber(link, currentThreadId) {
    const table = link.closest("table.tborder");
    if (!(table instanceof HTMLTableElement)) {
      return null;
    }
    const status = normalizeText(table.querySelector("td.vbmenu_control")?.textContent);
    if (!/^Pág \d+ de \d+$/.test(status)) {
      return null;
    }
    const url = toUrl(link.getAttribute("href") || link.href);
    if (!url || getThreadId(url) !== currentThreadId) {
      return null;
    }
    return getPageNumber(url);
  }
  function getOriginalThreadPageNavTables() {
    return Array.from(document.querySelectorAll("table.tborder")).filter((table) => {
      if (!(table instanceof HTMLTableElement)) {
        return false;
      }
      const status = normalizeText(table.querySelector("td.vbmenu_control")?.textContent);
      return /^Pág \d+ de \d+$/.test(status);
    });
  }
  function createOriginalThreadPageCell(pageNumber, currentPage, hrefForPage) {
    const cell = document.createElement("td");
    const isCurrent = pageNumber === currentPage;
    cell.className = isCurrent ? "alt2" : "alt1";
    if (isCurrent) {
      const span = document.createElement("span");
      span.className = "mfont";
      span.title = `Pagina ${pageNumber}`;
      const strong = document.createElement("strong");
      strong.textContent = String(pageNumber);
      span.append(strong);
      cell.append(span);
      return cell;
    }
    const link = document.createElement("a");
    link.className = "mfont";
    link.href = hrefForPage(pageNumber);
    link.title = `Mostrar pagina ${pageNumber}`;
    link.textContent = String(pageNumber);
    cell.append(link);
    return cell;
  }
  function createOriginalThreadPageActionCell(text, pageNumber, hrefForPage) {
    const cell = document.createElement("td");
    cell.className = "alt1";
    const link = document.createElement("a");
    link.className = "smallfont";
    link.href = hrefForPage(pageNumber);
    link.textContent = text;
    cell.append(link);
    return cell;
  }

  // src/app/core/threadPagePaginationController.ts
  function createThreadPagePaginationController(options) {
    function setPageFilter(pageNumber) {
      if (!isThreadPage()) {
        return;
      }
      options.setActivePageFilter(pageNumber);
      options.clearGraphView();
      options.updateThreadPageUrl(pageNumber, { history: "push" });
      updateOriginalThreadPageMenus2();
      options.renderThreadPosts();
      options.renderThreadSummaryMenu();
      window.scrollTo({ top: 0, behavior: "auto" });
    }
    function navigatePage(direction) {
      if (!isThreadPage() || options.getActiveGraphView()) {
        return false;
      }
      const totalPages = options.getThreadPages().length;
      if (totalPages <= 1) {
        return false;
      }
      const currentPage = options.getActivePageFilter() || getPageNumber(new URL(location.href));
      const nextPage = Math.min(Math.max(currentPage + direction, 1), totalPages);
      if (nextPage === currentPage) {
        return false;
      }
      setPageFilter(nextPage);
      return true;
    }
    function applyPageFilter() {
      return applyPageFilterToRenderedPosts(options.getPostsElement(), options.getActivePageFilter());
    }
    function updateOriginalThreadPageMenus2() {
      const threadPages = options.getThreadPages();
      if (!isThreadPage() || threadPages.length <= 1 || options.getActiveGraphView()) {
        return;
      }
      const totalPages = threadPages.length;
      const currentPage = options.getActivePageFilter() || getPageNumber(new URL(location.href));
      updateOriginalThreadPageMenus({
        totalPages,
        currentPage,
        visiblePages: getVisiblePageNumbers(totalPages, currentPage),
        hrefForPage: (pageNumber) => options.getThreadPageUrl(pageNumber).href
      });
    }
    function handleThreadPageNavigationClick(event) {
      const link = event.target instanceof Element ? event.target.closest("a[href*='showthread.php']") : null;
      if (!(link instanceof HTMLAnchorElement)) {
        return;
      }
      const pageNumber = getOriginalThreadPageLinkNumber(link, getThreadId(new URL(location.href)));
      if (!pageNumber) {
        return;
      }
      event.preventDefault();
      setPageFilter(pageNumber);
    }
    function installThreadPageNavigation() {
      document.addEventListener("click", handleThreadPageNavigationClick, true);
    }
    return {
      setPageFilter,
      navigatePage,
      applyPageFilter,
      updateOriginalThreadPageMenus: updateOriginalThreadPageMenus2,
      installThreadPageNavigation
    };
  }

  // src/ui/components/ThreadSearchPanel.tsx
  function ThreadSearchPanel(props) {
    return renderElement(/* @__PURE__ */ u2("table", {
      id: THREAD_SEARCH_PANEL_ID,
      className: "tborder",
      cellPadding: "4",
      cellSpacing: "1",
      ...{ border: "0" },
      children: /* @__PURE__ */ u2("tbody", {
        children: [
          /* @__PURE__ */ u2("tr", {
            children: /* @__PURE__ */ u2("td", {
              className: "thead",
              children: "Buscar mensajes"
            }, undefined, false, undefined, this)
          }, undefined, false, undefined, this),
          /* @__PURE__ */ u2("tr", {
            children: /* @__PURE__ */ u2("td", {
              className: "alt1 fc-premium-thread-search-cell",
              children: [
                /* @__PURE__ */ u2("div", {
                  className: "fc-premium-thread-search-layout",
                  children: [
                    /* @__PURE__ */ u2("label", {
                      className: "fc-premium-thread-search-field",
                      children: [
                        "Texto",
                        /* @__PURE__ */ u2("input", {
                          id: THREAD_SEARCH_TEXT_INPUT_ID,
                          type: "search",
                          className: "bginput",
                          placeholder: "Buscar en mensajes",
                          value: props.searchQuery,
                          onInput: (event) => {
                            const input = event.currentTarget;
                            if (input instanceof HTMLInputElement) {
                              props.onSearchInput(input.value);
                            }
                          }
                        }, undefined, false, undefined, this)
                      ]
                    }, undefined, true, undefined, this),
                    /* @__PURE__ */ u2("label", {
                      className: "fc-premium-thread-search-field",
                      children: [
                        "Usuario",
                        /* @__PURE__ */ u2("input", {
                          id: THREAD_SEARCH_AUTHOR_INPUT_ID,
                          type: "text",
                          className: "bginput",
                          placeholder: "Escribe un usuario",
                          list: THREAD_SEARCH_AUTHOR_DATALIST_ID,
                          autoComplete: "off",
                          onKeyDown: (event) => {
                            if (event.key !== "Enter") {
                              return;
                            }
                            event.preventDefault();
                            props.onAddAuthor();
                          }
                        }, undefined, false, undefined, this)
                      ]
                    }, undefined, true, undefined, this),
                    /* @__PURE__ */ u2("button", {
                      type: "button",
                      className: "fc-premium-thread-search-button",
                      onClick: props.onAddAuthor,
                      children: "Añadir"
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ u2("button", {
                      type: "button",
                      className: "fc-premium-thread-search-button",
                      onClick: props.onClearFilters,
                      children: "Limpiar"
                    }, undefined, false, undefined, this),
                    /* @__PURE__ */ u2("span", {
                      id: THREAD_SEARCH_STATUS_ID
                    }, undefined, false, undefined, this)
                  ]
                }, undefined, true, undefined, this),
                /* @__PURE__ */ u2("datalist", {
                  id: THREAD_SEARCH_AUTHOR_DATALIST_ID
                }, undefined, false, undefined, this),
                /* @__PURE__ */ u2("div", {
                  id: THREAD_SEARCH_SELECTED_AUTHORS_ID
                }, undefined, false, undefined, this),
                /* @__PURE__ */ u2("div", {
                  id: THREAD_SEARCH_EMPTY_ID
                }, undefined, false, undefined, this)
              ]
            }, undefined, true, undefined, this)
          }, undefined, false, undefined, this)
        ]
      }, undefined, true, undefined, this)
    }, undefined, false, undefined, this));
  }

  // src/domain/threadAuthors.ts
  function getThreadOriginalPosterName(posts) {
    return sortPostsChronologically(posts)[0]?.author || "";
  }
  function getThreadAuthorOptions(posts, currentUsername) {
    const optionsByKey = new Map;
    const originalPosterKey = normalizeAuthorName(getThreadOriginalPosterName(posts));
    const currentUserKey = normalizeAuthorName(currentUsername);
    for (const post of posts) {
      const key = normalizeAuthorName(post.author);
      if (!key) {
        continue;
      }
      const option = optionsByKey.get(key) || {
        key,
        name: post.author,
        count: 0,
        isOriginalPoster: key === originalPosterKey,
        isCurrentUser: key === currentUserKey
      };
      option.count += 1;
      option.isOriginalPoster = option.isOriginalPoster || key === originalPosterKey;
      option.isCurrentUser = option.isCurrentUser || key === currentUserKey;
      optionsByKey.set(key, option);
    }
    if (currentUserKey && !optionsByKey.has(currentUserKey)) {
      optionsByKey.set(currentUserKey, {
        key: currentUserKey,
        name: currentUsername,
        count: 0,
        isOriginalPoster: currentUserKey === originalPosterKey,
        isCurrentUser: true
      });
    }
    return Array.from(optionsByKey.values()).sort((left, right) => {
      if (left.isOriginalPoster !== right.isOriginalPoster) {
        return left.isOriginalPoster ? -1 : 1;
      }
      if (left.isCurrentUser !== right.isCurrentUser) {
        return left.isCurrentUser ? -1 : 1;
      }
      return left.name.localeCompare(right.name, "es", {
        sensitivity: "base"
      });
    });
  }
  function getThreadAuthorOptionLabel(option) {
    const markers = [];
    if (option.isOriginalPoster) {
      markers.push("autor");
    }
    if (option.isCurrentUser) {
      markers.push("tú");
    }
    return markers.length > 0 ? `${option.name} (${markers.join(", ")})` : option.name;
  }
  function resolveThreadAuthorInputValue(value, options) {
    const input = normalizeText(value);
    const inputKey = normalizeAuthorName(input);
    if (!inputKey) {
      return null;
    }
    for (const option of options) {
      const labelKey = normalizeAuthorName(getThreadAuthorOptionLabel(option));
      if (option.key === inputKey || normalizeAuthorName(option.name) === inputKey || labelKey === inputKey) {
        return option.key;
      }
    }
    return null;
  }

  // src/ui/threadSearchPanelDom.ts
  function syncThreadSearchTextInput(searchQuery) {
    const textInput = document.getElementById(THREAD_SEARCH_TEXT_INPUT_ID);
    if (textInput instanceof HTMLInputElement && document.activeElement !== textInput) {
      textInput.value = searchQuery;
    }
  }
  function refreshThreadAuthorDatalist(options, activeAuthorFilters) {
    const datalist = document.getElementById(THREAD_SEARCH_AUTHOR_DATALIST_ID);
    if (!(datalist instanceof HTMLDataListElement)) {
      return;
    }
    datalist.textContent = "";
    for (const option of options) {
      if (activeAuthorFilters.has(option.key)) {
        continue;
      }
      const element = document.createElement("option");
      element.value = getThreadAuthorOptionLabel(option);
      element.label = `${option.count} mensajes`;
      datalist.append(element);
    }
  }
  function refreshSelectedThreadAuthors(authorKeys, authorOptions, onRemoveAuthor) {
    const container = document.getElementById(THREAD_SEARCH_SELECTED_AUTHORS_ID);
    if (!(container instanceof HTMLElement)) {
      return;
    }
    container.textContent = "";
    for (const authorKey of authorKeys) {
      const option = authorOptions.find((candidate) => candidate.key === authorKey) || null;
      const chip = document.createElement("span");
      chip.className = "fc-premium-thread-author-chip";
      chip.textContent = option ? getThreadAuthorOptionLabel(option) : authorKey;
      const remove = document.createElement("button");
      remove.type = "button";
      remove.textContent = "x";
      remove.title = "Quitar usuario";
      remove.addEventListener("click", () => {
        onRemoveAuthor(authorKey);
      });
      chip.append(remove);
      container.append(chip);
    }
  }
  function renderThreadSearchStatus(options) {
    const status = document.getElementById(THREAD_SEARCH_STATUS_ID);
    if (!(status instanceof HTMLElement)) {
      return;
    }
    const total = options.counts?.total ?? options.totalPosts;
    const visible = options.counts?.visible ?? getVisibleThreadSearchPostWrapperCount();
    const loading = options.threadLoadState.isLoading ? ` · cargando ${options.threadLoadState.loadedPages}/${options.threadLoadState.targetPages}` : "";
    status.textContent = options.hasActiveFilters ? `${visible}/${total} mensajes${loading}` : `${total} mensajes${loading}`;
  }
  function renderThreadSearchEmptyState(options) {
    const posts = options.posts;
    if (!posts) {
      return;
    }
    let empty = document.getElementById(THREAD_SEARCH_EMPTY_ID);
    if (!empty) {
      empty = document.createElement("div");
      empty.id = THREAD_SEARCH_EMPTY_ID;
      posts.before(empty);
    }
    empty.textContent = options.isLoading ? "No hay mensajes cargados que coincidan con estos filtros." : "No hay mensajes que coincidan con estos filtros.";
    empty.hidden = !(options.hasActiveFilters && (options.counts?.visible ?? 0) === 0);
  }
  function getVisibleThreadSearchPostWrapperCount() {
    return Array.from(document.querySelectorAll(".fc-premium-post-wrapper")).filter((wrapper) => wrapper instanceof HTMLElement && isVisible(wrapper)).length;
  }

  // src/app/core/threadPostFilterController.ts
  function createThreadPostFilterController(options) {
    const threadPostSearchTextById = new Map;
    function getAuthenticatedUsername() {
      const profileLink = Array.from(document.querySelectorAll("a[href*='member.php?u=']")).find((link) => link instanceof HTMLAnchorElement && normalizeText(link.textContent) === "Tu Perfil");
      const profileUserId = profileLink instanceof HTMLAnchorElement ? toUrl(profileLink.href)?.searchParams.get("u") || "" : "";
      if (profileUserId) {
        const usernameLink = Array.from(document.querySelectorAll("a[href*='member.php?u=']")).find((link) => {
          if (!(link instanceof HTMLAnchorElement)) {
            return false;
          }
          const text = normalizeText(link.textContent);
          return text && text !== "Tu Perfil" && toUrl(link.href)?.searchParams.get("u") === profileUserId;
        });
        if (usernameLink instanceof HTMLAnchorElement) {
          return normalizeText(usernameLink.textContent);
        }
      }
      return normalizeText(document.querySelector("#navbar_username")?.textContent);
    }
    function getThreadAuthorOptions2(posts = options.getLoadedThreadPosts()) {
      return getThreadAuthorOptions(posts, getAuthenticatedUsername());
    }
    function resolveThreadAuthorInputValue2(value) {
      return resolveThreadAuthorInputValue(value, getThreadAuthorOptions2());
    }
    function getThreadPostSearchText(post) {
      const cached = threadPostSearchTextById.get(post.id);
      if (cached !== undefined) {
        return cached;
      }
      const doc = parseHtml(post.html);
      const message = doc.getElementById(`post_message_${post.id}`) || doc.body || null;
      const text = normalizeLayoutText(message?.textContent || "");
      threadPostSearchTextById.set(post.id, text);
      return text;
    }
    function hasActiveThreadPostFilters() {
      return Boolean(options.getActiveThreadSearchQuery()) || options.getActiveAuthorFilters().size > 0;
    }
    function ensureThreadSearchPanel() {
      const existing = document.getElementById(THREAD_SEARCH_PANEL_ID);
      const headerSlot = document.getElementById(THREAD_SEARCH_HEADER_SLOT_ID);
      if (existing instanceof HTMLTableElement && headerSlot instanceof HTMLElement && !headerSlot.contains(existing)) {
        headerSlot.append(existing);
      }
      if (existing instanceof HTMLTableElement) {
        return existing;
      }
      const posts = options.getPostsElement();
      if (!posts) {
        return null;
      }
      const panel = ThreadSearchPanel({
        searchQuery: options.getActiveThreadSearchQuery(),
        onSearchInput: setThreadSearchQuery,
        onAddAuthor: addThreadAuthorFilterFromInput,
        onClearFilters: clearThreadPostFilters
      });
      if (headerSlot instanceof HTMLElement) {
        headerSlot.append(panel);
      } else {
        posts.before(panel);
      }
      return panel;
    }
    function refreshThreadAuthorDatalist2() {
      refreshThreadAuthorDatalist(getThreadAuthorOptions2(), options.getActiveAuthorFilters());
    }
    function refreshSelectedThreadAuthors2() {
      refreshSelectedThreadAuthors(options.getActiveAuthorFilters(), getThreadAuthorOptions2(), removeThreadAuthorFilter);
    }
    function renderThreadSearchStatus2(counts) {
      renderThreadSearchStatus({
        counts,
        totalPosts: options.getLoadedThreadPosts().length,
        threadLoadState: options.getThreadLoadState(),
        hasActiveFilters: hasActiveThreadPostFilters()
      });
    }
    function renderThreadSearchEmptyState2(counts) {
      renderThreadSearchEmptyState({
        posts: options.getPostsElement(),
        counts,
        isLoading: options.getThreadLoadState().isLoading,
        hasActiveFilters: hasActiveThreadPostFilters()
      });
    }
    function renderThreadSearchPanel(counts) {
      const panel = ensureThreadSearchPanel();
      if (!panel) {
        return;
      }
      syncThreadSearchTextInput(options.getActiveThreadSearchQuery());
      refreshThreadAuthorDatalist2();
      refreshSelectedThreadAuthors2();
      renderThreadSearchStatus2(counts);
      renderThreadSearchEmptyState2(counts);
    }
    function setThreadSearchQuery(query2) {
      const hadFilters = hasActiveThreadPostFilters();
      const nextQuery = normalizeText(query2);
      if (options.getActiveThreadSearchQuery() === nextQuery) {
        return;
      }
      options.setActiveThreadSearchQuery(nextQuery);
      updateThreadPostFilters({
        render: hadFilters !== hasActiveThreadPostFilters()
      });
    }
    function addThreadAuthorFilter(authorKey) {
      const activeAuthorFilters = options.getActiveAuthorFilters();
      if (!authorKey || activeAuthorFilters.has(authorKey)) {
        return;
      }
      const hadFilters = hasActiveThreadPostFilters();
      activeAuthorFilters.add(authorKey);
      updateThreadPostFilters({
        render: hadFilters !== hasActiveThreadPostFilters()
      });
    }
    function addThreadAuthorFilterFromInput() {
      const input = document.getElementById(THREAD_SEARCH_AUTHOR_INPUT_ID);
      if (!(input instanceof HTMLInputElement)) {
        return;
      }
      const authorKey = resolveThreadAuthorInputValue2(input.value);
      if (!authorKey) {
        return;
      }
      input.value = "";
      addThreadAuthorFilter(authorKey);
    }
    function removeThreadAuthorFilter(authorKey) {
      if (!options.getActiveAuthorFilters().delete(authorKey)) {
        return;
      }
      updateThreadPostFilters({
        render: !hasActiveThreadPostFilters()
      });
    }
    function clearThreadPostFilters() {
      if (!hasActiveThreadPostFilters()) {
        return;
      }
      options.setActiveThreadSearchQuery("");
      options.clearActiveAuthorFilters();
      updateThreadPostFilters({ render: true });
    }
    function updateThreadPostFilters(optionsOverride = {}) {
      const hadGraphView = options.hasGraphViewOrPendingGraphView();
      if (hasActiveThreadPostFilters()) {
        options.clearGraphViewAndPageFilter();
      } else if (!options.hasPageFilter()) {
        options.setPageFilterToCurrentPage();
      }
      options.syncThreadStateUrl();
      if (hadGraphView || optionsOverride.render) {
        threadPostSearchTextById.clear();
        options.renderThreadPosts();
        options.renderThreadSummaryMenu();
        return;
      }
      const counts = applyThreadPostFilters();
      options.applyPageFilter();
      options.updateOriginalThreadPageMenus();
      renderThreadSearchPanel(counts);
      options.refreshNavigation({ reset: true });
    }
    function clearThreadFilters() {
      if (!hasActiveThreadPostFilters() && !options.hasGraphViewOrPendingGraphView()) {
        return;
      }
      options.setActiveThreadSearchQuery("");
      options.clearActiveAuthorFilters();
      options.setPageFilterToCurrentPage();
      options.clearGraphViewAndPageFilter();
      options.syncThreadStateUrl();
      options.updateOriginalThreadPageMenus();
      options.renderThreadPosts();
      options.renderThreadSummaryMenu();
    }
    function toggleAuthorFilter(author) {
      const authorKey = normalizeAuthorName(author);
      if (!authorKey) {
        return;
      }
      const activeAuthorFilters = options.getActiveAuthorFilters();
      const hadFilters = hasActiveThreadPostFilters();
      if (activeAuthorFilters.has(authorKey)) {
        activeAuthorFilters.delete(authorKey);
      } else {
        activeAuthorFilters.add(authorKey);
      }
      updateThreadPostFilters({
        render: hadFilters !== hasActiveThreadPostFilters()
      });
    }
    function applyThreadPostFilters() {
      const query2 = normalizeLayoutText(options.getActiveThreadSearchQuery());
      const loadedThreadPosts = options.getLoadedThreadPosts();
      const postById = new Map(loadedThreadPosts.map((post) => [post.id, post]));
      return applyThreadPostFiltersToRenderedPosts({
        posts: options.getPostsElement(),
        query: query2,
        activeAuthorFilters: options.getActiveAuthorFilters(),
        postById,
        getPostSearchText: getThreadPostSearchText
      });
    }
    function enhanceAuthorFilterButton2(wrapper, author) {
      enhanceAuthorFilterButton(wrapper, author, toggleAuthorFilter);
    }
    return {
      hasActiveThreadPostFilters,
      renderThreadSearchPanel,
      clearThreadPostFilters,
      clearThreadFilters,
      applyThreadPostFilters,
      enhanceAuthorFilterButton: enhanceAuthorFilterButton2
    };
  }

  // src/ui/postQuoteDom.ts
  function enhanceQuoteLinks(options) {
    for (const link of options.wrapper.querySelectorAll("a[href*='showthread.php?p='][href*='#post']")) {
      if (!(link instanceof HTMLAnchorElement)) {
        continue;
      }
      const quotedPostId = options.getQuotedPostId(link.getAttribute("href") || link.href);
      if (!quotedPostId) {
        continue;
      }
      link.dataset.fcPremiumQuoteTarget = quotedPostId;
      link.title = "Ir al mensaje citado";
      markQuoteBlock({
        link,
        quotedPostId,
        sourcePostId: options.sourcePostId,
        onReadConversation: options.onReadConversation
      });
      link.addEventListener("click", (event) => {
        const target = document.getElementById(`post${quotedPostId}`);
        if (!target) {
          return;
        }
        event.preventDefault();
        options.onOpenQuotedPost(quotedPostId);
      });
    }
  }
  function markQuoteBlock(options) {
    const quoteTable = options.link.closest("table");
    const quoteWrapper = quoteTable?.parentElement;
    if (!(quoteWrapper instanceof HTMLElement)) {
      return;
    }
    if (!(quoteTable instanceof HTMLTableElement)) {
      return;
    }
    quoteWrapper.dataset.fcPremiumQuoteBlock = options.quotedPostId;
    renderQuoteBlockActions({
      quoteWrapper,
      quoteLink: options.link,
      sourcePostId: options.sourcePostId,
      quotedPostId: options.quotedPostId,
      onReadConversation: options.onReadConversation
    });
    const quoteCell = quoteTable.querySelector("td");
    const body = Array.from(quoteCell?.children || []).find((child) => child instanceof HTMLElement && child !== options.link.parentElement && child.textContent.trim().length > 0);
    if (body instanceof HTMLElement) {
      body.dataset.fcPremiumQuoteBody = "true";
    }
  }
  function renderQuoteBlockActions(options) {
    if (!options.sourcePostId) {
      return;
    }
    options.quoteWrapper.querySelector(".fc-premium-quote-actions")?.remove();
    const targetContainer = options.quoteLink.parentElement || options.quoteWrapper;
    const actions = document.createElement("div");
    actions.className = "fc-premium-quote-actions";
    const conversationButton = document.createElement("button");
    conversationButton.type = "button";
    conversationButton.textContent = "Ver conversación";
    conversationButton.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      options.onReadConversation(options.sourcePostId, options.quotedPostId);
    });
    actions.append(conversationButton);
    targetContainer.append(actions);
  }

  // src/ui/postNativeDom.ts
  function getPostStatusImage(wrapper) {
    const footerRow = getPostFooterRow(wrapper);
    const image = footerRow?.querySelector("img[src*='statusicon/user_']");
    return image instanceof HTMLImageElement ? image : null;
  }
  function getPostReportLink(wrapper) {
    const footerRow = getPostFooterRow(wrapper);
    const link = footerRow?.querySelector("a[href*='report.php?p=']");
    return link instanceof HTMLAnchorElement ? link : null;
  }
  function relocatePostFooterControls(wrapper) {
    const footerRow = getPostFooterRow(wrapper);
    const existingActions = wrapper.querySelector(".fc-premium-post-reply-actions");
    const existingReplyLinks = Array.from(existingActions?.querySelectorAll("a[href*='newreply.php?do=newreply']") || []).filter((link) => link instanceof HTMLAnchorElement);
    existingActions?.remove();
    const footerReplyLinks = Array.from(footerRow?.querySelectorAll("a[href*='newreply.php?do=newreply']") || []).filter((link) => link instanceof HTMLAnchorElement);
    const replyLinks = [...footerReplyLinks, ...existingReplyLinks].filter((link) => !isQuickReplyLink(link) && isQuoteReplyLink(link));
    for (const link of footerReplyLinks) {
      if (isQuickReplyLink(link)) {
        link.remove();
      }
    }
    if (replyLinks.length > 0) {
      const actions = document.createElement("div");
      actions.className = "fc-premium-post-reply-actions";
      for (const link of replyLinks) {
        actions.append(link);
      }
      wrapper.append(actions);
    }
    if (footerRow) {
      footerRow.classList.add("fc-premium-post-footer-row");
    }
  }
  function removeTrailingPostLayoutArtifacts(wrapper) {
    const table = wrapper.querySelector(POST_TABLE_SELECTOR);
    const postContainer = table?.closest("div[id^='edit']");
    if (!(table instanceof HTMLElement) || !postContainer) {
      return;
    }
    let node = table.nextSibling;
    while (node) {
      const next = node.nextSibling;
      if (isPreservedHiddenPostMenuNode(node)) {
        node = next;
        continue;
      }
      if (!isRemovableTrailingPostLayoutNode(node)) {
        break;
      }
      node.remove();
      node = next;
    }
  }
  function getPostFooterRow(wrapper) {
    const footerSelector = "a[href*='report.php?p='], a[href*='newreply.php?do=newreply'], img[src*='statusicon/user_']";
    const rows = Array.from(wrapper.querySelectorAll("tr"));
    return rows.find((row) => {
      if (!(row instanceof HTMLTableRowElement)) {
        return false;
      }
      return Array.from(row.querySelectorAll(footerSelector)).some((control) => control instanceof HTMLElement && !isInsidePremiumPostUi(control));
    }) || null;
  }
  function isInsidePremiumPostUi(element) {
    return Boolean(element.closest(".fc-premium-author-hover-card, .fc-premium-post-reply-actions, .fc-premium-quote-actions"));
  }
  function isPreservedHiddenPostMenuNode(node) {
    return node instanceof HTMLElement && (node.classList.contains("vbmenu_popup") || /_menu$/.test(node.id));
  }
  function isSpacerImage(image) {
    const src = image.getAttribute("src") || "";
    return /nada\.gif|clear\.gif|spacer/i.test(src);
  }
  function isEmptyPostSeparatorTable(element) {
    if (!(element instanceof HTMLTableElement) || !element.classList.contains("cajasprin") || normalizeText(element.textContent)) {
      return false;
    }
    if (element.querySelector("a, button, input, select, textarea")) {
      return false;
    }
    return Array.from(element.querySelectorAll("img")).every((image) => image instanceof HTMLImageElement && isSpacerImage(image));
  }
  function isRemovableTrailingPostLayoutNode(node) {
    if (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.COMMENT_NODE) {
      return true;
    }
    if (node instanceof HTMLBRElement) {
      return true;
    }
    return node instanceof HTMLElement && isEmptyPostSeparatorTable(node);
  }

  // src/ui/postAuthorDom.ts
  function createHeaderAuthorMeta(post, authorCell, wrapper) {
    const meta = document.createElement("span");
    meta.className = "fc-premium-header-author";
    const authorLink = authorCell?.querySelector(".bigusername");
    if (authorLink instanceof HTMLAnchorElement) {
      const link = document.createElement("a");
      link.href = authorLink.href;
      link.textContent = post.author || normalizeText(authorLink.textContent);
      meta.append(link);
    } else {
      meta.textContent = post.author;
    }
    if (authorCell instanceof HTMLElement) {
      const card = document.createElement("span");
      card.className = "fc-premium-author-hover-card";
      const avatar = getAuthorProfileImage(authorCell);
      if (avatar) {
        card.append(avatar);
      }
      const title = document.createElement("strong");
      title.textContent = post.author || "Usuario";
      card.append(title);
      for (const line of getAuthorHoverLines(authorCell)) {
        const detail = document.createElement("span");
        detail.textContent = line;
        card.append(detail);
      }
      appendAuthorFooterControls(card, wrapper);
      meta.append(card);
    }
    return meta;
  }
  function getAuthorHoverLines(authorCell) {
    const lines = [];
    const seen = new Set;
    const addLine = (text) => {
      const line = normalizeText(text).replace(/\s+filtrar$/, "");
      if (!line || seen.has(line)) {
        return;
      }
      seen.add(line);
      lines.push(line);
    };
    for (const block of authorCell.querySelectorAll(".smallfont")) {
      const childDivs = Array.from(block.children).filter((child) => child instanceof HTMLDivElement);
      if (childDivs.length === 0) {
        addLine(block.textContent);
        continue;
      }
      for (const child of childDivs) {
        addLine(child.textContent);
      }
    }
    return lines.slice(0, 8);
  }
  function getAuthorProfileImage(authorCell) {
    const images = Array.from(authorCell.querySelectorAll("img")).filter((image) => {
      if (!(image instanceof HTMLImageElement)) {
        return false;
      }
      const src = image.getAttribute("src") || "";
      return Boolean(src) && !/statusicon|clear\.gif|spacer|button/i.test(src);
    });
    const avatar = images.find((image) => /customavatar|avatar|profilepic|album/i.test(image.getAttribute("src") || "")) || images.find((image) => {
      const width = Number(image.getAttribute("width") || image.width || 0);
      const height = Number(image.getAttribute("height") || image.height || 0);
      return width >= 40 || height >= 40;
    });
    if (!avatar) {
      return null;
    }
    const clone = document.createElement("img");
    clone.className = "fc-premium-author-avatar";
    clone.src = avatar.src;
    clone.alt = avatar.alt || "";
    clone.loading = "lazy";
    return clone;
  }
  function appendAuthorFooterControls(card, wrapper) {
    const statusImage = getPostStatusImage(wrapper);
    const reportLink = getPostReportLink(wrapper);
    if (!statusImage && !reportLink) {
      return;
    }
    const actions = document.createElement("span");
    actions.className = "fc-premium-author-card-actions";
    if (statusImage) {
      const status = document.createElement("span");
      status.className = "fc-premium-author-status";
      const icon = statusImage.cloneNode(true);
      const label = document.createElement("span");
      label.textContent = statusImage.title || statusImage.alt || "Estado";
      status.append(icon, label);
      actions.append(status);
    }
    if (reportLink) {
      const report = document.createElement("a");
      const reportImage = reportLink.querySelector("img");
      report.className = "fc-premium-author-report-link";
      report.href = reportLink.href;
      report.rel = reportLink.rel;
      report.title = reportLink.title || reportImage?.title || reportImage?.alt || "Reportar mensaje";
      if (reportImage instanceof HTMLImageElement) {
        report.append(reportImage.cloneNode(true));
      }
      const label = document.createElement("span");
      label.textContent = "Reportar";
      report.append(label);
      actions.append(report);
    }
    card.append(actions);
  }

  // src/ui/postHeaderDom.ts
  function enhanceNativePostHeader(wrapper, post) {
    const table = wrapper.querySelector(POST_TABLE_SELECTOR);
    const postCountLink = table?.querySelector(`a[id='postcount${post.id}']`);
    const numberCell = postCountLink?.closest("td");
    const headerRow = postCountLink?.closest("tr");
    const dateCell = Array.from(headerRow?.children || []).find((cell) => cell instanceof HTMLTableCellElement && cell !== numberCell) || null;
    const authorCellElement = table?.querySelector("td[width='175'][rowspan]");
    const authorCell = authorCellElement instanceof HTMLElement ? authorCellElement : null;
    if (authorCell) {
      authorCell.classList.add("fc-premium-author-cell");
    }
    const messageCell = table?.querySelector(`#td_post_${post.id}`);
    if (messageCell instanceof HTMLElement) {
      messageCell.classList.add("fc-premium-message-cell");
    }
    if (dateCell instanceof HTMLTableCellElement && !dateCell.querySelector(".fc-premium-header-author")) {
      dateCell.classList.add("fc-premium-post-date-cell");
      dateCell.append(createHeaderAuthorMeta(post, authorCell, wrapper));
    }
    if (numberCell instanceof HTMLTableCellElement) {
      numberCell.classList.add("fc-premium-post-number-cell");
    }
    return {
      dateCell: dateCell instanceof HTMLTableCellElement ? dateCell : null,
      numberCell: numberCell instanceof HTMLTableCellElement ? numberCell : null
    };
  }

  // src/ui/postCompactLayoutDom.ts
  function updatePostCompactLayout(wrapper, compact) {
    const table = wrapper.querySelector(POST_TABLE_SELECTOR);
    if (!(table instanceof HTMLTableElement)) {
      return;
    }
    const authorCell = table.querySelector(".fc-premium-author-cell");
    const headerRow = table.rows[0] || null;
    for (const row of Array.from(table.rows)) {
      if (row === headerRow) {
        continue;
      }
      const rowHasAuthorCell = Array.from(row.cells).some((cell) => cell.classList.contains("fc-premium-author-cell"));
      const shouldExpandRow = rowHasAuthorCell || row.cells.length === 1;
      for (const cell of Array.from(row.cells)) {
        if (!(cell instanceof HTMLTableCellElement) || cell === authorCell || cell.classList.contains("fc-premium-author-cell")) {
          continue;
        }
        if (compact && shouldExpandRow) {
          applyCompactColSpan(cell);
        } else {
          restoreOriginalColSpan(cell);
        }
      }
    }
  }
  function rememberCellColSpan(cell) {
    if (!cell.dataset.fcPremiumOriginalColspan) {
      cell.dataset.fcPremiumOriginalColspan = String(cell.colSpan || 1);
    }
  }
  function applyCompactColSpan(cell) {
    rememberCellColSpan(cell);
    cell.colSpan = Math.max(cell.colSpan, 2);
  }
  function restoreOriginalColSpan(cell) {
    const original = Number(cell.dataset.fcPremiumOriginalColspan || "1");
    cell.colSpan = Number.isFinite(original) && original > 0 ? original : 1;
  }

  // src/ui/postReplyBadgeDom.ts
  function appendReplyBadge(options) {
    if (options.post.replyCount <= 0 || !options.container) {
      return;
    }
    const post = options.post;
    const wrapper = options.container.closest(".fc-premium-post-wrapper");
    if (wrapper instanceof HTMLElement) {
      wrapper.dataset.fcPremiumReplyCount = String(post.replyCount);
      wrapper.dataset.fcPremiumRank = String(options.rank);
    }
    const badge = document.createElement("span");
    badge.className = "fc-premium-reply-badge";
    badge.textContent = post.replyCount === 1 ? "1 cita" : `${post.replyCount} citas`;
    appendReplyLinks({
      badge,
      post,
      postById: options.postById,
      onJumpToPost: options.onJumpToPost,
      onShowQuotedBy: options.onShowQuotedBy
    });
    options.container.append(badge);
  }
  function appendReplyLinks(options) {
    const maxLinks = 3;
    const visibleReplyIds = options.post.replyingPostIds.slice(0, maxLinks);
    if (visibleReplyIds.length === 0) {
      return;
    }
    const label = document.createElement("span");
    label.className = "fc-premium-original-position";
    label.textContent = "·";
    options.badge.append(label);
    for (const replyingPostId of visibleReplyIds) {
      const reply = options.postById.get(replyingPostId);
      const link = document.createElement("a");
      link.href = new URL(`showthread.php?p=${replyingPostId}#post${replyingPostId}`, location.href).href;
      link.textContent = `#${reply?.postNumber || replyingPostId}`;
      link.addEventListener("click", (event) => {
        if (!document.getElementById(`post${replyingPostId}`)) {
          return;
        }
        event.preventDefault();
        options.onJumpToPost(replyingPostId);
      });
      options.badge.append(link);
      options.badge.append(document.createTextNode(" "));
    }
    if (options.post.replyingPostIds.length > visibleReplyIds.length) {
      const remaining = document.createElement("span");
      remaining.className = "fc-premium-original-position";
      remaining.textContent = ` +${options.post.replyingPostIds.length - visibleReplyIds.length}`;
      options.badge.append(remaining);
    }
    if (options.post.replyingPostIds.length > 1) {
      const quotedByButton = document.createElement("button");
      quotedByButton.type = "button";
      quotedByButton.textContent = "Ver todas";
      quotedByButton.title = "Ver citadores";
      quotedByButton.addEventListener("click", (event) => {
        event.preventDefault();
        event.stopPropagation();
        options.onShowQuotedBy(options.post.id);
      });
      options.badge.append(quotedByButton);
    }
  }

  // src/app/core/threadPostRenderer.ts
  function createThreadPostRenderer(options) {
    let lastRenderedStablePageSignature = null;
    function selectPostById(postId, selectOptions = {}) {
      const table = document.getElementById(`post${postId}`);
      const wrapper = table?.closest(".fc-premium-post-wrapper");
      if (!(wrapper instanceof HTMLElement)) {
        return;
      }
      options.selectNavigationElement(wrapper, selectOptions);
    }
    function enhanceQuoteLinks2(wrapper) {
      enhanceQuoteLinks({
        wrapper,
        sourcePostId: getPostIdFromNavigationElement(wrapper),
        getQuotedPostId,
        onOpenQuotedPost: options.jumpToLoadedPost,
        onReadConversation: (sourcePostId, quotedPostId) => {
          options.setActiveGraphView("conversation", sourcePostId, quotedPostId, {
            scrollToFirstPost: true
          });
        }
      });
    }
    function renderPost(post, rank, postById, replyIndentDepth) {
      const template = document.createElement("template");
      template.innerHTML = post.html;
      const wrapper = template.content.firstElementChild;
      if (!(wrapper instanceof HTMLElement)) {
        return document.createElement("div");
      }
      wrapper.classList.add("fc-premium-post-wrapper");
      wrapper.dataset.fcPremiumOriginalPage = String(post.pageNumber);
      if (replyIndentDepth > 0) {
        wrapper.dataset.fcPremiumReplyIndent = String(replyIndentDepth);
      }
      enhanceQuoteLinks2(wrapper);
      options.enhanceAuthorFilterButton(wrapper, post.author);
      const header = enhanceNativePostHeader(wrapper, post);
      removeTrailingPostLayoutArtifacts(wrapper);
      relocatePostFooterControls(wrapper);
      if (post.isOriginalPoster) {
        wrapper.dataset.fcPremiumOriginalPoster = "true";
      }
      appendReplyBadge({
        container: header.dateCell || header.numberCell,
        post,
        rank,
        postById,
        onJumpToPost: options.jumpToLoadedPost,
        onShowQuotedBy: (postId) => {
          options.setActiveGraphView("quoted-by", postId, null, {
            scrollToFirstReply: true
          });
        }
      });
      updatePostCompactLayout(wrapper, options.compactModeEnabled);
      return wrapper;
    }
    function getStablePageRenderSignature(optionsForSignature) {
      const activePageFilter = options.getActivePageFilter();
      if (!activePageFilter || options.getActiveGraphView() || options.hasActiveThreadPostFilters() || optionsForSignature.pendingInitialHashPostId) {
        return null;
      }
      const visiblePageSignature = optionsForSignature.viewPosts.filter((post) => post.pageNumber === activePageFilter).map((post) => [
        post.id,
        post.pageNumber,
        hashString(post.html).toString(36),
        post.isOriginalPoster ? 1 : 0
      ].join(":")).join("|");
      return `${activePageFilter}|${visiblePageSignature}`;
    }
    function renderThreadPosts(posts) {
      const postsElement = options.getPostsElement();
      if (!postsElement) {
        return;
      }
      const pendingInitialHashPostId = options.getPendingInitialHashPostId();
      const selectedPostId = pendingInitialHashPostId || getPostIdFromNavigationElement(options.getSelectedNavigationItem()?.element || undefined);
      postsElement.dataset.fcPremiumGraphView = options.getActiveGraphView()?.type || "";
      const fragment = document.createDocumentFragment();
      const postById = new Map(posts.map((post) => [post.id, post]));
      const rankByPostId = getReplyRankByPostId(posts);
      const viewPosts = options.getThreadViewPosts(posts);
      const stablePageSignature = getStablePageRenderSignature({
        pendingInitialHashPostId,
        viewPosts
      });
      if (stablePageSignature !== null && stablePageSignature === lastRenderedStablePageSignature) {
        options.renderThreadSearchPanel();
        return;
      }
      lastRenderedStablePageSignature = stablePageSignature;
      postsElement.textContent = "";
      for (const [index, post] of viewPosts.entries()) {
        fragment.append(renderPost(post, rankByPostId.get(post.id) || 0, postById, options.getReplyIndentDepth(post, index)));
      }
      postsElement.append(fragment);
      const filterCounts = options.applyThreadPostFilters();
      options.applyPageFilter();
      options.updateOriginalThreadPageMenus();
      options.renderThreadSearchPanel(filterCounts);
      options.refreshNavigation({ reset: true });
      if (selectedPostId) {
        const selectedTable = document.getElementById(`post${selectedPostId}`);
        const selectedWrapper = selectedTable?.closest(".fc-premium-post-wrapper");
        if (selectedWrapper instanceof HTMLElement && isVisible(selectedWrapper)) {
          selectPostById(selectedPostId, {
            scroll: selectedPostId === pendingInitialHashPostId,
            updateUrl: false
          });
          if (selectedPostId === pendingInitialHashPostId) {
            options.clearPendingInitialHashPostId();
          }
        }
      }
    }
    return {
      renderThreadPosts,
      selectPostById
    };
  }

  // src/app/core/threadUrlController.ts
  function createThreadUrlController(options) {
    function writeCurrentThreadStateQueryParams(url) {
      clearThreadStateQueryParams(url);
      const graphView = options.getActiveGraphView() || options.getPendingGraphView();
      if (graphView) {
        url.searchParams.set(THREAD_STATE_QUERY_PARAMS.graphType, graphView.type);
        url.searchParams.set(THREAD_STATE_QUERY_PARAMS.graphRoot, graphView.rootPostId);
        if (graphView.relatedPostId) {
          url.searchParams.set(THREAD_STATE_QUERY_PARAMS.graphRelated, graphView.relatedPostId);
        }
      }
      const searchQuery = options.getActiveThreadSearchQuery();
      if (searchQuery) {
        url.searchParams.set(THREAD_STATE_QUERY_PARAMS.searchQuery, searchQuery);
      }
      for (const author of options.getActiveAuthorFilters()) {
        url.searchParams.append(THREAD_STATE_QUERY_PARAMS.authorFilter, author);
      }
    }
    function updateBrowserHistory(url, historyMode) {
      if (historyMode === "push" && url.href !== location.href) {
        window.history.pushState(window.history.state, "", url.href);
        return;
      }
      window.history.replaceState(window.history.state, "", url.href);
    }
    function syncThreadStateUrl(syncOptions = {}) {
      if (!isThreadPage()) {
        return;
      }
      const url = new URL(location.href);
      writeCurrentThreadStateQueryParams(url);
      updateBrowserHistory(url, syncOptions.history || "replace");
    }
    function resolveCurrentThreadId() {
      return getThreadId(new URL(location.href)) || getThreadIdFromDocument(document) || options.getThreadPages().map((page) => getThreadId(new URL(page.url))).find(Boolean) || null;
    }
    function getThreadPageUrl(pageNumber, urlOptions = {}) {
      const currentUrl = new URL(location.href);
      const threadId = resolveCurrentThreadId() || "";
      const url = new URL(currentUrl.origin + currentUrl.pathname);
      if (threadId) {
        url.searchParams.set("t", threadId);
      }
      if (pageNumber > 1) {
        url.searchParams.set("page", String(pageNumber));
      }
      if (urlOptions.includeState) {
        writeCurrentThreadStateQueryParams(url);
      }
      if (urlOptions.preserveHash) {
        url.hash = location.hash;
      }
      return url;
    }
    function updateThreadPageUrl(pageNumber, updateOptions = {}) {
      const url = getThreadPageUrl(pageNumber, {
        includeState: true,
        preserveHash: updateOptions.preserveHash
      });
      updateBrowserHistory(url, updateOptions.history || "replace");
    }
    function updateSelectedPostUrl(selected) {
      const postId = getPostIdFromNavigationElement(selected.element);
      if (!postId) {
        return;
      }
      const threadId = resolveCurrentThreadId();
      if (!threadId) {
        return;
      }
      const post = options.getLoadedThreadPosts().find((item) => item.id === postId);
      const url = new URL(location.href);
      url.search = "";
      url.searchParams.set("t", threadId);
      if (post && post.pageNumber > 1) {
        url.searchParams.set("page", String(post.pageNumber));
      }
      writeCurrentThreadStateQueryParams(url);
      url.hash = `post${postId}`;
      window.history.replaceState(window.history.state, "", url.href);
    }
    function getThreadPagesForTotal(totalPages) {
      const pages = [];
      for (let pageNumber = 1;pageNumber <= totalPages; pageNumber += 1) {
        pages.push({ pageNumber, url: getThreadPageUrl(pageNumber).href });
      }
      return pages;
    }
    return {
      writeCurrentThreadStateQueryParams,
      syncThreadStateUrl,
      updateSelectedPostUrl,
      getThreadPagesForTotal,
      getThreadPageUrl,
      updateThreadPageUrl
    };
  }

  // src/app/core/threadPageController.ts
  function createThreadPageController() {
    const initialThreadQueryState = readThreadQueryState();
    let loadedThreadPosts = [];
    let threadPages = [];
    let loadedThreadPageNumbers = new Set;
    let threadLoadState = {
      loadedPages: 0,
      targetPages: 0,
      totalPages: 0,
      loadedPosts: 0,
      isLoading: false
    };
    let threadGraph = createEmptyThreadGraph();
    let activeGraphView = null;
    let pendingGraphView = initialThreadQueryState.graphView;
    const compactModeEnabled = true;
    let activePageFilter = initialThreadQueryState.pageFilter;
    let activeAuthorFilters = new Set(initialThreadQueryState.authorFilters);
    let activeThreadSearchQuery = initialThreadQueryState.searchQuery;
    let pendingInitialHashPostId = getLocationPostHashId();
    const threadUrlController = createThreadUrlController({
      getThreadPages: () => threadPages,
      getLoadedThreadPosts: () => loadedThreadPosts,
      getActiveGraphView: () => activeGraphView,
      getPendingGraphView: () => pendingGraphView,
      getActiveThreadSearchQuery: () => activeThreadSearchQuery,
      getActiveAuthorFilters: () => activeAuthorFilters
    });
    const syncThreadStateUrl = threadUrlController.syncThreadStateUrl;
    const getThreadPagesForTotal = threadUrlController.getThreadPagesForTotal;
    const getThreadPageUrl = threadUrlController.getThreadPageUrl;
    const updateThreadPageUrl = threadUrlController.updateThreadPageUrl;
    function getPostsElement() {
      const posts = document.querySelector(POSTS_SELECTOR);
      return posts instanceof HTMLElement ? posts : null;
    }
    const threadPageHeaderController = createThreadPageHeaderController();
    const prepareThreadPage = threadPageHeaderController.prepareThreadPage;
    const renderShortcutHelpButton2 = threadPageHeaderController.renderShortcutHelpButton;
    function collectNavigationItems() {
      return isThreadPage() ? getPostNavigationItems(getPostsElement()) : [];
    }
    const navigationController = createNavigationController({
      collectNavigationItems,
      onRenderNavigationStatus: renderNavigationStatus,
      onUpdateSelectedThreadUrl: threadUrlController.updateSelectedPostUrl,
      getPostsElement
    });
    function renderNavigationStatus(selected) {
      document.getElementById(NAVIGATION_STATUS_ID)?.remove();
    }
    const refreshNavigation = navigationController.refreshNavigation;
    const moveNavigation = navigationController.moveNavigation;
    const selectNavigationElement = navigationController.selectNavigationElement;
    const getSelectedNavigationItem = navigationController.getSelectedNavigationItem;
    const getSelectedPostWrapper2 = navigationController.getSelectedPostWrapper;
    function quoteSelectedPost(wrapper) {
      return clickPostQuoteAction(wrapper);
    }
    function toggleSelectedPostMultiquote(wrapper) {
      return togglePostMultiquote(wrapper, getPostIdFromNavigationElement(wrapper));
    }
    function openThreadReplyWithoutQuote2() {
      return openThreadReplyWithoutQuote(getThreadId(new URL(location.href)));
    }
    function returnToThreadList() {
      const link = getThreadForumListLink();
      if (!link) {
        return false;
      }
      location.assign(link.href);
      return true;
    }
    function getThreadPages() {
      const maxPage = getMaxThreadPage(document);
      return getThreadPagesForTotal(maxPage);
    }
    function ensureThreadSummary2() {
      return ensureThreadSummary(getPostsElement());
    }
    function renderThreadSummaryMenu2(summary) {
      renderThreadSummaryMenu({
        summary,
        state: threadLoadState,
        onRefreshCache: async () => {
          await clearCurrentThreadCache();
          location.reload();
        }
      });
    }
    function jumpToLoadedPost(postId) {
      const post = loadedThreadPosts.find((item) => item.id === postId);
      if (post) {
        activePageFilter = post.pageNumber;
        updateThreadPageUrl(post.pageNumber);
        updateOriginalThreadPageMenus2();
        renderThreadPosts(loadedThreadPosts);
        renderThreadSummaryMenu2(document.getElementById(THREAD_SUMMARY_ID));
      }
      selectPostById(postId);
    }
    const threadPagePaginationController = createThreadPagePaginationController({
      getPostsElement,
      getThreadPages: () => threadPages,
      getActivePageFilter: () => activePageFilter,
      setActivePageFilter: (pageNumber) => {
        activePageFilter = pageNumber;
      },
      getActiveGraphView: () => activeGraphView,
      clearGraphView: () => {
        activeGraphView = null;
        pendingGraphView = null;
      },
      getThreadPageUrl,
      updateThreadPageUrl,
      renderThreadPosts: () => renderThreadPosts(loadedThreadPosts),
      renderThreadSummaryMenu: () => {
        renderThreadSummaryMenu2(document.getElementById(THREAD_SUMMARY_ID));
      }
    });
    const applyPageFilter = threadPagePaginationController.applyPageFilter;
    const updateOriginalThreadPageMenus2 = threadPagePaginationController.updateOriginalThreadPageMenus;
    const installThreadPageNavigation = threadPagePaginationController.installThreadPageNavigation;
    const threadPageKeyboard = createThreadPageKeyboardController({
      moveNavigation,
      getActiveGraphView: () => activeGraphView,
      hasActiveThreadPostFilters: () => hasActiveThreadPostFilters(),
      openThreadReplyWithoutQuote: openThreadReplyWithoutQuote2,
      quoteSelectedPost: (wrapper) => quoteSelectedPost(wrapper),
      toggleSelectedPostMultiquote: (wrapper) => toggleSelectedPostMultiquote(wrapper),
      getSelectedPostWrapper: getSelectedPostWrapper2,
      clearThreadFilters: () => clearThreadFilters(),
      navigatePage: (direction) => threadPagePaginationController.navigatePage(direction),
      returnToThreadList
    });
    function installKeyboardNavigation() {
      window.addEventListener("keydown", threadPageKeyboard.handleNavigationKeyDown, true);
    }
    const threadPostFilterController = createThreadPostFilterController({
      getPostsElement,
      getLoadedThreadPosts: () => loadedThreadPosts,
      getThreadLoadState: () => threadLoadState,
      getActiveThreadSearchQuery: () => activeThreadSearchQuery,
      setActiveThreadSearchQuery: (query2) => {
        activeThreadSearchQuery = query2;
      },
      getActiveAuthorFilters: () => activeAuthorFilters,
      clearActiveAuthorFilters: () => {
        activeAuthorFilters.clear();
      },
      setPageFilterToCurrentPage: () => {
        activePageFilter = getPageNumber(new URL(location.href));
      },
      hasPageFilter: () => Boolean(activePageFilter),
      hasGraphViewOrPendingGraphView: () => Boolean(activeGraphView || pendingGraphView),
      clearGraphViewAndPageFilter: () => {
        activeGraphView = null;
        pendingGraphView = null;
        activePageFilter = null;
      },
      syncThreadStateUrl,
      renderThreadPosts: () => renderThreadPosts(loadedThreadPosts),
      renderThreadSummaryMenu: () => {
        renderThreadSummaryMenu2(document.getElementById(THREAD_SUMMARY_ID));
      },
      applyPageFilter,
      updateOriginalThreadPageMenus: updateOriginalThreadPageMenus2,
      refreshNavigation
    });
    const hasActiveThreadPostFilters = threadPostFilterController.hasActiveThreadPostFilters;
    const renderThreadSearchPanel = threadPostFilterController.renderThreadSearchPanel;
    const clearThreadPostFilters = threadPostFilterController.clearThreadPostFilters;
    const clearThreadFilters = threadPostFilterController.clearThreadFilters;
    const applyThreadPostFilters = threadPostFilterController.applyThreadPostFilters;
    const enhanceAuthorFilterButton2 = threadPostFilterController.enhanceAuthorFilterButton;
    const threadGraphViewController = createThreadGraphViewController({
      getThreadGraph: () => threadGraph,
      getLoadedThreadPosts: () => loadedThreadPosts,
      getActiveGraphView: () => activeGraphView,
      setActiveGraphViewState: (view) => {
        activeGraphView = view;
      },
      getPendingGraphView: () => pendingGraphView,
      setPendingGraphView: (view) => {
        pendingGraphView = view;
      },
      getActivePageFilter: () => activePageFilter,
      setActivePageFilter: (pageNumber) => {
        activePageFilter = pageNumber;
      },
      setActiveAuthorFilters: (authors) => {
        activeAuthorFilters = authors;
      },
      setActiveThreadSearchQuery: (query2) => {
        activeThreadSearchQuery = query2;
      },
      hasActiveThreadPostFilters,
      syncThreadStateUrl,
      updateThreadPageUrl,
      updateOriginalThreadPageMenus: updateOriginalThreadPageMenus2,
      renderThreadPosts: () => renderThreadPosts(loadedThreadPosts),
      renderThreadSummaryMenu: () => {
        renderThreadSummaryMenu2(document.getElementById(THREAD_SUMMARY_ID));
      },
      selectPostById: (postId, options) => selectPostById(postId, options)
    });
    const setActiveGraphView = threadGraphViewController.setActiveGraphView;
    const activatePendingGraphView = threadGraphViewController.activatePendingGraphView;
    const applyThreadUrlState = threadGraphViewController.applyThreadUrlState;
    const installThreadHistoryNavigation = threadGraphViewController.installThreadHistoryNavigation;
    const getThreadViewPosts2 = threadGraphViewController.getThreadViewPosts;
    const getReplyIndentDepth2 = threadGraphViewController.getReplyIndentDepth;
    const threadPostRenderer = createThreadPostRenderer({
      compactModeEnabled,
      getPostsElement,
      getActiveGraphView: () => activeGraphView,
      getActivePageFilter: () => activePageFilter,
      hasActiveThreadPostFilters: () => hasActiveThreadPostFilters(),
      getPendingInitialHashPostId: () => pendingInitialHashPostId,
      clearPendingInitialHashPostId: () => {
        pendingInitialHashPostId = null;
      },
      getSelectedNavigationItem,
      getThreadViewPosts: getThreadViewPosts2,
      getReplyIndentDepth: getReplyIndentDepth2,
      applyThreadPostFilters,
      applyPageFilter,
      updateOriginalThreadPageMenus: updateOriginalThreadPageMenus2,
      renderThreadSearchPanel,
      refreshNavigation,
      selectNavigationElement,
      enhanceAuthorFilterButton: enhanceAuthorFilterButton2,
      setActiveGraphView,
      jumpToLoadedPost
    });
    const renderThreadPosts = threadPostRenderer.renderThreadPosts;
    const selectPostById = threadPostRenderer.selectPostById;
    function hydrateThreadPosts(posts) {
      applyReplyCounts(posts);
      applyOriginalPosterFlags(posts);
      loadedThreadPosts = posts.slice();
      threadGraph = buildThreadGraph(loadedThreadPosts);
      activatePendingGraphView();
    }
    async function enhanceThreadPage2() {
      await enhanceThreadPage({
        prepareThreadPage,
        ensureThreadSummary: ensureThreadSummary2,
        getThreadPages,
        getPendingInitialHashPostId: () => pendingInitialHashPostId,
        setThreadPages: (pages) => {
          threadPages = pages;
        },
        setLoadedThreadPosts: (posts) => {
          loadedThreadPosts = posts;
        },
        setLoadedThreadPageNumbers: (pageNumbers) => {
          loadedThreadPageNumbers = pageNumbers;
        },
        setThreadGraphEmpty: () => {
          threadGraph = createEmptyThreadGraph();
        },
        setActiveGraphView: (view) => {
          activeGraphView = view;
        },
        setPendingGraphView: (view) => {
          pendingGraphView = view;
        },
        setActivePageFilter: (pageNumber) => {
          activePageFilter = pageNumber;
        },
        setActiveAuthorFilters: (authors) => {
          activeAuthorFilters = authors;
        },
        setActiveThreadSearchQuery: (query2) => {
          activeThreadSearchQuery = query2;
        },
        setThreadLoadState: (state) => {
          threadLoadState = state;
        },
        getThreadLoadState: () => threadLoadState,
        hydrateThreadPosts,
        renderThreadPosts: () => renderThreadPosts(loadedThreadPosts),
        renderThreadSummaryMenu: renderThreadSummaryMenu2,
        renderThreadSearchPanel: () => renderThreadSearchPanel(),
        updateThreadPageUrl,
        syncThreadStateUrl
      });
    }
    async function initialize() {
      if (!isThreadPage()) {
        await cleanupThreadCache();
        return;
      }
      installKeyboardNavigation();
      prepareThreadPage();
      renderShortcutHelpButton2();
      await enhanceThreadPage2();
      installThreadPageNavigation();
      installThreadHistoryNavigation();
    }
    return {
      init: initialize,
      handleNavigationKeyDown: threadPageKeyboard.handleNavigationKeyDown,
      refreshNavigation,
      updateSummaryMenu: () => {
        renderThreadSummaryMenu2(document.getElementById(THREAD_SUMMARY_ID));
      }
    };
  }

  // src/app/core/runForocochesPremiumCore.ts
  function createDocumentReadyPromise() {
    if (document.readyState === "loading") {
      return new Promise((resolve) => {
        document.addEventListener("DOMContentLoaded", () => resolve(), { once: true });
      });
    }
    return Promise.resolve();
  }
  async function runForocochesPremium() {
    const scriptWindow = window;
    if (scriptWindow[INSTANCE_KEY] === SCRIPT_INSTANCE_VERSION) {
      return;
    }
    scriptWindow[INSTANCE_KEY] = SCRIPT_INSTANCE_VERSION;
    await createDocumentReadyPromise();
    const forumController = createForumPageController();
    const threadController = createThreadPageController();
    await Promise.all([
      forumController.init(),
      threadController.init()
    ]);
    if (!isForumDisplayPage() && !isThreadPage()) {
      return;
    }
    if (isForumDisplayPage()) {
      forumController.renderForumControlsRow();
      forumController.refreshNavigation({ reset: true });
    }
    if (isThreadPage()) {
      threadController.refreshNavigation({ reset: true });
      threadController.updateSummaryMenu();
    }
  }
  // src/index.ts
  runForocochesPremium();
})();