EhPeek

A touch-optimized E-H/ExH viewer

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         EhPeek
// @version      260721.1622
// @description  A touch-optimized E-H/ExH viewer
// @icon         https://raw.githubusercontent.com/yamipot/ehpeek/master/icon.svg
// @icon64       https://raw.githubusercontent.com/yamipot/ehpeek/master/icon.svg
// @license      MIT
// @namespace    https://github.com/yamipot/ehpeek
// @homepage     https://github.com/yamipot/ehpeek
// @match        *://exhentai.org/*
// @match        *://exhentai55ld2wyap5juskbm67czulomrouspdacjamjeloj7ugjbsad.onion/*
// @match        *://e-hentai.org/*
// @match        *://*.exhentai.org/*
// @match        *://*.exhentai55ld2wyap5juskbm67czulomrouspdacjamjeloj7ugjbsad.onion/*
// @match        *://*.e-hentai.org/*
// @match        *://*.hath.network/*
// @exclude      *://forums.e-hentai.org/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_registerMenuCommand
// @grant        GM_download
// @run-at       document-start
// ==/UserScript==

"use strict";
(() => {
  var __defProp = Object.defineProperty;
  var __typeError = (msg) => {
    throw TypeError(msg);
  };
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value;
  var __export = (target, all) => {
    for (var name in all)
      __defProp(target, name, { get: all[name], enumerable: !0 });
  };
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value), __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)), __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value), __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);

  // node_modules/.pnpm/[email protected]/node_modules/solid-js/dist/solid.js
  var sharedConfig = {
    context: void 0,
    registry: void 0,
    effects: void 0,
    done: !1,
    getContextId() {
      return getContextId(this.context.count);
    },
    getNextContextId() {
      return getContextId(this.context.count++);
    }
  };
  function getContextId(count) {
    let num = String(count), len = num.length - 1;
    return sharedConfig.context.id + (len ? String.fromCharCode(96 + len) : "") + num;
  }
  function setHydrateContext(context) {
    sharedConfig.context = context;
  }
  function nextHydrateContext() {
    return {
      ...sharedConfig.context,
      id: sharedConfig.getNextContextId(),
      count: 0
    };
  }
  var IS_DEV = !1, equalFn = (a, b) => a === b, $PROXY = /* @__PURE__ */ Symbol("solid-proxy"), SUPPORTS_PROXY = typeof Proxy == "function", $TRACK = /* @__PURE__ */ Symbol("solid-track");
  var signalOptions = {
    equals: equalFn
  }, ERROR = null, runEffects = runQueue, STALE = 1, PENDING = 2, UNOWNED = {
    owned: null,
    cleanups: null,
    context: null,
    owner: null
  };
  var Owner = null, Transition = null, Scheduler = null, ExternalSourceConfig = null, Listener = null, Updates = null, Effects = null, ExecCount = 0;
  function createRoot(fn, detachedOwner) {
    let listener = Listener, owner = Owner, unowned = fn.length === 0, current = detachedOwner === void 0 ? owner : detachedOwner, root = unowned ? UNOWNED : {
      owned: null,
      cleanups: null,
      context: current ? current.context : null,
      owner: current
    }, updateFn = unowned ? fn : () => fn(() => untrack(() => cleanNode(root)));
    Owner = root, Listener = null;
    try {
      return runUpdates(updateFn, !0);
    } finally {
      Listener = listener, Owner = owner;
    }
  }
  function createSignal(value, options) {
    options = options ? Object.assign({}, signalOptions, options) : signalOptions;
    let s = {
      value,
      observers: null,
      observerSlots: null,
      comparator: options.equals || void 0
    }, setter = (value2) => (typeof value2 == "function" && (Transition && Transition.running && Transition.sources.has(s) ? value2 = value2(s.tValue) : value2 = value2(s.value)), writeSignal(s, value2));
    return [readSignal.bind(s), setter];
  }
  function createRenderEffect(fn, value, options) {
    let c = createComputation(fn, value, !1, STALE);
    Scheduler && Transition && Transition.running ? Updates.push(c) : updateComputation(c);
  }
  function createEffect(fn, value, options) {
    runEffects = runUserEffects;
    let c = createComputation(fn, value, !1, STALE), s = SuspenseContext && useContext(SuspenseContext);
    s && (c.suspense = s), (!options || !options.render) && (c.user = !0), Effects ? Effects.push(c) : updateComputation(c);
  }
  function createMemo(fn, value, options) {
    options = options ? Object.assign({}, signalOptions, options) : signalOptions;
    let c = createComputation(fn, value, !0, 0);
    return c.observers = null, c.observerSlots = null, c.comparator = options.equals || void 0, Scheduler && Transition && Transition.running ? (c.tState = STALE, Updates.push(c)) : updateComputation(c), readSignal.bind(c);
  }
  function batch(fn) {
    return runUpdates(fn, !1);
  }
  function untrack(fn) {
    if (!ExternalSourceConfig && Listener === null) return fn();
    let listener = Listener;
    Listener = null;
    try {
      return ExternalSourceConfig ? ExternalSourceConfig.untrack(fn) : fn();
    } finally {
      Listener = listener;
    }
  }
  function onMount(fn) {
    createEffect(() => untrack(fn));
  }
  function onCleanup(fn) {
    return Owner === null || (Owner.cleanups === null ? Owner.cleanups = [fn] : Owner.cleanups.push(fn)), fn;
  }
  function getListener() {
    return Listener;
  }
  function startTransition(fn) {
    if (Transition && Transition.running)
      return fn(), Transition.done;
    let l = Listener, o = Owner;
    return Promise.resolve().then(() => {
      Listener = l, Owner = o;
      let t;
      return (Scheduler || SuspenseContext) && (t = Transition || (Transition = {
        sources: /* @__PURE__ */ new Set(),
        effects: [],
        promises: /* @__PURE__ */ new Set(),
        disposed: /* @__PURE__ */ new Set(),
        queue: /* @__PURE__ */ new Set(),
        running: !0
      }), t.done || (t.done = new Promise((res) => t.resolve = res)), t.running = !0), runUpdates(fn, !1), Listener = Owner = null, t ? t.done : void 0;
    });
  }
  var [transPending, setTransPending] = /* @__PURE__ */ createSignal(!1);
  function useContext(context) {
    let value;
    return Owner && Owner.context && (value = Owner.context[context.id]) !== void 0 ? value : context.defaultValue;
  }
  var SuspenseContext;
  function readSignal() {
    let runningTransition = Transition && Transition.running;
    if (this.sources && (runningTransition ? this.tState : this.state))
      if ((runningTransition ? this.tState : this.state) === STALE) updateComputation(this);
      else {
        let updates = Updates;
        Updates = null, runUpdates(() => lookUpstream(this), !1), Updates = updates;
      }
    if (Listener) {
      let observers = this.observers;
      if (!observers || observers[observers.length - 1] !== Listener) {
        let sSlot = observers ? observers.length : 0;
        Listener.sources ? (Listener.sources.push(this), Listener.sourceSlots.push(sSlot)) : (Listener.sources = [this], Listener.sourceSlots = [sSlot]), observers ? (observers.push(Listener), this.observerSlots.push(Listener.sources.length - 1)) : (this.observers = [Listener], this.observerSlots = [Listener.sources.length - 1]);
      }
    }
    return runningTransition && Transition.sources.has(this) ? this.tValue : this.value;
  }
  function writeSignal(node, value, isComp) {
    let current = Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value;
    if (!node.comparator || !node.comparator(current, value)) {
      if (Transition) {
        let TransitionRunning = Transition.running;
        (TransitionRunning || !isComp && Transition.sources.has(node)) && (Transition.sources.add(node), node.tValue = value), TransitionRunning || (node.value = value);
      } else node.value = value;
      node.observers && node.observers.length && runUpdates(() => {
        for (let i = 0; i < node.observers.length; i += 1) {
          let o = node.observers[i], TransitionRunning = Transition && Transition.running;
          TransitionRunning && Transition.disposed.has(o) || ((TransitionRunning ? !o.tState : !o.state) && (o.pure ? Updates.push(o) : Effects.push(o), o.observers && markDownstream(o)), TransitionRunning ? o.tState = STALE : o.state = STALE);
        }
        if (Updates.length > 1e6)
          throw Updates = [], new Error();
      }, !1);
    }
    return value;
  }
  function updateComputation(node) {
    if (!node.fn) return;
    cleanNode(node);
    let time = ExecCount;
    runComputation(node, Transition && Transition.running && Transition.sources.has(node) ? node.tValue : node.value, time), Transition && !Transition.running && Transition.sources.has(node) && queueMicrotask(() => {
      runUpdates(() => {
        Transition && (Transition.running = !0), Listener = Owner = node, runComputation(node, node.tValue, time), Listener = Owner = null;
      }, !1);
    });
  }
  function runComputation(node, value, time) {
    let nextValue, owner = Owner, listener = Listener;
    Listener = Owner = node;
    try {
      nextValue = node.fn(value);
    } catch (err) {
      return node.pure && (Transition && Transition.running ? (node.tState = STALE, node.tOwned && node.tOwned.forEach(cleanNode), node.tOwned = void 0) : (node.state = STALE, node.owned && node.owned.forEach(cleanNode), node.owned = null)), node.updatedAt = time + 1, handleError(err);
    } finally {
      Listener = listener, Owner = owner;
    }
    (!node.updatedAt || node.updatedAt <= time) && (node.updatedAt != null && "observers" in node ? writeSignal(node, nextValue, !0) : Transition && Transition.running && node.pure ? (Transition.sources.has(node) || (node.value = nextValue), Transition.sources.add(node), node.tValue = nextValue) : node.value = nextValue, node.updatedAt = time);
  }
  function createComputation(fn, init, pure, state2 = STALE, options) {
    let c = {
      fn,
      state: state2,
      updatedAt: null,
      owned: null,
      sources: null,
      sourceSlots: null,
      cleanups: null,
      value: init,
      owner: Owner,
      context: Owner ? Owner.context : null,
      pure
    };
    if (Transition && Transition.running && (c.state = 0, c.tState = state2), Owner === null || Owner !== UNOWNED && (Transition && Transition.running && Owner.pure ? Owner.tOwned ? Owner.tOwned.push(c) : Owner.tOwned = [c] : Owner.owned ? Owner.owned.push(c) : Owner.owned = [c]), ExternalSourceConfig && c.fn) {
      let sourceFn = c.fn, [track, trigger] = createSignal(void 0, {
        equals: !1
      }), ordinary = ExternalSourceConfig.factory(sourceFn, trigger);
      onCleanup(() => ordinary.dispose());
      let inTransition, triggerInTransition = () => startTransition(trigger).then(() => {
        inTransition && (inTransition.dispose(), inTransition = void 0);
      });
      c.fn = (x) => (track(), Transition && Transition.running ? (inTransition || (inTransition = ExternalSourceConfig.factory(sourceFn, triggerInTransition)), inTransition.track(x)) : ordinary.track(x));
    }
    return c;
  }
  function runTop(node) {
    let runningTransition = Transition && Transition.running;
    if ((runningTransition ? node.tState : node.state) === 0) return;
    if ((runningTransition ? node.tState : node.state) === PENDING) return lookUpstream(node);
    if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node);
    let ancestors = [node];
    for (; (node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount); ) {
      if (runningTransition && Transition.disposed.has(node)) return;
      (runningTransition ? node.tState : node.state) && ancestors.push(node);
    }
    for (let i = ancestors.length - 1; i >= 0; i--) {
      if (node = ancestors[i], runningTransition) {
        let top = node, prev = ancestors[i + 1];
        for (; (top = top.owner) && top !== prev; )
          if (Transition.disposed.has(top)) return;
      }
      if ((runningTransition ? node.tState : node.state) === STALE)
        updateComputation(node);
      else if ((runningTransition ? node.tState : node.state) === PENDING) {
        let updates = Updates;
        Updates = null, runUpdates(() => lookUpstream(node, ancestors[0]), !1), Updates = updates;
      }
    }
  }
  function runUpdates(fn, init) {
    if (Updates) return fn();
    let wait = !1;
    init || (Updates = []), Effects ? wait = !0 : Effects = [], ExecCount++;
    try {
      let res = fn();
      return completeUpdates(wait), res;
    } catch (err) {
      wait || (Effects = null), Updates = null, handleError(err);
    }
  }
  function completeUpdates(wait) {
    if (Updates && (Scheduler && Transition && Transition.running ? scheduleQueue(Updates) : runQueue(Updates), Updates = null), wait) return;
    let res;
    if (Transition) {
      if (!Transition.promises.size && !Transition.queue.size) {
        let sources = Transition.sources, disposed = Transition.disposed;
        Effects.push.apply(Effects, Transition.effects), res = Transition.resolve;
        for (let e2 of Effects)
          "tState" in e2 && (e2.state = e2.tState), delete e2.tState;
        Transition = null, runUpdates(() => {
          for (let d of disposed) cleanNode(d);
          for (let v of sources) {
            if (v.value = v.tValue, v.owned)
              for (let i = 0, len = v.owned.length; i < len; i++) cleanNode(v.owned[i]);
            v.tOwned && (v.owned = v.tOwned), delete v.tValue, delete v.tOwned, v.tState = 0;
          }
          setTransPending(!1);
        }, !1);
      } else if (Transition.running) {
        Transition.running = !1, Transition.effects.push.apply(Transition.effects, Effects), Effects = null, setTransPending(!0);
        return;
      }
    }
    let e = Effects;
    Effects = null, e.length && runUpdates(() => runEffects(e), !1), res && res();
  }
  function runQueue(queue) {
    for (let i = 0; i < queue.length; i++) runTop(queue[i]);
  }
  function scheduleQueue(queue) {
    for (let i = 0; i < queue.length; i++) {
      let item = queue[i], tasks = Transition.queue;
      tasks.has(item) || (tasks.add(item), Scheduler(() => {
        tasks.delete(item), runUpdates(() => {
          Transition.running = !0, runTop(item);
        }, !1), Transition && (Transition.running = !1);
      }));
    }
  }
  function runUserEffects(queue) {
    let i, userLength = 0;
    for (i = 0; i < queue.length; i++) {
      let e = queue[i];
      e.user ? queue[userLength++] = e : runTop(e);
    }
    if (sharedConfig.context) {
      if (sharedConfig.count) {
        sharedConfig.effects || (sharedConfig.effects = []), sharedConfig.effects.push(...queue.slice(0, userLength));
        return;
      }
      setHydrateContext();
    }
    for (sharedConfig.effects && (sharedConfig.done || !sharedConfig.count) && (queue = [...sharedConfig.effects, ...queue], userLength += sharedConfig.effects.length, delete sharedConfig.effects), i = 0; i < userLength; i++) runTop(queue[i]);
  }
  function lookUpstream(node, ignore) {
    let runningTransition = Transition && Transition.running;
    runningTransition ? node.tState = 0 : node.state = 0;
    for (let i = 0; i < node.sources.length; i += 1) {
      let source = node.sources[i];
      if (source.sources) {
        let state2 = runningTransition ? source.tState : source.state;
        state2 === STALE ? source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount) && runTop(source) : state2 === PENDING && lookUpstream(source, ignore);
      }
    }
  }
  function markDownstream(node) {
    let runningTransition = Transition && Transition.running;
    for (let i = 0; i < node.observers.length; i += 1) {
      let o = node.observers[i];
      (runningTransition ? !o.tState : !o.state) && (runningTransition ? o.tState = PENDING : o.state = PENDING, o.pure ? Updates.push(o) : Effects.push(o), o.observers && markDownstream(o));
    }
  }
  function cleanNode(node) {
    let i;
    if (node.sources)
      for (; node.sources.length; ) {
        let source = node.sources.pop(), index = node.sourceSlots.pop(), obs = source.observers;
        if (obs && obs.length) {
          let n = obs.pop(), s = source.observerSlots.pop();
          index < obs.length && (n.sourceSlots[s] = index, obs[index] = n, source.observerSlots[index] = s);
        }
      }
    if (node.tOwned) {
      for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]);
      delete node.tOwned;
    }
    if (Transition && Transition.running && node.pure)
      reset(node, !0);
    else if (node.owned) {
      for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]);
      node.owned = null;
    }
    if (node.cleanups) {
      for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i]();
      node.cleanups = null;
    }
    Transition && Transition.running ? node.tState = 0 : node.state = 0;
  }
  function reset(node, top) {
    if (top || (node.tState = 0, Transition.disposed.add(node)), node.owned)
      for (let i = 0; i < node.owned.length; i++) reset(node.owned[i]);
  }
  function castError(err) {
    return err instanceof Error ? err : new Error(typeof err == "string" ? err : "Unknown error", {
      cause: err
    });
  }
  function runErrors(err, fns, owner) {
    try {
      for (let f of fns) f(err);
    } catch (e) {
      handleError(e, owner && owner.owner || null);
    }
  }
  function handleError(err, owner = Owner) {
    let fns = ERROR && owner && owner.context && owner.context[ERROR], error = castError(err);
    if (!fns) throw error;
    Effects ? Effects.push({
      fn() {
        runErrors(error, fns, owner);
      },
      state: STALE
    }) : runErrors(error, fns, owner);
  }
  var FALLBACK = /* @__PURE__ */ Symbol("fallback");
  function dispose(d) {
    for (let i = 0; i < d.length; i++) d[i]();
  }
  function mapArray(list, mapFn, options = {}) {
    let items = [], mapped = [], disposers = [], len = 0, indexes = mapFn.length > 1 ? [] : null;
    return onCleanup(() => dispose(disposers)), () => {
      let newItems = list() || [], newLen = newItems.length, i, j;
      return newItems[$TRACK], untrack(() => {
        let newIndices, newIndicesNext, temp, tempdisposers, tempIndexes, start, end, newEnd, item;
        if (newLen === 0)
          len !== 0 && (dispose(disposers), disposers = [], items = [], mapped = [], len = 0, indexes && (indexes = [])), options.fallback && (items = [FALLBACK], mapped[0] = createRoot((disposer) => (disposers[0] = disposer, options.fallback())), len = 1);
        else if (len === 0) {
          for (mapped = new Array(newLen), j = 0; j < newLen; j++)
            items[j] = newItems[j], mapped[j] = createRoot(mapper);
          len = newLen;
        } else {
          for (temp = new Array(newLen), tempdisposers = new Array(newLen), indexes && (tempIndexes = new Array(newLen)), start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++) ;
          for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && items[end] === newItems[newEnd]; end--, newEnd--)
            temp[newEnd] = mapped[end], tempdisposers[newEnd] = disposers[end], indexes && (tempIndexes[newEnd] = indexes[end]);
          for (newIndices = /* @__PURE__ */ new Map(), newIndicesNext = new Array(newEnd + 1), j = newEnd; j >= start; j--)
            item = newItems[j], i = newIndices.get(item), newIndicesNext[j] = i === void 0 ? -1 : i, newIndices.set(item, j);
          for (i = start; i <= end; i++)
            item = items[i], j = newIndices.get(item), j !== void 0 && j !== -1 ? (temp[j] = mapped[i], tempdisposers[j] = disposers[i], indexes && (tempIndexes[j] = indexes[i]), j = newIndicesNext[j], newIndices.set(item, j)) : disposers[i]();
          for (j = start; j < newLen; j++)
            j in temp ? (mapped[j] = temp[j], disposers[j] = tempdisposers[j], indexes && (indexes[j] = tempIndexes[j], indexes[j](j))) : mapped[j] = createRoot(mapper);
          mapped = mapped.slice(0, len = newLen), items = newItems.slice(0);
        }
        return mapped;
      });
      function mapper(disposer) {
        if (disposers[j] = disposer, indexes) {
          let [s, set] = createSignal(j);
          return indexes[j] = set, mapFn(newItems[j], s);
        }
        return mapFn(newItems[j]);
      }
    };
  }
  var hydrationEnabled = !1;
  function createComponent(Comp, props) {
    if (hydrationEnabled && sharedConfig.context) {
      let c = sharedConfig.context;
      setHydrateContext(nextHydrateContext());
      let r = untrack(() => Comp(props || {}));
      return setHydrateContext(c), r;
    }
    return untrack(() => Comp(props || {}));
  }
  function trueFn() {
    return !0;
  }
  var propTraps = {
    get(_, property, receiver) {
      return property === $PROXY ? receiver : _.get(property);
    },
    has(_, property) {
      return property === $PROXY ? !0 : _.has(property);
    },
    set: trueFn,
    deleteProperty: trueFn,
    getOwnPropertyDescriptor(_, property) {
      return {
        configurable: !0,
        enumerable: !0,
        get() {
          return _.get(property);
        },
        set: trueFn,
        deleteProperty: trueFn
      };
    },
    ownKeys(_) {
      return _.keys();
    }
  };
  function splitProps(props, ...keys) {
    let len = keys.length;
    if (SUPPORTS_PROXY && $PROXY in props) {
      let blocked = len > 1 ? keys.flat() : keys[0], res = keys.map((k) => new Proxy({
        get(property) {
          return k.includes(property) ? props[property] : void 0;
        },
        has(property) {
          return k.includes(property) && property in props;
        },
        keys() {
          return k.filter((property) => property in props);
        }
      }, propTraps));
      return res.push(new Proxy({
        get(property) {
          return blocked.includes(property) ? void 0 : props[property];
        },
        has(property) {
          return blocked.includes(property) ? !1 : property in props;
        },
        keys() {
          return Object.keys(props).filter((k) => !blocked.includes(k));
        }
      }, propTraps)), res;
    }
    let objects = [];
    for (let i = 0; i <= len; i++)
      objects[i] = {};
    for (let propName of Object.getOwnPropertyNames(props)) {
      let keyIndex = len;
      for (let i = 0; i < keys.length; i++)
        if (keys[i].includes(propName)) {
          keyIndex = i;
          break;
        }
      let desc = Object.getOwnPropertyDescriptor(props, propName);
      !desc.get && !desc.set && desc.enumerable && desc.writable && desc.configurable ? objects[keyIndex][propName] = desc.value : Object.defineProperty(objects[keyIndex], propName, desc);
    }
    return objects;
  }
  var narrowedError = (name) => `Stale read from <${name}>.`;
  function For(props) {
    let fallback = "fallback" in props && {
      fallback: () => props.fallback
    };
    return createMemo(mapArray(() => props.each, props.children, fallback || void 0));
  }
  function Show(props) {
    let keyed = props.keyed, conditionValue = createMemo(() => props.when, void 0, void 0), condition = keyed ? conditionValue : createMemo(conditionValue, void 0, {
      equals: (a, b) => !a == !b
    });
    return createMemo(() => {
      let c = condition();
      if (c) {
        let child = props.children;
        return typeof child == "function" && child.length > 0 ? untrack(() => child(keyed ? c : () => {
          if (!untrack(condition)) throw narrowedError("Show");
          return conditionValue();
        })) : child;
      }
      return props.fallback;
    }, void 0, void 0);
  }

  // node_modules/.pnpm/[email protected]/node_modules/solid-js/web/dist/web.js
  var booleans = [
    "allowfullscreen",
    "async",
    "alpha",
    "autofocus",
    "autoplay",
    "checked",
    "controls",
    "default",
    "disabled",
    "formnovalidate",
    "hidden",
    "indeterminate",
    "inert",
    "ismap",
    "loop",
    "multiple",
    "muted",
    "nomodule",
    "novalidate",
    "open",
    "playsinline",
    "readonly",
    "required",
    "reversed",
    "seamless",
    "selected",
    "adauctionheaders",
    "browsingtopics",
    "credentialless",
    "defaultchecked",
    "defaultmuted",
    "defaultselected",
    "defer",
    "disablepictureinpicture",
    "disableremoteplayback",
    "preservespitch",
    "shadowrootclonable",
    "shadowrootcustomelementregistry",
    "shadowrootdelegatesfocus",
    "shadowrootserializable",
    "sharedstoragewritable"
  ], Properties = /* @__PURE__ */ new Set([
    "className",
    "value",
    "readOnly",
    "noValidate",
    "formNoValidate",
    "isMap",
    "noModule",
    "playsInline",
    "adAuctionHeaders",
    "allowFullscreen",
    "browsingTopics",
    "defaultChecked",
    "defaultMuted",
    "defaultSelected",
    "disablePictureInPicture",
    "disableRemotePlayback",
    "preservesPitch",
    "shadowRootClonable",
    "shadowRootCustomElementRegistry",
    "shadowRootDelegatesFocus",
    "shadowRootSerializable",
    "sharedStorageWritable",
    ...booleans
  ]), ChildProperties = /* @__PURE__ */ new Set(["innerHTML", "textContent", "innerText", "children"]), Aliases = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(null), {
    className: "class",
    htmlFor: "for"
  }), PropAliases = /* @__PURE__ */ Object.assign(/* @__PURE__ */ Object.create(null), {
    class: "className",
    novalidate: {
      $: "noValidate",
      FORM: 1
    },
    formnovalidate: {
      $: "formNoValidate",
      BUTTON: 1,
      INPUT: 1
    },
    ismap: {
      $: "isMap",
      IMG: 1
    },
    nomodule: {
      $: "noModule",
      SCRIPT: 1
    },
    playsinline: {
      $: "playsInline",
      VIDEO: 1
    },
    readonly: {
      $: "readOnly",
      INPUT: 1,
      TEXTAREA: 1
    },
    adauctionheaders: {
      $: "adAuctionHeaders",
      IFRAME: 1
    },
    allowfullscreen: {
      $: "allowFullscreen",
      IFRAME: 1
    },
    browsingtopics: {
      $: "browsingTopics",
      IMG: 1
    },
    defaultchecked: {
      $: "defaultChecked",
      INPUT: 1
    },
    defaultmuted: {
      $: "defaultMuted",
      AUDIO: 1,
      VIDEO: 1
    },
    defaultselected: {
      $: "defaultSelected",
      OPTION: 1
    },
    disablepictureinpicture: {
      $: "disablePictureInPicture",
      VIDEO: 1
    },
    disableremoteplayback: {
      $: "disableRemotePlayback",
      AUDIO: 1,
      VIDEO: 1
    },
    preservespitch: {
      $: "preservesPitch",
      AUDIO: 1,
      VIDEO: 1
    },
    shadowrootclonable: {
      $: "shadowRootClonable",
      TEMPLATE: 1
    },
    shadowrootdelegatesfocus: {
      $: "shadowRootDelegatesFocus",
      TEMPLATE: 1
    },
    shadowrootserializable: {
      $: "shadowRootSerializable",
      TEMPLATE: 1
    },
    sharedstoragewritable: {
      $: "sharedStorageWritable",
      IFRAME: 1,
      IMG: 1
    }
  });
  function getPropAlias(prop, tagName) {
    let a = PropAliases[prop];
    return typeof a == "object" ? a[tagName] ? a.$ : void 0 : a;
  }
  var DelegatedEvents = /* @__PURE__ */ new Set(["beforeinput", "click", "dblclick", "contextmenu", "focusin", "focusout", "input", "keydown", "keyup", "mousedown", "mousemove", "mouseout", "mouseover", "mouseup", "pointerdown", "pointermove", "pointerout", "pointerover", "pointerup", "touchend", "touchmove", "touchstart"]), SVGElements = /* @__PURE__ */ new Set([
    "altGlyph",
    "altGlyphDef",
    "altGlyphItem",
    "animate",
    "animateColor",
    "animateMotion",
    "animateTransform",
    "circle",
    "clipPath",
    "color-profile",
    "cursor",
    "defs",
    "desc",
    "ellipse",
    "feBlend",
    "feColorMatrix",
    "feComponentTransfer",
    "feComposite",
    "feConvolveMatrix",
    "feDiffuseLighting",
    "feDisplacementMap",
    "feDistantLight",
    "feDropShadow",
    "feFlood",
    "feFuncA",
    "feFuncB",
    "feFuncG",
    "feFuncR",
    "feGaussianBlur",
    "feImage",
    "feMerge",
    "feMergeNode",
    "feMorphology",
    "feOffset",
    "fePointLight",
    "feSpecularLighting",
    "feSpotLight",
    "feTile",
    "feTurbulence",
    "filter",
    "font",
    "font-face",
    "font-face-format",
    "font-face-name",
    "font-face-src",
    "font-face-uri",
    "foreignObject",
    "g",
    "glyph",
    "glyphRef",
    "hkern",
    "image",
    "line",
    "linearGradient",
    "marker",
    "mask",
    "metadata",
    "missing-glyph",
    "mpath",
    "path",
    "pattern",
    "polygon",
    "polyline",
    "radialGradient",
    "rect",
    "set",
    "stop",
    "svg",
    "switch",
    "symbol",
    "text",
    "textPath",
    "tref",
    "tspan",
    "use",
    "view",
    "vkern"
  ]), SVGNamespace = {
    xlink: "http://www.w3.org/1999/xlink",
    xml: "http://www.w3.org/XML/1998/namespace"
  };
  var memo = (fn) => createMemo(() => fn());
  function reconcileArrays(parentNode, a, b) {
    let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = a[aEnd - 1].nextSibling, map = null;
    for (; aStart < aEnd || bStart < bEnd; ) {
      if (a[aStart] === b[bStart]) {
        aStart++, bStart++;
        continue;
      }
      for (; a[aEnd - 1] === b[bEnd - 1]; )
        aEnd--, bEnd--;
      if (aEnd === aStart) {
        let node = bEnd < bLength ? bStart ? b[bStart - 1].nextSibling : b[bEnd - bStart] : after;
        for (; bStart < bEnd; ) parentNode.insertBefore(b[bStart++], node);
      } else if (bEnd === bStart)
        for (; aStart < aEnd; )
          (!map || !map.has(a[aStart])) && a[aStart].remove(), aStart++;
      else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
        let node = a[--aEnd].nextSibling;
        parentNode.insertBefore(b[bStart++], a[aStart++].nextSibling), parentNode.insertBefore(b[--bEnd], node), a[aEnd] = b[bEnd];
      } else {
        if (!map) {
          map = /* @__PURE__ */ new Map();
          let i = bStart;
          for (; i < bEnd; ) map.set(b[i], i++);
        }
        let index = map.get(a[aStart]);
        if (index != null)
          if (bStart < index && index < bEnd) {
            let i = aStart, sequence = 1, t;
            for (; ++i < aEnd && i < bEnd && !((t = map.get(a[i])) == null || t !== index + sequence); )
              sequence++;
            if (sequence > index - bStart) {
              let node = a[aStart];
              for (; bStart < index; ) parentNode.insertBefore(b[bStart++], node);
            } else parentNode.replaceChild(b[bStart++], a[aStart++]);
          } else aStart++;
        else a[aStart++].remove();
      }
    }
  }
  var $$EVENTS = "_$DX_DELEGATE";
  function render(code, element, init, options = {}) {
    let disposer;
    return createRoot((dispose2) => {
      disposer = dispose2, element === document ? code() : insert(element, code(), element.firstChild ? null : void 0, init);
    }, options.owner), () => {
      disposer(), element.textContent = "";
    };
  }
  function template(html, isImportNode, isSVG, isMathML) {
    let node, create = () => {
      let t = isMathML ? document.createElementNS("http://www.w3.org/1998/Math/MathML", "template") : document.createElement("template");
      return t.innerHTML = html, isSVG ? t.content.firstChild.firstChild : isMathML ? t.firstChild : t.content.firstChild;
    }, fn = isImportNode ? () => untrack(() => document.importNode(node || (node = create()), !0)) : () => (node || (node = create())).cloneNode(!0);
    return fn.cloneNode = fn, fn;
  }
  function delegateEvents(eventNames, document2 = window.document) {
    let e = document2[$$EVENTS] || (document2[$$EVENTS] = /* @__PURE__ */ new Set());
    for (let i = 0, l = eventNames.length; i < l; i++) {
      let name = eventNames[i];
      e.has(name) || (e.add(name), document2.addEventListener(name, eventHandler));
    }
  }
  function setAttribute(node, name, value) {
    isHydrating(node) || (value == null ? node.removeAttribute(name) : node.setAttribute(name, value));
  }
  function setAttributeNS(node, namespace, name, value) {
    isHydrating(node) || (value == null ? node.removeAttributeNS(namespace, name) : node.setAttributeNS(namespace, name, value));
  }
  function setBoolAttribute(node, name, value) {
    isHydrating(node) || (value ? node.setAttribute(name, "") : node.removeAttribute(name));
  }
  function className(node, value) {
    isHydrating(node) || (value == null ? node.removeAttribute("class") : node.className = value);
  }
  function addEventListener(node, name, handler, delegate) {
    if (delegate)
      Array.isArray(handler) ? (node[`$$${name}`] = handler[0], node[`$$${name}Data`] = handler[1]) : node[`$$${name}`] = handler;
    else if (Array.isArray(handler)) {
      let handlerFn = handler[0];
      node.addEventListener(name, handler[0] = (e) => handlerFn.call(node, handler[1], e));
    } else node.addEventListener(name, handler, typeof handler != "function" && handler);
  }
  function classList(node, value, prev = {}) {
    let classKeys = Object.keys(value || {}), prevKeys = Object.keys(prev), i, len;
    for (i = 0, len = prevKeys.length; i < len; i++) {
      let key = prevKeys[i];
      !key || key === "undefined" || value[key] || (toggleClassKey(node, key, !1), delete prev[key]);
    }
    for (i = 0, len = classKeys.length; i < len; i++) {
      let key = classKeys[i], classValue = !!value[key];
      !key || key === "undefined" || prev[key] === classValue || !classValue || (toggleClassKey(node, key, !0), prev[key] = classValue);
    }
    return prev;
  }
  function style(node, value, prev) {
    if (!value) return prev ? setAttribute(node, "style") : value;
    let nodeStyle = node.style;
    if (typeof value == "string") return nodeStyle.cssText = value;
    typeof prev == "string" && (nodeStyle.cssText = prev = void 0), prev || (prev = {}), value || (value = {});
    let v, s;
    for (s in prev)
      value[s] == null && nodeStyle.removeProperty(s), delete prev[s];
    for (s in value)
      v = value[s], v !== prev[s] && (nodeStyle.setProperty(s, v), prev[s] = v);
    return prev;
  }
  function setStyleProperty(node, name, value) {
    value != null ? node.style.setProperty(name, value) : node.style.removeProperty(name);
  }
  function spread(node, props = {}, isSVG, skipChildren) {
    let prevProps = {};
    return skipChildren || createRenderEffect(() => prevProps.children = insertExpression(node, props.children, prevProps.children)), createRenderEffect(() => typeof props.ref == "function" && use(props.ref, node)), createRenderEffect(() => assign(node, props, isSVG, !0, prevProps, !0)), prevProps;
  }
  function use(fn, element, arg) {
    return untrack(() => fn(element, arg));
  }
  function insert(parent, accessor, marker, initial) {
    if (marker !== void 0 && !initial && (initial = []), typeof accessor != "function") return insertExpression(parent, accessor, initial, marker);
    createRenderEffect((current) => insertExpression(parent, accessor(), current, marker), initial);
  }
  function assign(node, props, isSVG, skipChildren, prevProps = {}, skipRef = !1) {
    props || (props = {});
    for (let prop in prevProps)
      if (!(prop in props)) {
        if (prop === "children") continue;
        prevProps[prop] = assignProp(node, prop, null, prevProps[prop], isSVG, skipRef, props);
      }
    for (let prop in props) {
      if (prop === "children") {
        skipChildren || insertExpression(node, props.children);
        continue;
      }
      let value = props[prop];
      prevProps[prop] = assignProp(node, prop, value, prevProps[prop], isSVG, skipRef, props);
    }
  }
  function getNextElement(template2) {
    let node, key;
    return !isHydrating() || !(node = sharedConfig.registry.get(key = getHydrationKey())) ? template2() : (sharedConfig.completed && sharedConfig.completed.add(node), sharedConfig.registry.delete(key), node);
  }
  function isHydrating(node) {
    return !!sharedConfig.context && !sharedConfig.done && (!node || node.isConnected);
  }
  function toPropertyName(name) {
    return name.toLowerCase().replace(/-([a-z])/g, (_, w) => w.toUpperCase());
  }
  function toggleClassKey(node, key, value) {
    let classNames = key.trim().split(/\s+/);
    for (let i = 0, nameLen = classNames.length; i < nameLen; i++) node.classList.toggle(classNames[i], value);
  }
  function assignProp(node, prop, value, prev, isSVG, skipRef, props) {
    let isCE, isProp, isChildProp, propAlias, forceProp;
    if (prop === "style") return style(node, value, prev);
    if (prop === "classList") return classList(node, value, prev);
    if (value === prev) return prev;
    if (prop === "ref")
      skipRef || value(node);
    else if (prop.slice(0, 3) === "on:") {
      let e = prop.slice(3);
      prev && node.removeEventListener(e, prev, typeof prev != "function" && prev), value && node.addEventListener(e, value, typeof value != "function" && value);
    } else if (prop.slice(0, 10) === "oncapture:") {
      let e = prop.slice(10);
      prev && node.removeEventListener(e, prev, !0), value && node.addEventListener(e, value, !0);
    } else if (prop.slice(0, 2) === "on") {
      let name = prop.slice(2).toLowerCase(), delegate = DelegatedEvents.has(name);
      if (!delegate && prev) {
        let h = Array.isArray(prev) ? prev[0] : prev;
        node.removeEventListener(name, h);
      }
      (delegate || value) && (addEventListener(node, name, value, delegate), delegate && delegateEvents([name]));
    } else if (prop.slice(0, 5) === "attr:")
      setAttribute(node, prop.slice(5), value);
    else if (prop.slice(0, 5) === "bool:")
      setBoolAttribute(node, prop.slice(5), value);
    else if ((forceProp = prop.slice(0, 5) === "prop:") || (isChildProp = ChildProperties.has(prop)) || !isSVG && ((propAlias = getPropAlias(prop, node.tagName)) || (isProp = Properties.has(prop))) || (isCE = node.nodeName.includes("-") || "is" in props)) {
      if (forceProp)
        prop = prop.slice(5), isProp = !0;
      else if (isHydrating(node)) return value;
      prop === "class" || prop === "className" ? className(node, value) : isCE && !isProp && !isChildProp ? node[toPropertyName(prop)] = value : node[propAlias || prop] = value;
    } else {
      let ns = isSVG && prop.indexOf(":") > -1 && SVGNamespace[prop.split(":")[0]];
      ns ? setAttributeNS(node, ns, prop, value) : setAttribute(node, Aliases[prop] || prop, value);
    }
    return value;
  }
  function eventHandler(e) {
    if (sharedConfig.registry && sharedConfig.events && sharedConfig.events.find(([el, ev]) => ev === e))
      return;
    let node = e.target, key = `$$${e.type}`, oriTarget = e.target, oriCurrentTarget = e.currentTarget, retarget = (value) => Object.defineProperty(e, "target", {
      configurable: !0,
      value
    }), handleNode = () => {
      let handler = node[key];
      if (handler && !node.disabled) {
        let data = node[`${key}Data`];
        if (data !== void 0 ? handler.call(node, data, e) : handler.call(node, e), e.cancelBubble) return;
      }
      return node.host && typeof node.host != "string" && !node.host._$host && node.contains(e.target) && retarget(node.host), !0;
    }, walkUpTree = () => {
      for (; handleNode() && (node = node._$host || node.parentNode || node.host); ) ;
    };
    if (Object.defineProperty(e, "currentTarget", {
      configurable: !0,
      get() {
        return node || document;
      }
    }), sharedConfig.registry && !sharedConfig.done && (sharedConfig.done = _$HY.done = !0), e.composedPath) {
      let path = e.composedPath();
      retarget(path[0]);
      for (let i = 0; i < path.length - 2 && (node = path[i], !!handleNode()); i++) {
        if (node._$host) {
          node = node._$host, walkUpTree();
          break;
        }
        if (node.parentNode === oriCurrentTarget)
          break;
      }
    } else walkUpTree();
    retarget(oriTarget);
  }
  function insertExpression(parent, value, current, marker, unwrapArray) {
    let hydrating = isHydrating(parent);
    if (hydrating) {
      !current && (current = [...parent.childNodes]);
      let cleaned = [];
      for (let i = 0; i < current.length; i++) {
        let node = current[i];
        node.nodeType === 8 && node.data.slice(0, 2) === "!$" ? node.remove() : cleaned.push(node);
      }
      current = cleaned;
    }
    for (; typeof current == "function"; ) current = current();
    if (value === current) return current;
    let t = typeof value, multi = marker !== void 0;
    if (parent = multi && current[0] && current[0].parentNode || parent, t === "string" || t === "number") {
      if (hydrating || t === "number" && (value = value.toString(), value === current))
        return current;
      if (multi) {
        let node = current[0];
        node && node.nodeType === 3 ? node.data !== value && (node.data = value) : node = document.createTextNode(value), current = cleanChildren(parent, current, marker, node);
      } else
        current !== "" && typeof current == "string" ? current = parent.firstChild.data = value : current = parent.textContent = value;
    } else if (value == null || t === "boolean") {
      if (hydrating) return current;
      current = cleanChildren(parent, current, marker);
    } else {
      if (t === "function")
        return createRenderEffect(() => {
          let v = value();
          for (; typeof v == "function"; ) v = v();
          current = insertExpression(parent, v, current, marker);
        }), () => current;
      if (Array.isArray(value)) {
        let array = [], currentArray = current && Array.isArray(current);
        if (normalizeIncomingArray(array, value, current, unwrapArray))
          return createRenderEffect(() => current = insertExpression(parent, array, current, marker, !0)), () => current;
        if (hydrating) {
          if (!array.length) return current;
          if (marker === void 0) return current = [...parent.childNodes];
          let node = array[0];
          if (node.parentNode !== parent) return current;
          let nodes = [node];
          for (; (node = node.nextSibling) !== marker; ) nodes.push(node);
          return current = nodes;
        }
        if (array.length === 0) {
          if (current = cleanChildren(parent, current, marker), multi) return current;
        } else currentArray ? current.length === 0 ? appendNodes(parent, array, marker) : reconcileArrays(parent, current, array) : (current && cleanChildren(parent), appendNodes(parent, array));
        current = array;
      } else if (value.nodeType) {
        if (hydrating && value.parentNode) return current = multi ? [value] : value;
        if (Array.isArray(current)) {
          if (multi) return current = cleanChildren(parent, current, marker, value);
          cleanChildren(parent, current, null, value);
        } else current == null || current === "" || !parent.firstChild ? parent.appendChild(value) : parent.replaceChild(value, parent.firstChild);
        current = value;
      }
    }
    return current;
  }
  function normalizeIncomingArray(normalized, array, current, unwrap2) {
    let dynamic = !1;
    for (let i = 0, len = array.length; i < len; i++) {
      let item = array[i], prev = current && current[normalized.length], t;
      if (!(item == null || item === !0 || item === !1)) if ((t = typeof item) == "object" && item.nodeType)
        normalized.push(item);
      else if (Array.isArray(item))
        dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic;
      else if (t === "function")
        if (unwrap2) {
          for (; typeof item == "function"; ) item = item();
          dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item], Array.isArray(prev) ? prev : [prev]) || dynamic;
        } else
          normalized.push(item), dynamic = !0;
      else {
        let value = String(item);
        prev && prev.nodeType === 3 && prev.data === value ? normalized.push(prev) : normalized.push(document.createTextNode(value));
      }
    }
    return dynamic;
  }
  function appendNodes(parent, array, marker = null) {
    for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker);
  }
  function cleanChildren(parent, current, marker, replacement) {
    if (marker === void 0) return parent.textContent = "";
    let node = replacement || document.createTextNode("");
    if (current.length) {
      let inserted = !1;
      for (let i = current.length - 1; i >= 0; i--) {
        let el = current[i];
        if (node !== el) {
          let isParent = el.parentNode === parent;
          !inserted && !i ? isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker) : isParent && el.remove();
        } else inserted = !0;
      }
    } else parent.insertBefore(node, marker);
    return [node];
  }
  function getHydrationKey() {
    return sharedConfig.getNextContextId();
  }
  var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
  function createElement(tagName, isSVG = !1, is = void 0) {
    return isSVG ? document.createElementNS(SVG_NAMESPACE, tagName) : document.createElement(tagName, {
      is
    });
  }
  function createDynamic(component, props) {
    let cached = createMemo(component);
    return createMemo(() => {
      let component2 = cached();
      switch (typeof component2) {
        case "function":
          return untrack(() => component2(props));
        case "string":
          let isSvg = SVGElements.has(component2), el = sharedConfig.context ? getNextElement() : createElement(component2, isSvg, untrack(() => props.is));
          return spread(el, props, isSvg), el;
      }
    });
  }
  function Dynamic(props) {
    let [, others] = splitProps(props, ["component"]);
    return createDynamic(() => props.component, others);
  }

  // src/eh/url.ts
  var EXHENTAI_HOST = "exhentai.org", EXHENTAI_ONION_HOST = "exhentai55ld2wyap5juskbm67czulomrouspdacjamjeloj7ugjbsad.onion";
  function ehSiteTheme(url = window.location.href) {
    let hostname = new URL(url, window.location.href).hostname;
    return hostname === EXHENTAI_HOST || hostname.endsWith(`.${EXHENTAI_HOST}`) || hostname === EXHENTAI_ONION_HOST || hostname.endsWith(`.${EXHENTAI_ONION_HOST}`) ? "exhentai" : "e-hentai";
  }
  function galleryIdentityFromUrl(url = window.location.href) {
    try {
      let match = new URL(url, window.location.href).pathname.match(/^\/g\/(\d+)\/([^/]+)/i), galleryId = Number(match?.[1]), token = match?.[2];
      return token && Number.isSafeInteger(galleryId) && galleryId > 0 ? { galleryId, token } : null;
    } catch {
      return null;
    }
  }
  function urlPath(url) {
    try {
      return new URL(url, window.location.href).pathname.toLowerCase();
    } catch {
      return "";
    }
  }
  function galleryTagNameFromUrl(url) {
    let encodedName = urlPath(url).match(/^\/tag\/(.+?)\/?$/i)?.[1];
    try {
      return encodedName ? decodeURIComponent(encodedName.replace(/\+/g, " ")) : null;
    } catch {
      return null;
    }
  }
  function isFullImageUrl(url) {
    return urlPath(url).includes("/fullimg");
  }
  function extractPageType(url = window.location.href) {
    try {
      let parsed = new URL(url, window.location.href), galleryMatch = parsed.pathname.match(/^\/g\/(\d+)\/([^/]+)\/?$/i);
      if (galleryMatch) {
        let galleryId = Number(galleryMatch[1]), token = galleryMatch[2];
        if (token && Number.isFinite(galleryId) && galleryId > 0)
          return {
            type: "gallery",
            url: parsed.href,
            galleryId,
            token,
            previewIndex: previewPageIndex(parsed.href),
            peekPage: peekPageFromHash(parsed.hash)
          };
      }
      let imageMatch = parsed.pathname.match(/^\/s\/[^/]+\/(\d+)-(\d+)\/?$/i);
      if (imageMatch) {
        let galleryId = Number(imageMatch[1]), pageNum = Number(imageMatch[2]);
        if (Number.isFinite(galleryId) && galleryId > 0 && Number.isFinite(pageNum) && pageNum > 0)
          return {
            type: "image",
            url: parsed.href,
            galleryId,
            pageNum
          };
      }
      return parsed.pathname === "/favorites.php" ? {
        type: "favorites",
        url: parsed.href
      } : /^\/mytags\/?$/.test(parsed.pathname) ? {
        type: "myTags",
        url: parsed.href
      } : parsed.pathname === "/" || parsed.pathname.startsWith("/tag/") || parsed.pathname.startsWith("/uploader/") || /^\/(?:popular|watched)\/?$/.test(parsed.pathname) ? {
        type: "search",
        url: parsed.href
      } : {
        type: "other",
        url: parsed.href
      };
    } catch {
      return {
        type: "other",
        url
      };
    }
  }
  function galleryPageNumber(url) {
    let page = extractPageType(url);
    return page.type === "image" ? page.pageNum : void 0;
  }
  function previewPageIndex(url = window.location.href) {
    try {
      let value = Number(new URL(url).searchParams.get("p") || "0");
      return Number.isFinite(value) && value >= 0 ? value : 0;
    } catch {
      return 0;
    }
  }
  function previewUrlForIndex(previewIndex, pageUrl = window.location.href) {
    let url = new URL(pageUrl);
    return previewIndex <= 0 ? url.searchParams.delete("p") : url.searchParams.set("p", String(previewIndex)), url.hash = "", url.href;
  }
  function previewPageIndexForGalleryPage(galleryPage, pageSize, maxPreviewIndex) {
    let previewIndex = Math.max(0, Math.floor((galleryPage - 1) / pageSize));
    return Math.min(previewIndex, maxPreviewIndex);
  }
  function peekPageFromHash(hash = window.location.hash) {
    let params = new URLSearchParams(hash.replace(/^#/, "")), page = Number(params.get("peek_page") || "");
    return Number.isFinite(page) && page > 0 ? page : null;
  }
  function updatePeekLocation(pageNumber, pageSize, maxPreviewIndex) {
    if (!pageNumber || pageNumber <= 0)
      return;
    let url = new URL(window.location.href), params = new URLSearchParams(window.location.hash.replace(/^#/, "")), nextValue = String(pageNumber), nextPreviewIndex = previewPageIndexForGalleryPage(pageNumber, pageSize, maxPreviewIndex), changed = !1;
    nextPreviewIndex === 0 ? url.searchParams.has("p") && (url.searchParams.delete("p"), changed = !0) : url.searchParams.get("p") !== String(nextPreviewIndex) && (url.searchParams.set("p", String(nextPreviewIndex)), changed = !0), params.get("peek_page") !== nextValue && (params.set("peek_page", nextValue), changed = !0), changed && (url.hash = params.toString(), window.history.replaceState(window.history.state, "", url.href));
  }

  // src/eh/request.ts
  async function requestPage(url, options = {}) {
    let controller = new AbortController(), abort = () => controller.abort(), timeoutMs = options.timeoutMs === void 0 ? 3e4 : options.timeoutMs, timeout = timeoutMs === null ? null : window.setTimeout(abort, timeoutMs);
    options.signal?.aborted ? controller.abort() : options.signal?.addEventListener("abort", abort, { once: !0 });
    try {
      let response = await fetch(url, {
        method: options.method ?? "GET",
        body: options.body,
        credentials: "include",
        headers: options.headers,
        signal: controller.signal
      });
      if (!response.ok)
        throw new Error(`HTTP ${response.status}`);
      let html = await response.text();
      return {
        document: new DOMParser().parseFromString(html, "text/html"),
        url: response.url || url
      };
    } finally {
      timeout !== null && window.clearTimeout(timeout), options.signal?.removeEventListener("abort", abort);
    }
  }
  async function updateGalleryFavorite(actionUrl, value) {
    let body = new URLSearchParams();
    body.set("favcat", value), body.set("favnote", ""), body.set("apply", "Apply Changes"), body.set("update", "1"), await requestPage(actionUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body
    });
  }
  async function addMyTag(tagName, tagSet, mode) {
    let body = new URLSearchParams();
    body.set("usertag_action", "add"), body.set("tagname_new", tagName), body.set("tagcolor_new", ""), body.set("tagweight_new", "10"), mode === "watched" ? body.set("tagwatch_new", "on") : mode === "hidden" && body.set("taghide_new", "on");
    let url = new URL("/mytags", window.location.origin);
    return url.searchParams.set("tagset", tagSet), requestPage(url.href, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body
    });
  }
  async function deleteMyTag(tagId, tagSet) {
    let body = new URLSearchParams();
    body.set("usertag_action", "mass"), body.set("usertag_target", "0"), body.append("modify_usertags[]", tagId);
    let url = new URL("/mytags", window.location.origin);
    return url.searchParams.set("tagset", tagSet), requestPage(url.href, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded"
      },
      body
    });
  }

  // src/eh/dom/core.ts
  var EHPEEK_ANCHOR_ATTRIBUTE = "data-ehpeek-anchor", EH_SYRINGE_IGNORE_SELECTOR = ".eh-syringe-ignore", mountedNodes = /* @__PURE__ */ new WeakMap(), managedDocumentElement = null, managedBody = null;
  function originalPageNode(node) {
    return node.closest(EH_SYRINGE_IGNORE_SELECTOR) === null;
  }
  function anyDomNode() {
    return !0;
  }
  function createAnchor(name) {
    let selector = `[${EHPEEK_ANCHOR_ATTRIBUTE}="${CSS.escape(name)}"]`;
    if (document.querySelector(selector))
      return null;
    let anchor = document.createElement("div");
    return anchor.setAttribute(EHPEEK_ANCHOR_ATTRIBUTE, name), DomNode.from(anchor).inplace();
  }
  function createManagedElement(tagName) {
    return ManagedDomNode.from(document.createElement(tagName));
  }
  function documentElement() {
    return managedDocumentElement ?? (managedDocumentElement = DomNode.from(document.documentElement).inplace()), managedDocumentElement;
  }
  function documentBody() {
    return managedBody ?? (managedBody = DomNode.from(document.body).inplace()), managedBody;
  }
  var _node, _DomNode = class _DomNode {
    constructor(node) {
      __privateAdd(this, _node);
      __privateSet(this, _node, node);
    }
    static from(node) {
      return new _DomNode(node);
    }
    one(selector, filter = originalPageNode) {
      return Array.from(
        __privateGet(this, _node).querySelectorAll(selector),
        _DomNode.from
      ).find(filter) ?? null;
    }
    all(selector, filter = originalPageNode) {
      return Array.from(__privateGet(this, _node).querySelectorAll(selector)).map(_DomNode.from).filter(filter);
    }
    parent() {
      let parent = __privateGet(this, _node).parentElement;
      return parent ? _DomNode.from(parent) : null;
    }
    children() {
      return Array.from(__privateGet(this, _node).children, (child) => _DomNode.from(child));
    }
    closest(selector) {
      let element = __privateGet(this, _node).closest(selector);
      return element ? _DomNode.from(element) : null;
    }
    matches(selector) {
      return __privateGet(this, _node).matches(selector);
    }
    previous() {
      let previous = __privateGet(this, _node).previousElementSibling;
      return previous instanceof HTMLElement ? _DomNode.from(previous) : null;
    }
    form() {
      return __privateGet(this, _node).form ? _DomNode.from(__privateGet(this, _node).form) : null;
    }
    childElementCount() {
      return __privateGet(this, _node).childElementCount;
    }
    text() {
      return __privateGet(this, _node).textContent?.trim() ?? "";
    }
    attribute(name) {
      return __privateGet(this, _node).getAttribute(name);
    }
    hasAttribute(name) {
      return __privateGet(this, _node).hasAttribute(name);
    }
    attributeNames() {
      return __privateGet(this, _node).getAttributeNames();
    }
    hasClass(className2) {
      return __privateGet(this, _node).classList.contains(className2);
    }
    computedStyle() {
      return window.getComputedStyle(__privateGet(this, _node));
    }
    imageSize() {
      return {
        height: __privateGet(this, _node).naturalHeight || __privateGet(this, _node).height || Number(__privateGet(this, _node).getAttribute("height") || ""),
        width: __privateGet(this, _node).naturalWidth || __privateGet(this, _node).width || Number(__privateGet(this, _node).getAttribute("width") || "")
      };
    }
    inputValue() {
      return __privateGet(this, _node).value;
    }
    checked() {
      return __privateGet(this, _node).checked;
    }
    selected() {
      return __privateGet(this, _node).selected;
    }
    sameNode(other) {
      return __privateGet(this, _node) === __privateGet(other, _node);
    }
    observe(selector, acquire, onManaged, options = { childList: !0, subtree: !0 }) {
      let seen = [], cleanups = [], scan = () => {
        for (let node of this.all(selector)) {
          if (seen.some((candidate) => candidate.sameNode(node)))
            continue;
          seen.push(node);
          let managed = acquire(node);
          if (!managed)
            continue;
          let cleanup = onManaged(managed);
          cleanup && cleanups.push(cleanup);
        }
      }, observer = new MutationObserver(scan);
      return scan(), observer.observe(__privateGet(this, _node), options), () => {
        observer.disconnect(), cleanups.forEach((cleanup) => cleanup());
      };
    }
    inplace() {
      return ManagedDomNode.from(__privateGet(this, _node));
    }
    move() {
      let managed = this.inplace();
      return managed.remove(), managed;
    }
    clone(deep = !0) {
      return ManagedDomNode.from(__privateGet(this, _node).cloneNode(deep));
    }
  };
  _node = new WeakMap();
  var DomNode = _DomNode, _node2, _ManagedDomNode = class _ManagedDomNode {
    constructor(element) {
      __privateAdd(this, _node2);
      __privateSet(this, _node2, element), this.Component = () => __privateGet(this, _node2);
    }
    static from(element) {
      return new _ManagedDomNode(element);
    }
    all(selector) {
      return Array.from(
        __privateGet(this, _node2).querySelectorAll(selector),
        _ManagedDomNode.from
      );
    }
    rect() {
      return __privateGet(this, _node2).getBoundingClientRect();
    }
    readAttribute(name) {
      return __privateGet(this, _node2).getAttribute(name);
    }
    setAttributes(values) {
      for (let [name, value] of Object.entries(values))
        __privateGet(this, _node2).setAttribute(name, value);
      return this;
    }
    removeAttributes(...names) {
      for (let name of names)
        __privateGet(this, _node2).removeAttribute(name);
      return this;
    }
    addClasses(...names) {
      return __privateGet(this, _node2).classList.add(...names), this;
    }
    removeClasses(...names) {
      return __privateGet(this, _node2).classList.remove(...names), this;
    }
    replaceClasses(value) {
      return __privateGet(this, _node2).className = value, this;
    }
    styles(values, priority = "") {
      for (let [property, value] of Object.entries(values))
        __privateGet(this, _node2).style.setProperty(property, value, priority);
      return this;
    }
    removeStyles(...properties) {
      for (let property of properties)
        __privateGet(this, _node2).style.removeProperty(property);
      return this;
    }
    removeAllStyles() {
      return __privateGet(this, _node2).removeAttribute("style"), this;
    }
    attribute(name, value) {
      return __privateGet(this, _node2).setAttribute(name, value), this;
    }
    click() {
      __privateGet(this, _node2).click();
    }
    mount(view) {
      mountedNodes.get(__privateGet(this, _node2))?.(), __privateGet(this, _node2).replaceChildren(), mountedNodes.set(__privateGet(this, _node2), render(view, __privateGet(this, _node2)));
    }
    remove() {
      mountedNodes.get(__privateGet(this, _node2))?.(), mountedNodes.delete(__privateGet(this, _node2)), __privateGet(this, _node2).remove();
    }
    replaceWith(replacement) {
      __privateGet(this, _node2).replaceWith(
        replacement instanceof _ManagedDomNode ? __privateGet(replacement, _node2) : replacement
      );
    }
    before(sibling) {
      __privateGet(this, _node2).before(sibling instanceof _ManagedDomNode ? __privateGet(sibling, _node2) : sibling);
    }
    after(sibling) {
      __privateGet(this, _node2).after(sibling instanceof _ManagedDomNode ? __privateGet(sibling, _node2) : sibling);
    }
    append(...children) {
      return __privateGet(this, _node2).append(...children.map((child) => __privateGet(child, _node2))), this;
    }
    prepend(child) {
      __privateGet(this, _node2).prepend(child instanceof _ManagedDomNode ? __privateGet(child, _node2) : child);
    }
    setTextUnlessInput(text) {
      __privateGet(this, _node2) instanceof HTMLInputElement || (__privateGet(this, _node2).textContent = text);
    }
    setHidden(hidden) {
      return __privateGet(this, _node2).hidden = hidden, this;
    }
    replaceChildren(...children) {
      __privateGet(this, _node2).replaceChildren(...children.map((child) => child instanceof _ManagedDomNode ? __privateGet(child, _node2) : child));
    }
    listen(type, listener, options) {
      return __privateGet(this, _node2).addEventListener(type, listener, options), () => __privateGet(this, _node2).removeEventListener(type, listener, options);
    }
    observe(onChange, options = { childList: !0, subtree: !0 }) {
      let observer = new MutationObserver(onChange);
      return observer.observe(__privateGet(this, _node2), options), () => observer.disconnect();
    }
    focus() {
      __privateGet(this, _node2).focus();
    }
    scrollIntoView(options) {
      __privateGet(this, _node2).scrollIntoView(options);
    }
    isNode(node) {
      return __privateGet(this, _node2) === node;
    }
    copyAttributesTo(target) {
      for (let attribute of Array.from(__privateGet(this, _node2).attributes))
        __privateGet(target, _node2).setAttribute(attribute.name, attribute.value);
    }
    setInputValue(value) {
      __privateGet(this, _node2).value = value;
    }
    inputValue() {
      return __privateGet(this, _node2).value;
    }
    setSelected(selected) {
      __privateGet(this, _node2).selected = selected;
    }
    dispatchInput() {
      __privateGet(this, _node2).dispatchEvent(new Event("input", { bubbles: !0 }));
    }
    mirrorContentTo(target) {
      let update = () => {
        target.replaceChildren(
          ...Array.from(__privateGet(this, _node2).childNodes, (node) => node.cloneNode(!0))
        );
        let language = __privateGet(this, _node2).getAttribute("lang");
        language ? target.setAttribute("lang", language) : target.removeAttribute("lang");
      };
      return update(), this.observe(update, {
        attributes: !0,
        attributeFilter: ["lang"],
        characterData: !0,
        childList: !0,
        subtree: !0
      });
    }
  };
  _node2 = new WeakMap();
  var ManagedDomNode = _ManagedDomNode;

  // src/eh/dom/ehSyringe.ts
  var ehSyringe_exports = {};
  __export(ehSyringe_exports, {
    initialize: () => initialize
  });

  // src/state/index.ts
  var touchUiDefault = window.matchMedia("(pointer: coarse)").matches, state = {
    app: {
      ehSyringeDetected: persisted("ehpeek:ehsyringe:detected", !1),
      openGalleryInNewTab: persisted("ehpeek:open-gallery-in-new-tab", !1)
    },
    reader: {
      enabled: persisted("ehpeek:reader:enabled", !0),
      fullscreen: persisted("ehpeek:reader:fullscreen", prefersTouchFullscreen()),
      viewMode: persisted("ehpeek:reader:view-mode", "scroll"),
      readDirection: persisted("ehpeek:reader:read-direction", "rtl"),
      rightTapAction: persisted("ehpeek:reader:right-tap-action", "previous")
    },
    gallery: {
      enhanceThumbs: persisted("ehpeek:enhance-thumbs:enabled", !0),
      myTags: persisted("ehpeek:my-tags:enabled", !0),
      myTagAppearances: localJson("ehpeek:my-tags", [], isMyTagAppearance),
      myTagSets: localJson("ehpeek:my-tag-sets", [], isMyTagSetOption),
      readHistory: persisted("ehpeek:read-history:enabled", !0),
      readHistoryCount: persisted("ehpeek:history-count", 0)
    },
    search: {
      enhance: persisted("ehpeek:enhance-search:enabled", !0),
      grid: localSelection("ehpeek:search-grid", "ehpeek"),
      history: persisted("ehpeek:search-history:enabled", !0),
      searchHistory: persisted("ehpeek:search:history", [])
    },
    touch: {
      enabled: persisted("ehpeek:touch-ui:enabled", touchUiDefault)
    }
  };
  function loadSearchHistory() {
    let history = state.search.searchHistory.reload();
    return Array.isArray(history) ? history.filter((item) => typeof item == "string") : [];
  }
  function addSearchHistory(value) {
    let normalized = value.trim();
    if (!normalized)
      return loadSearchHistory();
    let history = [normalized, ...loadSearchHistory().filter((item) => item !== normalized)];
    return state.search.searchHistory.set(history), history;
  }
  function removeSearchHistory(value) {
    let history = loadSearchHistory().filter((item) => item !== value);
    return state.search.searchHistory.set(history), history;
  }
  function prefersTouchFullscreen() {
    return window.matchMedia("(pointer: coarse)").matches || navigator.maxTouchPoints > 0;
  }
  function persisted(key, defaultValue) {
    let item = {
      defaultValue,
      value: GM_getValue(key, defaultValue),
      set(value) {
        item.value = value, GM_setValue(key, value);
      },
      reload() {
        return item.value = GM_getValue(key, defaultValue), item.value;
      }
    };
    return item;
  }
  function localSelection(key, selectedValue) {
    let read = () => window.localStorage.getItem(key) === selectedValue, item = {
      defaultValue: !1,
      value: read(),
      set(value) {
        item.value = value, value ? window.localStorage.setItem(key, selectedValue) : window.localStorage.removeItem(key);
      },
      reload() {
        return item.value = read(), item.value;
      }
    };
    return item;
  }
  function localJson(key, defaultValue, valid) {
    let read = () => {
      try {
        let value = JSON.parse(window.localStorage.getItem(key) ?? "null");
        return Array.isArray(value) ? value.filter(valid) : defaultValue;
      } catch {
        return defaultValue;
      }
    }, item = {
      defaultValue,
      value: read(),
      set(value) {
        item.value = value, window.localStorage.setItem(key, JSON.stringify(value));
      },
      reload() {
        return item.value = read(), item.value;
      },
      clear() {
        item.value = defaultValue, window.localStorage.removeItem(key);
      },
      stored() {
        return window.localStorage.getItem(key) !== null;
      }
    };
    return item;
  }
  function isMyTagAppearance(value) {
    if (!value || typeof value != "object" || Array.isArray(value))
      return !1;
    let item = value;
    return typeof item.name == "string" && typeof item.backgroundColor == "string" && typeof item.color == "string" && typeof item.id == "string" && typeof item.tagSet == "string";
  }
  function isMyTagSetOption(value) {
    if (!value || typeof value != "object" || Array.isArray(value))
      return !1;
    let item = value;
    return typeof item.label == "string" && typeof item.selected == "boolean" && typeof item.value == "string";
  }

  // src/eh/dom/ehSyringe.ts
  var ROOT_CLASS = "ehs-injected", TAG_TIP_LIST_SELECTOR = ".eh-syringe-lite-auto-complete-list", TAG_TIP_LIST_CLASS_NAME = "!max-h-[60dvh] !py-sm [&_.auto-complete-item]:box-border [&_.auto-complete-item]:min-h-lg [&_.auto-complete-item]:!py-sm [&_.auto-complete-item]:!px-lg [&_.auto-complete-item]:!text-[length:var(--font-size-lg)] [&_.auto-complete-item]:!leading-[1.25] [&_.auto-complete-text]:!text-inherit [&_.auto-complete-text]:!leading-inherit", INJECTION_TIMEOUT_MS = 3e3;
  function initialize() {
    let stopCoordination = state.app.ehSyringeDetected.value ? coordinateEhSyringe() : null;
    detect().then((detected) => {
      if (detected) {
        stopCoordination ?? (stopCoordination = coordinateEhSyringe());
        return;
      }
      stopCoordination?.();
    });
  }
  function coordinateEhSyringe() {
    let updateTagTipListVisual = () => {
      DomNode.from(document).all(TAG_TIP_LIST_SELECTOR, anyDomNode).forEach((list) => {
        let classes = TAG_TIP_LIST_CLASS_NAME.split(" ");
        classes.some((className2) => !list.hasClass(className2)) && list.inplace().addClasses(...classes);
      });
    }, tagTipListObserver = new MutationObserver(updateTagTipListVisual);
    return tagTipListObserver.observe(document.documentElement, {
      childList: !0,
      subtree: !0
    }), updateTagTipListVisual(), () => {
      tagTipListObserver.disconnect();
    };
  }
  async function detect() {
    document.readyState === "loading" && await new Promise((resolve) => {
      document.addEventListener("DOMContentLoaded", () => resolve(), { once: !0 });
    });
    let detected = isInjected();
    return !detected && state.app.ehSyringeDetected.value && (await new Promise((resolve) => {
      window.setTimeout(resolve, INJECTION_TIMEOUT_MS);
    }), detected = isInjected()), state.app.ehSyringeDetected.value !== detected && state.app.ehSyringeDetected.set(detected), detected;
  }
  function isInjected() {
    return DomNode.from(document.documentElement).hasClass(ROOT_CLASS);
  }

  // src/texts.json
  var texts_default = {
    description: "A touch-optimized E-H/ExH viewer",
    button: {
      apply: "Apply",
      close: "Close",
      confirm: "Confirm",
      default: "Default"
    },
    reader: {
      download: "Download",
      downloadDisplayedImage: "Displayed image",
      downloadOriginalImage: "Original image",
      originalImageSource: "Original source provided by E-Hentai",
      originalImageUnavailable: "Original image unavailable",
      startReading: "Read",
      continueReading: "Continue",
      loading: "Loading...",
      pages: "Pages",
      endPage: "End",
      end: "End of gallery. Tap to exit.",
      failedPrefix: "Failed"
    },
    favorites: {
      all: "All"
    },
    gallery: {
      favoriteTag: "Favorite",
      removeFavoriteTag: "Remove My Tag",
      tagCollection: "Collection",
      tagBehavior: "Behavior",
      markTag: "Mark",
      watchTag: "Watch",
      hideTag: "Hide"
    },
    settings: {
      openSettings: "Settings",
      menuLabel: "Ehpeek",
      on: "On",
      off: "Off",
      readerLabel: "Reader",
      readerHelp: "Opens gallery images in Ehpeek's reader",
      readerFullscreenLabel: "Reader in Fullscreen",
      readerFullscreenHelp: "Enters fullscreen when the Reader opens",
      readerOptions: "Options",
      openGalleryInNewTabLabel: "Gallery in New Tab",
      openGalleryInNewTabHelp: "Opens Gallery links in a new browser tab",
      enhance: "Enhance",
      enhanceSearchLabel: "Search Grids",
      enhanceSearchHelp: "Adds swipe navigation to search pages",
      enhanceThumbsLabel: "Thumbs Grids",
      enhanceThumbsHelp: "Adds swipe navigation and scrollable pages bar for gallery preview",
      myTagsLabel: "My Tag",
      myTagsHelp: "Highlights your saved tags with colors in gallery",
      readHistoryLabel: "Read History",
      readHistoryHelp: "Remembers reading progress",
      searchHistoryLabel: "Search History",
      searchHistoryHelp: "Remembers previous searches",
      touchUiLabel: "Touch UI",
      touchUiHelp: "Uses touch-friendly navigation UI."
    },
    search: {
      advancedOptions: "Advanced Options",
      categories: "Categories",
      fileSearch: "File Search"
    },
    errors: {
      imageNotFound: "Image not found",
      downloadFailed: "Download failed",
      loadFailed: "Load failed",
      imageLoadFailed: "Image load failed",
      previewPageSizeUnknown: "Cannot determine gallery preview page size",
      searchPageContentNotFound: "Cannot find search page content"
    }
  };

  // src/utils.ts
  function clamp(value, min, max) {
    return max < min ? min : Math.min(max, Math.max(min, value));
  }
  function normalizeUrl(url, baseUrl = window.location.href) {
    try {
      return new URL(url, baseUrl).href;
    } catch {
      return "";
    }
  }
  function normalizedAspectRatio(value, fallback) {
    return value && Number.isFinite(value) && value > 0 ? value : fallback;
  }
  function positiveNumber(value) {
    return value && Number.isFinite(value) && value > 0 ? value : null;
  }
  function stopEvent(event) {
    event.stopPropagation();
  }
  function registerGlobalStyle(id, css) {
    if (!css || document.getElementById(id))
      return;
    let style2 = document.createElement("style");
    style2.id = id, style2.textContent = css, (document.head ?? document.documentElement).append(style2);
  }

  // src/eh/dom/gallery.ts
  var GALLERY_PAGE_DESCRIPTION_SELECTOR = ".gpc";
  function extractMyTagsPageData(root = document, tagSet) {
    let page = DomNode.from(root), tags = page.one("#usertags_outer");
    if (!tags)
      throw new Error("The My Tags page could not be read.");
    let options = page.all("#tagset_outer select option").map((option) => ({
      label: option.text() || option.inputValue(),
      selected: option.selected(),
      value: option.inputValue()
    })), activeTagSet = tagSet ?? options.find((option) => option.selected)?.value ?? "1", defaultColor = page.one("#tagcolor")?.inputValue().trim() ?? "", output = [];
    for (let item of tags.all(":scope > [id^='usertag_']")) {
      let preview = item.one("[id^='tagpreview_'][title]"), name = normalizeTagName(preview?.attribute("title") ?? "");
      if (!preview || !name)
        continue;
      let itemColor = item.one("input[id^='tagcolor_']")?.inputValue() ?? "", backgroundColor = normalizeTagColor(itemColor) || normalizeTagColor(defaultColor), id = item.attribute("id")?.match(/^usertag_(\d+)$/)?.[1] ?? "";
      id && output.push({
        name,
        backgroundColor,
        color: readableTagColor(backgroundColor),
        id,
        tagSet: activeTagSet
      });
    }
    return {
      appearances: output,
      enabled: page.one("#tagset_enable")?.checked() ?? !0,
      options
    };
  }
  function normalizeTagName(value) {
    return value.trim().replace(/\s+/g, " ").toLowerCase();
  }
  function normalizeTagColor(value) {
    let color = value.trim();
    return /^#[\da-f]{6}$/i.test(color) ? color : "";
  }
  function readableTagColor(backgroundColor) {
    let red = Number.parseInt(backgroundColor.slice(1, 3), 16) / 255, green = Number.parseInt(backgroundColor.slice(3, 5), 16) / 255, blue = Number.parseInt(backgroundColor.slice(5, 7), 16) / 255, linear = (channel) => channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
    return 0.2126 * linear(red) + 0.7152 * linear(green) + 0.0722 * linear(blue) > 0.179 ? "#000000" : "#ffffff";
  }
  function mutateGalleryMyTags(appearances) {
    let byName = new Map(appearances.map((appearance) => [appearance.name, appearance])), apply = () => {
      for (let tag of DomNode.from(document).all("#taglist a")) {
        let name = galleryTagNameFromUrl(tag.attribute("href") ?? ""), appearance = name ? byName.get(normalizeTagName(name)) : void 0;
        if (!appearance?.backgroundColor)
          continue;
        let container = tag.closest("div.gt, div.gtl, div.gtw");
        container && (container.inplace().styles({
          "--ehpeek-my-tag-color": appearance.color,
          "background-color": appearance.backgroundColor
        }, "important").addClasses("[&>a]:!text-[var(--ehpeek-my-tag-color)]"), tag.inplace().removeStyles("color").removeClasses("!text-[var(--ehpeek-my-tag-color)]").setAttributes({
          "data-ehpeek-my-tag-id": appearance.id,
          "data-ehpeek-my-tag-set": appearance.tagSet
        }));
      }
    };
    return apply(), DomNode.from(document).one("#taglist")?.inplace().observe(apply) ?? (() => {
    });
  }
  function manageGalleryContinueReadingButtonMount() {
    let managedHost = createManagedElement("div"), viewerOptions = DomNode.from(document).one("#gd5")?.inplace();
    return viewerOptions ? (viewerOptions.addClasses("ehpeek-gallery-actions").append(managedHost), managedHost) : (documentBody().append(managedHost), managedHost);
  }
  function manageGalleryPreview(root = document, baseUrl = window.location.href) {
    let page = DomNode.from(root), currentUrl = new URL(baseUrl, window.location.href).href, currentIndex = previewPageIndex(currentUrl), pageDescriptionSource = page.one(GALLERY_PAGE_DESCRIPTION_SELECTOR), rangeText = pageDescriptionSource?.text() ?? "", rangeValues = rangeText.match(/([\d,]+)\s*-\s*([\d,]+)\D+([\d,]+)/)?.slice(1).map((value) => Number(value.replace(/,/g, ""))) ?? [], startImage = rangeValues[0], endImage = rangeValues[1], totalImages = rangeValues[2];
    if (startImage === void 0 || endImage === void 0 || totalImages === void 0 || !Number.isSafeInteger(startImage) || !Number.isSafeInteger(endImage) || !Number.isSafeInteger(totalImages) || startImage <= 0 || endImage <= 0 || totalImages <= 0 || endImage < startImage || totalImages < endImage)
      throw new Error("Cannot read the gallery preview image range.");
    let currentPageSize = endImage - startImage + 1, inferredFullPageSize = endImage === totalImages && currentIndex > 0 ? (totalImages - currentPageSize) / currentIndex : currentPageSize;
    if (!Number.isInteger(inferredFullPageSize) || inferredFullPageSize <= 0)
      throw new Error("Cannot determine the gallery preview page size.");
    let pageSize = inferredFullPageSize, maxIndex = Math.max(currentIndex, Math.ceil(totalImages / pageSize) - 1), seen = /* @__PURE__ */ new Set(), pages = page.all("#gdt a[href], .gdtm a[href], .gdtl a[href], a[href*='/s/']").flatMap((link) => {
      let url = normalizeUrl(link.attribute("href") || "", currentUrl), imagePage = extractPageType(url);
      if (imagePage.type !== "image" || seen.has(url))
        return [];
      seen.add(url);
      let size = link.one("img")?.imageSize();
      return [{
        aspectRatio: size && size.width > 0 && size.height > 0 ? size.height / size.width : 1.42,
        pageNum: imagePage.pageNum,
        url
      }];
    }).sort((left, right) => (left.pageNum ?? Number.MAX_SAFE_INTEGER) - (right.pageNum ?? Number.MAX_SAFE_INTEGER)), data = {
      currentIndex,
      currentUrl,
      descriptionText: rangeText,
      endImage,
      maxIndex,
      pageSize,
      pages,
      startImage,
      totalImages
    }, thumbsSource = page.one("#gdt"), pageBarTopSource = page.one(".ptt"), pageBarBottomSource = page.one(".ptb"), createPageBarMount = (position) => createManagedElement("div").replaceClasses(
      `w-max max-w-full mx-auto overflow-x-auto touch-pan-y [-webkit-overflow-scrolling:touch] [&[data-dragging=true]]:select-none ${position === "top" ? "mt-2px mb-0" : "mt-0 mb-10px"}`
    ), elems = {
      mount: root === document && thumbsSource ? createManagedElement("div").replaceClasses("contents") : null,
      originalPageBarBottom: pageBarBottomSource?.inplace() ?? null,
      originalPageBarTop: pageBarTopSource?.inplace() ?? null,
      originalPageDescription: pageDescriptionSource?.inplace() ?? null,
      pageBarBottom: pageBarBottomSource ? createPageBarMount("bottom") : null,
      pageBarDescription: pageDescriptionSource && pageBarTopSource ? createManagedElement("div") : null,
      pageBarTop: pageBarTopSource ? createPageBarMount("top") : null,
      thumbImages: thumbsSource?.all("img").map((image) => image.inplace()) ?? [],
      thumbItems: thumbsSource?.children().map(
        (item) => root === document ? item.inplace() : item.move()
      ) ?? [],
      thumbs: root === document ? thumbsSource?.inplace() ?? null : null
    };
    return elems.mount && elems.thumbs && elems.thumbs.before(elems.mount), { data, elems, handle: {
      /** Opens thumbnail image links in EhPeek Reader instead of original navigation. */
      interceptPreviewImageOpen(onOpen) {
        let handleClick = (event) => {
          let link = event.target instanceof Element ? DomNode.from(event.target).closest("a[href]") : null, href = link?.attribute("href") ?? "";
          !link || extractPageType(href).type !== "image" || !link.one("img") && !link.closest("#gdt, .gdtm, .gdtl") || (event.preventDefault(), event.stopPropagation(), onOpen(normalizeUrl(href, currentUrl)));
        };
        return elems.thumbs?.listen("click", handleClick) ?? (() => {
        });
      },
      /** Makes thumbnail dragging available to the horizontal preview-page gesture. */
      ensurePreviewSwipeInput() {
        elems.thumbs?.addClasses("select-none", "touch-pan-y");
        for (let image of elems.thumbImages)
          image.setAttributes({ draggable: "false" }).addClasses("[-webkit-user-drag:none]");
      },
      /** Installs a fetched preview page into the currently visible thumbnail host. */
      replacePreviewThumbs(items) {
        elems.thumbs?.replaceChildren(...items);
      },
      /** Marks preview loading while retaining the currently visible thumbnails. */
      updatePreviewLoading(loading) {
        elems.thumbs?.attribute("aria-busy", String(loading));
      },
      /** Replaces both original page bars with mounts owned by EhPeek pagination. */
      installPreviewPageBars() {
        elems.originalPageBarTop && elems.pageBarTop && (elems.originalPageBarTop.after(elems.pageBarTop), elems.originalPageBarTop.setHidden(!0)), elems.originalPageBarBottom && elems.pageBarBottom && (elems.originalPageBarBottom.after(elems.pageBarBottom), elems.originalPageBarBottom.setHidden(!0)), elems.originalPageDescription && elems.pageBarDescription && elems.pageBarTop && (elems.originalPageDescription.setHidden(!0), elems.pageBarTop.before(elems.pageBarDescription));
      },
      /** Brings the requested EhPeek page bar into view after preview navigation. */
      scrollPreviewPageBarIntoView(position) {
        (position === "top" ? elems.pageBarTop : elems.pageBarBottom)?.scrollIntoView({
          behavior: "smooth",
          block: position === "top" ? "start" : "end"
        });
      }
    } };
  }
  async function loadGalleryPreviewPage(previewIndex, pageUrl) {
    let url = previewUrlForIndex(previewIndex, pageUrl), response = await requestPage(url);
    return manageGalleryPreview(response.document, response.url);
  }
  function extractImageGalleryPage(root = document) {
    for (let link of DomNode.from(root).all("a[href]")) {
      let page = extractPageType(normalizeUrl(link.attribute("href") || ""));
      if (page.type === "gallery")
        return page;
    }
    return null;
  }
  async function loadEhImagePage(page) {
    let response = await requestPage(page.url), source = DomNode.from(response.document), image = source.one("img#img"), imageUrl = normalizeUrl(
      image?.attribute("src") || image?.attribute("data-src") || "",
      page.url
    );
    if (!imageUrl)
      throw new Error(texts_default.errors.imageNotFound);
    let numberAttribute = (name) => {
      let value = Number(image?.attribute(name));
      return Number.isFinite(value) && value > 0 ? value : null;
    };
    return {
      height: numberAttribute("height"),
      imageUrl,
      originalImageUrl: source.all("a[href]").map((link) => normalizeUrl(link.attribute("href") || "", page.url)).find(isFullImageUrl) ?? null,
      width: numberAttribute("width")
    };
  }

  // src/eh/dom/galleryInfo.ts
  function manageGalleryInfo(preview) {
    let mount = createAnchor("gallery-info");
    if (!mount)
      return null;
    let page = DomNode.from(document), original = page.one("#gmid"), host = original?.parent() ?? page.one("#gleft")?.parent() ?? null;
    if (!original || !host)
      return null;
    let readMeta = () => {
      let rows = page.all("#gdd tr").map((row) => {
        let cells = row.all("td, th");
        return {
          label: cells[0]?.text() ?? "",
          value: cells.slice(1).map((cell) => cell.text()).filter(Boolean).join(" ")
        };
      });
      return {
        favorited: rows[6]?.value ? [rows[6].label, rows[6].value].filter(Boolean).join(" ") : void 0,
        fileSize: rows[4]?.value,
        language: rows[3]?.value,
        parent: rows[1]?.value,
        posted: rows[0]?.value
      };
    }, readCategory = (node) => {
      let style2 = node?.computedStyle();
      return {
        "background-color": style2?.backgroundColor ?? "",
        "background-image": style2?.backgroundImage ?? "",
        "border-color": style2?.borderColor ?? "",
        color: style2?.color ?? ""
      };
    }, readCoverUrl = (cover2, source) => {
      let direct = source?.attribute("src") ?? "";
      if (direct)
        return direct;
      for (let node of cover2 ? [cover2, ...cover2.all("*")] : []) {
        let match = node.computedStyle().backgroundImage.match(/url\(["']?(.+?)["']?\)/);
        if (match?.[1])
          return match[1];
      }
      return "";
    }, readFavorite = (element, scripts2) => {
      let displayed = element?.one("#favoritelink")?.text() || element?.one("[title]")?.attribute("title")?.trim() || "", slot = displayed.match(/(?:^|\D)([0-9])(?:\D|$)/)?.[1], favorited = slot !== void 0 || /^favorited$/i.test(displayed), match = (scripts2.find(
        (item) => item.includes("popbase") && item.includes("addfav")
      ) ?? "").match(
        /popbase\s*=\s*base_url\s*\+\s*"gallerypopups\.php\?gid=(\d+)&t=([^"]+)&act="/
      );
      return {
        actionUrl: match ? `/gallerypopups.php?gid=${match[1]}&t=${match[2]}&act=addfav` : "",
        color: slot === void 0 ? null : `var(--color-site-favorite-${slot})`,
        favorited,
        label: favorited ? displayed : "Not Favorited"
      };
    }, readRating = (count, image, labelNode, scripts2) => {
      let label = labelNode?.text() ?? "", match = (scripts2.find((item) => item.includes("display_rating")) ?? "").match(/\bdisplay_rating\s*=\s*(-?\d+(?:\.\d+)?)/), scriptValue = Number(match?.[1]), value = match && Number.isFinite(scriptValue) ? scriptValue : null;
      return label && value !== null ? {
        count: count?.text() ?? "",
        label,
        rated: ["irb", "irg", "irr"].some(
          (className2) => image?.hasClass(className2)
        ),
        value
      } : null;
    }, readActions = () => page.all(
      "#gd5 a, #gd5 button, #gd5 input[type='button'], #gd5 input[type='submit']"
    ).filter((node) => {
      let href = node.attribute("href")?.trim() ?? "";
      return node.hasAttribute("onclick") || !!(href && href !== "#" && !/^javascript:/i.test(href));
    }).slice(0, 6).map((node) => ({
      label: node.text() || node.attribute("title")?.trim() || node.attribute("aria-label")?.trim() || "",
      node
    })), readTag = (tag) => {
      let label = tag.text() || tag.attribute("ehs-tag")?.trim() || tag.attribute("title")?.trim() || "", href = tag.attribute("href") ?? "", name = galleryTagNameFromUrl(href);
      if (!label || !name || !href)
        return null;
      let container = tag.closest("div.gt, div.gtl, div.gtw") ?? tag, tagStyle = tag.computedStyle(), containerStyle = container.computedStyle(), myTagId = tag.attribute("data-ehpeek-my-tag-id"), myTagSet = tag.attribute("data-ehpeek-my-tag-set");
      return {
        data: {
          appearance: {
            backgroundColor: containerStyle.backgroundColor,
            borderColor: containerStyle.borderColor,
            color: tagStyle.color
          },
          label,
          myTag: myTagId && myTagSet ? { id: myTagId, tagSet: myTagSet } : null,
          name,
          url: href
        },
        source: tag
      };
    }, readTagGroups = () => {
      let rows = page.all("#taglist tr");
      return rows.length > 0 ? rows.map((row) => ({
        namespace: row.one(".tc, td:first-child")?.text().replace(/:$/, "") || "tag",
        tags: row.all("a").map(readTag).filter((tag) => tag !== null).slice(0, 30)
      })).filter((group) => group.tags.length > 0) : [{
        namespace: "tag",
        tags: page.all("#taglist a").map(readTag).filter((tag) => tag !== null).slice(0, 60)
      }].filter((group) => group.tags.length > 0);
    }, favoriteColor = (value) => {
      let slot = value.match(/^(?:fav)?([0-9])$/i)?.[1] ?? value.match(/^favorites?\s+([0-9])$/i)?.[1];
      return slot === void 0 ? null : `var(--color-site-favorite-${slot})`;
    }, readFavoriteOptions = (doc, favorited) => DomNode.from(doc).all("input[name='favcat']").map((input) => {
      let row = input.closest("div[style*='height']"), value = input.inputValue();
      return {
        color: favoriteColor(value),
        label: row?.text().replace(/\s+/g, " ") || value,
        selected: favorited && input.checked(),
        value
      };
    }), manageTagGroups = () => readTagGroups().map((group) => ({
      namespace: group.namespace,
      tags: group.tags.map(({ data: tag, source }) => ({
        ...tag,
        contentSource: source.inplace()
      }))
    })), meta = readMeta(), category = page.one("#gdc"), categoryStyle = category?.one("[class*='ct']") ?? category, cover = page.one("#gd1"), coverSource = cover?.one("img") ?? null, favorite = page.one("#fav"), ratingCount = page.one("#rating_count"), ratingImage = page.one("#rating_image"), ratingLabel = page.one("#rating_label"), ratingActions = page.all('map[name="rating"] area'), tagMenuAction = page.one("#tagmenu_act"), newTag = page.one("#tagmenu_new"), newTagButton = newTag?.one("#newtagbutton") ?? null, newTagField = newTag?.one("#newtagfield") ?? null, newTagForm = newTag?.one("form") ?? null, scripts = page.all("script").map((script) => script.text()), actionSources = readActions(), tagContentSources = [], tagGroups = readTagGroups().map((group) => ({
      namespace: group.namespace,
      tags: group.tags.map(({ data: tag, source }) => {
        let contentSourceIndex = tagContentSources.push(source) - 1;
        return { ...tag, contentSourceIndex };
      })
    })), data = {
      category: category?.text() ?? "",
      categoryAppearance: readCategory(categoryStyle),
      favorite: readFavorite(favorite, scripts),
      rating: readRating(ratingCount, ratingImage, ratingLabel, scripts),
      summary: [
        meta.language,
        preview?.totalImages ? `${preview.totalImages} ${texts_default.reader.pages.toLowerCase()}` : void 0,
        meta.fileSize,
        meta.favorited,
        meta.posted ?? meta.parent
      ].filter((value) => !!value).slice(0, 6).map((value) => ({ value })),
      tagGroups,
      titleMain: page.one("#gn")?.text() ?? "",
      titleSub: page.one("#gj")?.text() ?? ""
    }, coverUrl = readCoverUrl(cover, coverSource), hostChildSources = host.all(":scope > *").filter((child) => !newTag?.sameNode(child)), elems = {
      actionItems: actionSources.map(({ node }) => node.move()),
      cover: coverUrl ? coverSource?.clone() ?? createManagedElement("img") : null,
      host: host.inplace(),
      hostChildren: hostChildSources.map((child) => child.inplace()),
      mount,
      newTag: newTagButton && newTagField && newTagForm ? newTag?.move() ?? null : null,
      newTagButton: newTagButton?.inplace() ?? null,
      newTagField: newTagField?.inplace() ?? null,
      newTagForm: newTagForm?.inplace() ?? null,
      ratingActions: ratingActions.map((action) => action.inplace()),
      tagContents: tagContentSources.map((source) => source.inplace()),
      tagList: page.one("#taglist")?.inplace() ?? null,
      tagMenuAction: tagMenuAction?.inplace() ?? null
    }, selectedTagSource = null, activateTagMenu = (source) => {
      let stopNavigation = source.listen("click", (event) => {
        event.preventDefault();
      }, { once: !0 });
      source.click(), stopNavigation();
    };
    return { data, elems, handle: {
      /** Normalizes the original cover for GalleryInfoPanel's responsive layout. */
      updateCoverVisual(className2) {
        elems.cover?.removeAttributes("id", "style", "width", "height").setAttributes({ alt: "", decoding: "async", loading: "eager", src: coverUrl }).replaceClasses(className2);
      },
      /** Converts original Gallery actions into consistently styled component items. */
      updateActionItemsVisual(className2) {
        elems.actionItems.forEach((action, index) => {
          action.removeAttributes("id").replaceClasses(className2).removeAllStyles(), action.setTextUnlessInput(actionSources[index]?.label ?? "");
        });
      },
      /** Fits the original New Tag form into GalleryInfoPanel's tag controls. */
      updateNewTagVisual(classes) {
        elems.newTag?.addClasses(...classes.container.split(" ")).setHidden(!1).removeStyles("display"), elems.newTagButton?.addClasses(...classes.button.split(" ")), elems.newTagField?.removeAttributes("size").addClasses(...classes.field.split(" ")), elems.newTagForm?.addClasses(...classes.form.split(" "));
      },
      /** Hides original GalleryInfo children and installs the component mount. */
      installGalleryInfoPanel(className2) {
        elems.host.addClasses(className2), elems.hostChildren.forEach((child) => {
          child.setHidden(!0), child.styles({ display: "none" }, "important");
        }), elems.host.prepend(elems.mount);
      },
      /** Loads the original favorite dialog choices for EhPeek's favorite modal. */
      async loadGalleryFavoriteOptions(actionUrl, favorited) {
        let response = await requestPage(actionUrl);
        return readFavoriteOptions(response.document, favorited);
      },
      /** Submits a tag to the chosen My Tags collection and validates the response. */
      async submitFavoriteTag(tag, tagSet, mode) {
        let response = await addMyTag(tag.name, tagSet, mode);
        return extractMyTagsPageData(response.document, tagSet);
      },
      /** Keeps component tag groups synchronized with original-page tag updates. */
      observeGalleryTagGroups(onChange) {
        return elems.tagList?.observe(() => onChange(manageTagGroups())) ?? (() => {
        });
      },
      /** Activates E-H's original rating area and lets its page script submit the vote. */
      submitGalleryRating(value) {
        let rating = Math.round(value * 2);
        if (rating < 1 || rating > 10)
          throw new RangeError("Gallery rating must be between 0.5 and 5 stars.");
        let action = elems.ratingActions[rating - 1];
        if (!action)
          throw new Error("Gallery rating action is unavailable.");
        action.click();
      },
      /** Removes the selected tag from its stored My Tags collection. */
      async removeFavoriteTag(tag) {
        if (!tag.myTag)
          throw new Error("The tag is not in My Tags.");
        let response = await deleteMyTag(tag.myTag.id, tag.myTag.tagSet);
        return extractMyTagsPageData(response.document, tag.myTag.tagSet);
      },
      /** Opens E-H's original tag actions and only adapts their presentation. */
      openGalleryTagMenu(tag, containerClassName, itemClassName) {
        if (!elems.tagMenuAction)
          throw new Error("Gallery tag actions are unavailable.");
        activateTagMenu(tag.contentSource), elems.newTag?.setHidden(!1).removeStyles("display");
        let actions = elems.tagMenuAction.all("a");
        if (actions.length === 0)
          throw activateTagMenu(tag.contentSource), elems.newTag?.setHidden(!1).removeStyles("display"), new Error("Gallery tag actions could not be opened.");
        selectedTagSource = tag.contentSource, elems.tagMenuAction.replaceClasses(containerClassName), elems.tagMenuAction.all("img").forEach((image) => {
          image.setHidden(!0);
        }), actions.forEach((action) => {
          action.replaceClasses(itemClassName).removeAllStyles();
        }), actions.find((action) => action.readAttribute("onclick")?.includes("toggle_tagmenu"))?.setHidden(!0);
      },
      /** Closes E-H's selected tag without replacing its action DOM. */
      closeGalleryTagMenu() {
        selectedTagSource && activateTagMenu(selectedTagSource), elems.newTag?.setHidden(!1).removeStyles("display"), selectedTagSource = null;
      },
      /** Updates the Gallery favorite state through the original site endpoint. */
      updateGalleryFavorite
    } };
  }
  function mutateGalleryCommentsTouch() {
    let items = DomNode.from(document).all("#cdiv .c5").filter((trigger) => trigger.attribute("data-ehpeek-touch-comment-score") !== "true").map((trigger) => ({
      trigger,
      details: trigger.closest(".c1")?.one(".c7[id^='cvotes_']") ?? null
    })).filter((item) => item.details !== null).map(({ trigger, details }) => ({
      details: details.inplace(),
      detailsId: details.attribute("id") ?? "",
      expanded: !1,
      trigger: trigger.inplace()
    })), setExpanded = (item, expanded) => {
      item.expanded = expanded, item.trigger.attribute("aria-expanded", String(expanded)), item.details.attribute("aria-hidden", String(!expanded)), item.details.styles({ display: expanded ? "" : "none" });
    };
    for (let item of items) {
      item.trigger.removeAttributes("onmouseover", "onmouseout", "onclick").setAttributes({
        "data-ehpeek-touch-comment-score": "true",
        role: "button",
        tabindex: "0",
        "aria-controls": item.detailsId
      }).addClasses("whitespace-nowrap"), setExpanded(item, !1);
      let toggle = (event) => {
        event.preventDefault(), event.stopImmediatePropagation();
        let shouldExpand = !item.expanded;
        for (let candidate of items)
          setExpanded(candidate, candidate === item && shouldExpand);
      };
      item.trigger.listen("click", toggle), item.trigger.listen("keydown", (event) => {
        (event.key === "Enter" || event.key === " ") && toggle(event);
      });
    }
  }

  // src/eh/dom/search.ts
  var TOUCH_FAVORITES_PAGE_CLASS_NAME = "!min-w-0 !max-w-full !overflow-x-hidden", TOUCH_FAVORITES_CONTENT_CLASS_NAME = "box-border !min-w-0 !w-full !max-w-full !overflow-x-hidden", TOUCH_FAVORITES_NAV_CLASS_NAME = "box-border !max-w-full overflow-x-auto", TOUCH_FAVORITES_RESULTS_CLASS_NAME = "ehpeek-touch-favorites-results box-border !min-w-0 !w-full !max-w-full overflow-x-auto", TOUCH_FAVORITES_RESULT_LIST_CLASS_NAME = "!min-w-0 !w-full !max-w-full", TOUCH_FAVORITES_ALL_RESULTS_CLASS_NAME = "!overflow-x-hidden", TOUCH_SEARCH_RESULTS_PAGE_CLASS_NAME = "!min-w-0 !max-w-full !overflow-x-hidden", TOUCH_SEARCH_RESULTS_CONTENT_CLASS_NAME = "box-border !min-w-0 !w-full !max-w-full !overflow-x-hidden", TOUCH_SEARCH_RESULTS_WRAPPER_CLASS_NAME = "ehpeek-touch-search-results box-border !min-w-0 !w-full !max-w-full overflow-x-auto", TOUCH_SEARCH_RESULT_LIST_CLASS_NAME = "!min-w-0 !w-full !max-w-full";
  function manageSearchResults() {
    let page = DomNode.from(document), resultSource = page.one(".itg");
    if (!resultSource)
      return null;
    let data = {
      nextUrl: page.one(".searchnav a[id$='next'][href]")?.attribute("href") ?? null,
      previousUrl: page.one(".searchnav a[id$='prev'][href]")?.attribute("href") ?? null
    }, elems = {
      resultList: resultSource.inplace(),
      searchInput: page.one("#f_search, input[name='f_search']")?.inplace() ?? null
    };
    return { data, elems, handle: {
      /** Routes the original pagination controls through the active page owner. */
      interceptSearchNavigation(onNavigate) {
        let handleClick = (event) => {
          let url = (event.target instanceof Element ? DomNode.from(event.target).closest(
            ".searchnav a[id$='first'][href], .searchnav a[id$='prev'][href], .searchnav a[id$='next'][href], .searchnav a[id$='last'][href]"
          ) : null)?.attribute("href") ?? null;
          url && (event.preventDefault(), event.stopPropagation(), onNavigate(url));
        };
        return document.addEventListener("click", handleClick, !0), () => document.removeEventListener("click", handleClick, !0);
      },
      /** Replaces the current result page for enhanced swipe navigation. */
      async loadSearchPage(url) {
        let response = await requestPage(url);
        if (!replaceSearchPageContent(response.document))
          throw new Error(texts_default.errors.searchPageContentNotFound);
        window.history.pushState(window.history.state, "", url);
      },
      /** Returns enhanced Search navigation to its input, or the page top when absent. */
      scrollSearchPageToInput() {
        elems.searchInput ? elems.searchInput.scrollIntoView({ block: "start", behavior: "auto" }) : window.scrollTo({ top: 0, behavior: "auto" });
      },
      /** Exposes result loading state without removing the current result list. */
      updateSearchLoading(busy) {
        busy ? elems.resultList.setAttributes({ "aria-busy": "true" }) : elems.resultList.removeAttributes("aria-busy");
      },
      /** Prevents result content from stealing a horizontal swipe gesture. */
      ensureSearchSwipeInput() {
        elems.resultList.addClasses("overscroll-x-contain", "touch-pan-y", "[&[data-dragging=true]]:select-none");
      },
      /** Applies the user setting to gallery links already owned by the result list. */
      ensureGalleryLinksOpenInNewTab() {
        for (let link of elems.resultList.all("a[href]"))
          extractPageType(link.readAttribute("href") ?? "").type === "gallery" && link.setAttributes({ target: "_blank", rel: "noopener noreferrer" });
      }
    } };
  }
  function manageSearchTextInput() {
    let inputSource = DomNode.from(document).one("#f_search, input[name='f_search']"), formSource = inputSource?.form() ?? null, submitSource = formSource?.one(
      "input[name='f_apply'], button[name='f_apply']"
    ) ?? inputSource?.parent()?.one(
      "input[type='submit'], button[type='submit']"
    ) ?? null;
    if (!inputSource || !submitSource)
      return null;
    let elems = {
      form: formSource?.inplace() ?? null,
      input: inputSource.inplace(),
      submit: submitSource.inplace()
    };
    return { data: { value: elems.input.inputValue() }, elems, handle: {
      /** Connects the original input to EhPeek's history and suggestion overlay. */
      listenSearchHistoryOverlay(callbacks, overlay) {
        let update = () => callbacks.onInput(
          elems.input.inputValue(),
          document.activeElement === elems.input.Component()
        ), submitValue = () => callbacks.onSubmit(elems.input.inputValue()), outsidePointer = (event) => {
          let target = event.target;
          target instanceof Node && (elems.input.isNode(target) || overlay()?.contains(target)) || callbacks.onOutsidePointer();
        }, disconnect = [
          elems.input.listen("input", update),
          elems.input.listen("focus", callbacks.onFocus),
          elems.input.listen("pointerdown", callbacks.onFocus),
          elems.input.listen("keydown", callbacks.onKeyDown),
          elems.submit.listen("click", submitValue),
          ...elems.form ? [elems.form.listen("submit", submitValue)] : []
        ];
        return document.addEventListener("pointerdown", outsidePointer, !0), document.addEventListener("scroll", callbacks.onPositionChange, !0), window.addEventListener("resize", callbacks.onPositionChange), () => {
          disconnect.forEach((cleanup) => cleanup()), document.removeEventListener("pointerdown", outsidePointer, !0), document.removeEventListener("scroll", callbacks.onPositionChange, !0), window.removeEventListener("resize", callbacks.onPositionChange);
        };
      },
      /** Locates the overlay directly below the original search input. */
      readSearchOverlayPosition() {
        let rect = elems.input.rect();
        return { left: rect.left, top: rect.bottom, width: rect.width };
      },
      /** Commits a history or suggestion choice through the original input events. */
      applySearchSelection(value) {
        elems.input.setInputValue(value), elems.input.dispatchInput(), elems.input.focus(), elems.input.Component().setSelectionRange(value.length, value.length);
      }
    } };
  }
  function mutateSearchGrid() {
    let page = DomNode.from(document);
    page.one(".ehpeek-search-grid-host")?.inplace().remove();
    let resultList = page.one(".itg");
    if (!resultList)
      return;
    let rows = resultList.all("tbody > tr").map(resolveSearchGridRow).filter((row) => row !== null), resultListElem = resultList.inplace(), bodyElem = resultList.one("tbody")?.inplace() ?? null;
    resultListElem.setHidden(!1).styles({
      display: "block",
      width: "100%",
      "table-layout": "auto"
    }, "important"), bodyElem?.styles({ display: "block" }, "important");
    for (let row of rows)
      applySearchGridRow(row);
    function resolveSearchGridRow(row) {
      let thumbnailCell = row.one(":scope > .gl1e"), contentCell = row.one(":scope > .gl2e"), detail = contentCell?.one(".gl4e"), metadata = contentCell?.one(".gl3e");
      if (!thumbnailCell || !contentCell || !detail || !metadata)
        return null;
      let title = detail.one(":scope > .glink"), parent = detail.parent(), galleryLink = parent?.matches("a[href]") ? parent : null, tags = detail.children().filter((element) => !title?.sameNode(element)), thumbnail = thumbnailCell.one(":scope > div");
      return {
        contentCell,
        detail,
        galleryHref: galleryLink?.attribute("href") ?? null,
        galleryLink,
        image: thumbnail?.one("img") ?? null,
        metadata,
        metadataItems: metadata.children(),
        row,
        selectionCell: row.one(":scope > .glfe"),
        tagCells: tags.flatMap((container) => container.all("td")),
        tagElements: detail.all(".gt, .gtl, .gtw, td.tc"),
        tagTables: tags.flatMap((container) => container.all("table, tbody, tr")),
        tags,
        thumbnail,
        thumbnailCell,
        title,
        titleText: title?.text() ?? ""
      };
    }
    function applySearchGridRow(source) {
      let row = source.row.inplace(), thumbnailCell = source.thumbnailCell.inplace(), contentCell = source.contentCell.inplace(), detail = source.detail.inplace(), metadata = source.metadata.inplace();
      row.styles({
        display: "grid",
        "grid-template-columns": source.selectionCell ? "clamp(112px, 34%, 250px) minmax(0, 1fr) auto" : "clamp(112px, 34%, 250px) minmax(0, 1fr)",
        "align-items": "start",
        "column-gap": "0",
        width: "100%"
      }, "important"), thumbnailCell.styles({ width: "auto" }, "important"), contentCell.styles({
        width: "auto",
        "min-width": "0",
        "align-self": "stretch",
        height: "100%",
        "box-sizing": "border-box",
        "padding-left": "0"
      }, "important"), source.selectionCell?.inplace().styles({ width: "auto", "margin-left": "6px" }, "important"), source.thumbnail?.inplace().styles({ width: "100%", height: "auto" }, "important"), source.image?.inplace().styles({ width: "100%", height: "auto" }, "important"), applySearchGridContent(source, contentCell, detail, metadata);
    }
    function applySearchGridContent(source, contentCell, detail, metadata) {
      let tags = source.tags.map((node) => node.inplace()), title = source.title?.inplace() ?? null, galleryLink = source.galleryLink?.inplace() ?? null;
      if (galleryLink && title && source.galleryHref) {
        let titleLink = createManagedElement("a").attribute("href", source.galleryHref).replaceClasses("block min-w-0 ehp-color-site-text no-underline");
        titleLink.append(title), galleryLink.before(detail), galleryLink.remove(), detail.replaceChildren(titleLink, metadata, ...tags), makeSearchGridContentClickable(contentCell, titleLink, source.galleryHref, source.titleText);
      } else title && title.after(metadata);
      title?.styles({
        height: "auto",
        "min-height": "0",
        overflow: "visible",
        "overflow-wrap": "anywhere",
        "white-space": "normal",
        "word-break": "normal",
        "text-align": "left",
        "font-size": "var(--font-size-md)",
        "font-weight": "700",
        "line-height": "1.35"
      }, "important"), detail.styles({
        display: "flex",
        "flex-direction": "column",
        "justify-content": "flex-start",
        "align-items": "stretch",
        gap: "var(--space-md, 12px)",
        "min-height": "0",
        width: "100%",
        "box-sizing": "border-box",
        "padding-left": "6px"
      }, "important"), metadata.styles({
        display: "flex",
        "flex-direction": "row",
        "flex-wrap": "wrap",
        "align-items": "center",
        "align-content": "flex-start",
        "justify-content": "flex-start",
        gap: "8px 12px",
        float: "none",
        position: "static",
        width: "100%",
        height: "auto",
        "min-height": "0",
        margin: "0",
        padding: "0",
        "font-weight": "600"
      }, "important");
      for (let tag of tags)
        tag.styles({
          position: "static",
          width: "100%",
          height: "auto",
          "min-height": "0",
          flex: "0 0 auto",
          margin: "0",
          padding: "0"
        }, "important");
      for (let table of source.tagTables)
        table.inplace().styles({ height: "auto", "min-height": "0", margin: "0" }, "important");
      for (let cell of source.tagCells)
        cell.inplace().styles({ height: "auto", "min-height": "0", "vertical-align": "top" }, "important");
      for (let tag of source.tagElements)
        tag.inplace().styles({ "font-size": "var(--font-size-sm)", "line-height": "1.2" }, "important");
      for (let itemSource of source.metadataItems) {
        let item = itemSource.inplace();
        if (item.styles({
          float: "none",
          position: "static",
          flex: "0 0 auto",
          "min-width": "0",
          margin: "0",
          "font-size": "var(--font-size-sm)",
          "font-weight": "600"
        }, "important"), itemSource.matches(".ir, .gldown")) {
          item.removeStyles("width", "height");
          continue;
        }
        item.styles({ width: "auto", height: "auto", padding: "0", "line-height": "1.3" }, "important"), itemSource.matches(".cn, .cs, [class*='ct']") && item.styles({
          display: "inline-flex",
          "align-items": "center",
          "justify-content": "center",
          "box-sizing": "border-box",
          width: "max(72px, 6em)",
          height: "max(32px, 2.2em)",
          padding: "0 0.6em"
        }, "important");
      }
    }
    function makeSearchGridContentClickable(contentCell, galleryLink, galleryHref, title) {
      let overlay = createManagedElement("a").attribute("href", galleryHref).attribute("aria-label", title || "Open gallery").replaceClasses("hidden coarse:block absolute inset-0 z-1");
      contentCell.styles({ position: "relative", cursor: "pointer" }, "important").append(overlay).listen("click", (event) => {
        (event.target instanceof Element ? DomNode.from(event.target) : null)?.closest("a[href], button, input, select, textarea, label, [onclick]") || galleryLink.click();
      });
    }
  }
  function mutateSearchGridModeSelect(selected, onEhPeekSelect, onOriginalSelect) {
    let selects = DomNode.from(document).all(
      "select[onchange*='inline_set=dm_']"
    );
    for (let source of selects) {
      let select = source.inplace(), option = source.all("option").find((item) => item.inputValue() === "ehpeek")?.inplace() ?? null;
      option || (option = createManagedElement("option").attribute("value", "ehpeek"), option.setTextUnlessInput("EhPeek"), select.append(option)), option.setSelected(selected), source.attribute("data-ehpeek-grid-mode") !== "true" && (select.attribute("data-ehpeek-grid-mode", "true"), select.listen("change", (event) => {
        if (select.inputValue() !== "ehpeek") {
          onOriginalSelect();
          return;
        }
        event.preventDefault(), event.stopImmediatePropagation(), onEhPeekSelect();
      }, !0));
    }
  }
  function replaceSearchPageContent(doc) {
    let currentList = DomNode.from(document).one(".itg"), incomingList = DomNode.from(doc).one(".itg");
    if (!currentList || !incomingList || !refreshSearchRangeBar(doc))
      return !1;
    replaceFirstElement(".searchtext", doc), replaceSearchNavigationBars(doc);
    let current = currentList.inplace(), importedList = incomingList.clone();
    return current.replaceWith(importedList), !0;
  }
  function refreshSearchRangeBar(doc) {
    let current = DomNode.from(document).one("#rangebar"), incomingPage = DomNode.from(doc), incoming = incomingPage.one("#rangebar");
    if (!current && !incoming)
      return !0;
    if (!current || !incoming)
      return !1;
    let script = incomingPage.all("script").map((item) => item.text()).find((item) => item.includes("build_rangebar()")), rangeUrl = script?.match(/\brangeurl\s*=\s*["']([^"']*)["']/)?.[1], rangeMin = Number(script?.match(/\brangemin\s*=\s*(-?\d+)/)?.[1]), rangeMax = Number(script?.match(/\brangemax\s*=\s*(-?\d+)/)?.[1]), rangeSpan = Number(script?.match(/\brangespan\s*=\s*(-?\d+)/)?.[1]);
    if (rangeUrl === void 0 || !Number.isFinite(rangeMin) || !Number.isFinite(rangeMax) || !Number.isFinite(rangeSpan))
      return !1;
    let items = [];
    if (rangeSpan > 0)
      for (let index = 0; index < 99; index += rangeSpan) {
        let marker = createManagedElement("div");
        if ((index === 98 && rangeMin === 99 || index >= rangeMin && index <= rangeMax) && marker.attribute("data-inrange", "1"), !rangeUrl) {
          items.push(marker);
          continue;
        }
        let href = index === 0 ? rangeUrl : `${rangeUrl}${rangeUrl.includes("?") ? "&" : "?"}range=${index}`;
        items.push(createManagedElement("a").attribute("href", href).append(marker));
      }
    return current.inplace().replaceChildren(...items), !0;
  }
  function replaceSearchNavigationBars(doc) {
    let currentBars = DomNode.from(document).all(".searchnav"), incomingBars = DomNode.from(doc).all(".searchnav"), count = Math.min(currentBars.length, incomingBars.length);
    for (let index = 0; index < count; index += 1) {
      let currentSource = currentBars[index], incomingSource = incomingBars[index];
      if (!currentSource || !incomingSource)
        continue;
      let current = currentSource.inplace(), incoming = incomingSource.clone();
      current.replaceWith(incoming);
    }
  }
  function replaceFirstElement(selector, doc) {
    let current = DomNode.from(document).one(selector), incoming = DomNode.from(doc).one(selector);
    if (!current || !incoming)
      return;
    let currentElement = current.inplace(), incomingElement = incoming.clone();
    currentElement.replaceWith(incomingElement);
  }
  function favoritesPageTouch() {
    documentElement().addClasses(...TOUCH_FAVORITES_PAGE_CLASS_NAME.split(" ")), documentBody().addClasses(...TOUCH_FAVORITES_PAGE_CLASS_NAME.split(" "));
    let page = DomNode.from(document);
    page.one(".ido")?.inplace().removeStyles("min-width").addClasses(...TOUCH_FAVORITES_CONTENT_CLASS_NAME.split(" "));
    let categories = page.one(".ido > .nosel"), categorySelect = categories ? manageFavoritesCategories(categories) : null;
    page.one("input[name='f_search']")?.form()?.parent()?.inplace().removeStyles("width").addClasses("box-border", "!w-full", "!min-w-0", "!max-w-full");
    for (let navigation of page.all(".searchnav"))
      navigation.inplace().addClasses(...TOUCH_FAVORITES_NAV_CLASS_NAME.split(" "));
    let resultSource = page.one(".itg");
    if (!resultSource)
      return categorySelect;
    let existingWrapperSource = resultSource.parent(), existingWrapper = existingWrapperSource?.hasClass("ehpeek-touch-favorites-results") ? existingWrapperSource : null, contentSource = existingWrapper?.parent() ?? resultSource.parent(), allSelected = categorySelect?.info.categories[0]?.selected === !0;
    contentSource?.inplace().addClasses(...TOUCH_FAVORITES_CONTENT_CLASS_NAME.split(" "));
    let resultList = resultSource.inplace();
    if (resultList.addClasses(...TOUCH_FAVORITES_RESULT_LIST_CLASS_NAME.split(" ")), existingWrapper)
      return categorySelect;
    (allSelected || window.innerWidth < 850) && compactFavoritesResultList(resultSource);
    let wrapper = createManagedElement("div").replaceClasses(TOUCH_FAVORITES_RESULTS_CLASS_NAME);
    return (allSelected || window.innerWidth < 850) && wrapper.addClasses(...TOUCH_FAVORITES_ALL_RESULTS_CLASS_NAME.split(" ")), resultList.replaceWith(wrapper), wrapper.append(resultList), categorySelect;
  }
  function compactFavoritesResultList(source) {
    source.inplace().styles({
      "table-layout": "auto",
      width: "100%"
    }, "important");
    for (let content of source.all("tbody > tr > .gl2e"))
      content.inplace().styles({ width: "auto", "overflow-wrap": "anywhere" }, "important");
    for (let title of source.all(".glink"))
      title.inplace().styles({ "white-space": "normal", "overflow-wrap": "anywhere" }, "important");
    for (let tags of source.all(".gl4e table"))
      tags.inplace().styles({
        "table-layout": "fixed",
        width: "100%",
        "max-width": "100%"
      }, "important");
    for (let cell of source.all(".gl4e td"))
      cell.inplace().styles({ "min-width": "0", "overflow-wrap": "anywhere" }, "important");
    for (let namespace of source.all(".gl4e td.tc"))
      namespace.inplace().styles({ width: "4em", "white-space": "nowrap" }, "important");
    for (let selection of source.all("tbody > tr > .glfe"))
      selection.inplace().styles({ width: "1%", "white-space": "nowrap" }, "important");
  }
  function manageFavoritesCategories(container) {
    let nodes = container.all(":scope > .fp, :scope > .fps");
    if (nodes.length === 0)
      return null;
    let parsed = nodes.map((node) => {
      let children = node.children(), countText = children[0]?.text() ?? "0", label = children[children.length - 1]?.text() || node.text(), count = Number(countText.replace(/,/g, "")), indicatorStyle = node.one(".i")?.computedStyle() ?? null;
      return {
        appearance: indicatorStyle ? {
          backgroundImage: indicatorStyle.backgroundImage,
          backgroundPosition: indicatorStyle.backgroundPosition,
          backgroundSize: indicatorStyle.backgroundSize
        } : null,
        count: Number.isFinite(count) ? count : 0,
        label,
        selected: node.hasClass("fps"),
        source: node
      };
    }), all = parsed.find((category) => category.source.childElementCount() === 0), favorites = parsed.filter((category) => category !== all), total = favorites.reduce((sum, category) => sum + category.count, 0);
    container.inplace().setHidden(!0);
    let categories = [
      ...all ? [{ ...all, count: total, label: texts_default.favorites.all }] : [],
      ...favorites
    ];
    return {
      info: {
        categories: categories.map(({ appearance, count, label, selected }) => ({
          appearance,
          count,
          label,
          selected
        }))
      },
      items: categories.map(({ source }) => source.inplace())
    };
  }
  function searchResultsPageTouch() {
    documentElement().addClasses(...TOUCH_SEARCH_RESULTS_PAGE_CLASS_NAME.split(" ")), documentBody().addClasses(...TOUCH_SEARCH_RESULTS_PAGE_CLASS_NAME.split(" "));
    let resultSource = DomNode.from(document).one(".itg");
    if (!resultSource)
      return;
    let existingWrapperSource = resultSource.parent(), existingWrapper = existingWrapperSource?.hasClass("ehpeek-touch-search-results") ? existingWrapperSource : null, contentSource = existingWrapper?.parent() ?? resultSource.parent();
    resultSource.closest(".ido")?.inplace().addClasses(...TOUCH_SEARCH_RESULTS_CONTENT_CLASS_NAME.split(" ")), contentSource?.inplace().addClasses(...TOUCH_SEARCH_RESULTS_CONTENT_CLASS_NAME.split(" "));
    let resultList = resultSource.inplace();
    if (resultList.addClasses(...TOUCH_SEARCH_RESULT_LIST_CLASS_NAME.split(" ")), existingWrapper)
      return;
    let wrapper = createManagedElement("div").replaceClasses(TOUCH_SEARCH_RESULTS_WRAPPER_CLASS_NAME);
    resultList.replaceWith(wrapper), wrapper.append(resultList);
  }
  function manageTouchResultsPage(page) {
    let apply = () => page.type === "favorites" ? favoritesPageTouch() : (page.type === "search" && searchResultsPageTouch(), null), favoritesCategory = apply(), data = { favoritesCategory: favoritesCategory?.info ?? null }, elems = {
      favoriteCategoryItems: favoritesCategory?.items ?? []
    };
    return { data, elems, handle: {
      /** Activates E-H's original Favorites collection control. */
      activateFavoriteCategory(index) {
        elems.favoriteCategoryItems[index]?.click();
      },
      /** Reapplies TouchUI layout after the result list is replaced in place. */
      updateTouchResultsLayout() {
        let updated = apply();
        updated && elems.favoriteCategoryItems.splice(
          0,
          elems.favoriteCategoryItems.length,
          ...updated.items
        );
      }
    } };
  }

  // src/eh/dom/searchPanel.ts
  function manageSearchPanel() {
    let page = DomNode.from(document), searchInput = page.one("#f_search, input[name='f_search']"), form = searchInput?.form() ?? null, standardSearchBox = page.one("#searchbox"), categories = standardSearchBox?.one("form > table") ?? null, advancedPanel = standardSearchBox?.one("#advdiv") ?? null, optionLinks = advancedPanel?.previous() ?? null, fileSearch = page.one("#fsdiv"), searchSubmit = form?.one("input[name='f_apply'], button[name='f_apply']") ?? searchInput?.parent()?.one("input[type='submit'], button[type='submit']") ?? null, clearButton = form?.one("input[name='f_clear'], button[name='f_clear']") ?? searchInput?.parent()?.one("input[type='button'], button[type='button']") ?? null;
    if (!searchInput || !form || !searchSubmit)
      return null;
    let mount = createAnchor("search-panel"), categoryToggleMount = categories && optionLinks ? createAnchor("search-category-toggle") : null, searchActionMount = createAnchor("search-action"), clearActionMount = clearButton ? createAnchor("search-clear-action") : null;
    if (!mount || !searchActionMount || clearButton && !clearActionMount)
      return null;
    let categoryRows = categories?.all("tr") ?? [], categoryCells = categories?.all("td") ?? [], categoryItems = categories?.all("[id^='cat_']") ?? [], optionLinkItems = optionLinks?.all("a") ?? [], advancedToggle = advancedPanel ? optionLinkItems[0] ?? null : null, fileSearchToggle = fileSearch ? optionLinkItems[advancedToggle ? 1 : 0] ?? null : null, advancedToggleMount = advancedToggle ? createAnchor("search-advanced-toggle") : null, fileSearchToggleMount = fileSearchToggle ? createAnchor("search-file-toggle") : null, searchControls = createManagedElement("div"), elems = {
      advancedPanel: advancedPanel?.inplace() ?? null,
      advancedToggle: advancedToggle?.inplace() ?? null,
      advancedToggleMount,
      categories: categories?.inplace() ?? null,
      categoryCells: categoryCells.map((cell) => cell.inplace()),
      categoryItems: categoryItems.map((item) => item.inplace()),
      categoryRows: categoryRows.map((row) => row.inplace()),
      categoryToggleMount,
      clearActionMount,
      clearButton: clearButton?.inplace() ?? null,
      fileSearch: fileSearch?.inplace() ?? null,
      fileSearchToggle: fileSearchToggle?.inplace() ?? null,
      fileSearchToggleMount,
      form: form.inplace(),
      mount,
      optionLinks: optionLinks?.inplace() ?? null,
      optionLinkItems: optionLinkItems.map((link) => link.inplace()),
      searchActionMount,
      searchBox: standardSearchBox?.inplace() ?? searchControls,
      searchControls,
      searchInput: searchInput.inplace(),
      searchSubmit: searchSubmit.inplace()
    };
    (standardSearchBox ? elems.searchBox : elems.form).before(elems.mount), standardSearchBox && elems.searchBox.remove(), elems.searchInput.replaceWith(elems.searchControls), elems.searchControls.append(elems.searchInput), elems.searchSubmit.remove(), elems.clearButton && elems.clearActionMount && (elems.clearButton.remove(), elems.searchControls.append(elems.clearActionMount)), elems.searchControls.append(elems.searchActionMount), elems.categories && elems.optionLinks && elems.categoryToggleMount && (elems.optionLinks.after(elems.categories), elems.optionLinks.prepend(elems.categoryToggleMount)), elems.optionLinks && elems.advancedToggle && elems.advancedToggleMount && (elems.advancedToggle.after(elems.advancedToggleMount), elems.advancedToggle.remove()), elems.optionLinks && elems.fileSearchToggle && elems.fileSearchToggleMount && (elems.fileSearchToggle.after(elems.fileSearchToggleMount), elems.fileSearchToggle.remove()), elems.fileSearch?.remove();
    let formInsideSearchBox = standardSearchBox?.one("form")?.sameNode(form) ?? !1, formId = form.attribute("id") || "ehpeek-search-form", categoryColors = categoryItems.map(
      (item) => ["ct1", "ct2", "ct3", "ct4", "ct5", "ct6", "ct7", "ct8", "ct9", "cta"].find((name) => item.hasClass(name)) ?? null
    );
    return { data: {
      clearLabel: clearButton ? actionLabel(clearButton) : null,
      hasClear: elems.clearButton !== null && elems.clearActionMount !== null,
      searchLabel: actionLabel(searchSubmit)
    }, elems, handle: {
      /** Reflows original Search controls into EhPeek's shared SearchPanel structure. */
      updateSearchPanelVisual(classes) {
        elems.searchActionMount.replaceClasses(classes.actionMount), elems.clearActionMount?.replaceClasses(classes.actionMount), elems.searchBox.replaceClasses(standardSearchBox ? classes.searchBox : classes.controls), formInsideSearchBox ? elems.form.removeAttributes("style").replaceClasses(classes.form) : (elems.form.setAttributes({ id: formId }), elems.searchInput.setAttributes({ form: formId }), elems.searchSubmit.setAttributes({ form: formId }), elems.clearButton?.setAttributes({ form: formId })), elems.searchControls.replaceClasses(classes.controls), elems.searchInput.removeAttributes("style").replaceClasses(classes.input), elems.categories?.replaceClasses(classes.categoryTable).setHidden(!0), elems.categoryRows.forEach((row) => row.replaceClasses(classes.categoryRow)), elems.categoryCells.forEach((cell) => cell.replaceClasses(classes.categoryCell)), elems.categoryItems.forEach((item, index) => item.replaceClasses(`${categoryColors[index] ? `${categoryColors[index]} ` : ""}${classes.category}`)), elems.optionLinks?.replaceClasses(classes.optionLinks), elems.optionLinkItems.forEach((link) => link.replaceClasses(classes.optionLink)), elems.advancedPanel?.replaceClasses(classes.advancedPanel), elems.fileSearch?.replaceClasses(classes.fileSearch).removeStyles("margin-top"), elems.searchSubmit.setHidden(!0), elems.clearButton?.setHidden(!0);
      },
      /** Controls the original category table from EhPeek's category toggle. */
      updateCategoryVisibility(open) {
        elems.categories?.setHidden(!open), elems.categories?.setAttributes({ "aria-hidden": String(!open) });
      },
      /** Activates E-H's original Search submit control. */
      activateSearch() {
        elems.searchSubmit.click();
      },
      /** Clears only the Search text without invoking E-H's page-navigation reset. */
      clearSearchText() {
        elems.searchInput.setInputValue(""), elems.searchInput.dispatchInput(), elems.searchInput.focus();
      },
      toggleAdvancedOptions() {
        elems.advancedToggle?.click();
      },
      toggleFileSearch() {
        elems.fileSearchToggle?.click();
      }
    } };
  }
  function actionLabel(element) {
    return element.attribute("value") ?? element.text();
  }

  // src/eh/dom/topBar.ts
  function manageSettingsMenuMount() {
    let page = DomNode.from(document), thumbnailContainer = page.one("#gdt"), titleContainer = page.one("#gd2, h1"), topNav = page.one("#nb"), anchor = thumbnailContainer ?? titleContainer;
    if (topNav) {
      let item2 = createManagedElement("div");
      return topNav.inplace().append(item2), item2;
    }
    if (!anchor?.parent())
      return null;
    let item = createManagedElement("div");
    item.styles({ "text-align": "right" });
    let managedAnchor = anchor.inplace();
    return thumbnailContainer ? managedAnchor.before(item) : managedAnchor.after(item), item;
  }
  function manageTopBar() {
    let mount = createAnchor("top-bar");
    if (!mount)
      return null;
    let original = DomNode.from(document).one("#nb"), links = original?.all("a[href]") ?? [];
    if (!original || links.length === 0)
      return null;
    let data = {
      favoritesHref: new URL("/favorites.php", window.location.href).href,
      homeHref: links[0]?.attribute("href") ?? "/"
    }, elems = {
      mount,
      navItems: links.map((link) => link.move())
    };
    return original.inplace().replaceWith(elems.mount), {
      data,
      elems,
      handle: {
        /** Normalizes original links moved into EhPeek's icon-based TopBar. */
        updateNavItemVisual(className2) {
          elems.navItems.forEach(
            (item) => item.removeAttributes("id").replaceClasses(className2).removeAllStyles()
          );
        }
      }
    };
  }

  // src/components/PointerGesture.tsx
  var DEFAULT_TAP_MOVE_THRESHOLD_PX = 8, DEFAULT_DRAG_START_THRESHOLD_PX = 8, DEFAULT_DRAG_INTENT_RATIO = 1, MOUSE_POINTER_ID = -1, PointerGesture = class {
    constructor(target, callbacks) {
      __publicField(this, "pinchPointers", /* @__PURE__ */ new Map());
      __publicField(this, "drag", null);
      __publicField(this, "suppressClick", !1);
      __publicField(this, "suppressClickPoint", null);
      __publicField(this, "suppressClickTimer", null);
      __publicField(this, "pinch", null);
      __publicField(this, "onDragStart", (event) => {
        this.drag?.canDrag && event.preventDefault();
      });
      __publicField(this, "onClick", (event) => {
        let point = this.suppressClickPoint, targetInside = event.target instanceof Node && this.target.contains(event.target), nearReleasePoint = point !== null && Math.hypot(event.clientX - point.clientX, event.clientY - point.clientY) <= 24;
        !this.suppressClick || !targetInside && !nearReleasePoint || (this.clearClickSuppression(), event.preventDefault(), event.stopImmediatePropagation());
      });
      __publicField(this, "onContextMenu", () => {
        this.drag?.active || (this.cancel(), this.clearPinch());
      });
      __publicField(this, "onPointerDown", (event) => {
        if (event.pointerType === "mouse" && event.button !== 0 || this.trackPinchPointerDown(event) || this.pinch || this.drag)
          return;
        let callbacks = this.callbacks(), canDrag = callbacks.shouldCaptureDrag?.(event) ?? !0;
        (canDrag || (callbacks.shouldObserveTap?.(event) ?? !1)) && (this.start(event.pointerId, event.pointerType, event.clientX, event.clientY, event, canDrag), event.pointerType === "mouse" && this.addMouseListeners());
      });
      __publicField(this, "onMouseDown", (event) => {
        event.button !== 0 || typeof PointerEvent < "u" || this.drag || !(this.callbacks().shouldCaptureDrag?.(event) ?? !0) || (this.start(MOUSE_POINTER_ID, "mouse", event.clientX, event.clientY, event, !0), this.addMouseListeners());
      });
      __publicField(this, "onPointerMove", (event) => {
        !this.drag || event.pointerId !== this.drag.pointerId || this.drag.pointerType === "mouse" || this.move(event.clientX, event.clientY, event);
      });
      __publicField(this, "onPointerUp", (event) => {
        !this.drag || event.pointerId !== this.drag.pointerId || (this.finish(event.clientX, event.clientY, event), this.releasePinchPointer(event));
      });
      __publicField(this, "onPointerCancel", (event) => {
        !this.drag || event.pointerId !== this.drag.pointerId || (this.finish(event.clientX, event.clientY, event, !0), this.releasePinchPointer(event));
      });
      __publicField(this, "onMouseMove", (event) => {
        !this.drag || this.drag.pointerType !== "mouse" || this.move(event.clientX, event.clientY, event);
      });
      __publicField(this, "onMouseUp", (event) => {
        !this.drag || this.drag.pointerType !== "mouse" || this.finish(event.clientX, event.clientY, event);
      });
      __publicField(this, "onPinchPointerMove", (event) => {
        if (!this.pinch || !this.pinchPointers.has(event.pointerId))
          return;
        this.pinchPointers.set(event.pointerId, {
          clientX: event.clientX,
          clientY: event.clientY
        });
        let snapshot = this.pinchSnapshot();
        snapshot && (this.callbacks().onPinchMove?.({
          ...snapshot,
          scale: snapshot.distance / this.pinch.startDistance
        }, event), event.preventDefault());
      });
      __publicField(this, "onPinchPointerEnd", (event) => {
        this.pinchPointers.has(event.pointerId) && (this.pinchPointers.delete(event.pointerId), !(!this.pinch || this.pinchPointers.size >= 2) && (this.callbacks().onPinchEnd?.(), this.clearPinch(), event.preventDefault()));
      });
      this.target = target, this.callbacks = callbacks, this.setDragging(!1), target.addEventListener("pointerdown", this.onPointerDown), target.addEventListener("mousedown", this.onMouseDown), target.addEventListener("dragstart", this.onDragStart), target.addEventListener("contextmenu", this.onContextMenu);
    }
    dispose() {
      this.drag && this.releaseCapture(this.drag), this.drag = null, this.setDragging(!1), this.clearPinch(), this.removePointerListeners(), this.removeMouseListeners(), this.target.removeEventListener("pointerdown", this.onPointerDown), this.target.removeEventListener("mousedown", this.onMouseDown), this.target.removeEventListener("dragstart", this.onDragStart), this.target.removeEventListener("contextmenu", this.onContextMenu), this.clearClickSuppression();
    }
    isDragging() {
      return this.drag?.active === !0;
    }
    cancel() {
      this.drag && (this.releaseCapture(this.drag), this.drag = null, this.setDragging(!1), this.removePointerListeners(), this.removeMouseListeners());
    }
    start(pointerId, pointerType, clientX, clientY, event, canDrag) {
      this.drag = {
        active: !1,
        canDrag,
        captureTarget: null,
        pointerId,
        pointerType,
        startClientX: clientX,
        startClientY: clientY,
        lastClientX: clientX,
        lastClientY: clientY,
        lastMoveTime: event.timeStamp,
        startTarget: event.target,
        tapCancelled: !1,
        velocityY: 0
      };
      let captureTarget = event.target;
      canDrag && "pointerId" in event && typeof captureTarget?.setPointerCapture == "function" && (captureTarget.setPointerCapture(pointerId), this.drag.captureTarget = captureTarget), this.addPointerListeners();
    }
    move(clientX, clientY, event) {
      let drag = this.drag;
      if (!drag)
        return;
      let dx = clientX - drag.startClientX, dy = clientY - drag.startClientY, tapMoveThreshold = this.tapMoveThreshold();
      if ((Math.abs(dx) >= tapMoveThreshold || Math.abs(dy) >= tapMoveThreshold) && (drag.tapCancelled = !0), !drag.canDrag) {
        this.updateLastMove(drag, clientX, clientY, event);
        return;
      }
      let intent = this.dragIntent(dx, dy);
      if (!drag.active && intent === "cancel") {
        this.cancel();
        return;
      }
      if (!drag.active && intent !== "start") {
        this.updateLastMove(drag, clientX, clientY, event);
        return;
      }
      drag.active || this.activateDrag(drag, event);
      let elapsed = Math.max(1, event.timeStamp - drag.lastMoveTime);
      drag.velocityY = (clientY - drag.lastClientY) / elapsed, drag.lastClientX = clientX, drag.lastClientY = clientY, drag.lastMoveTime = event.timeStamp, this.callbacks().onMove?.({
        pointerId: drag.pointerId,
        clientX,
        clientY,
        dx: clientX - drag.startClientX,
        dy: clientY - drag.startClientY,
        velocityY: drag.velocityY
      }, event), event.preventDefault();
    }
    finish(clientX, clientY, event, cancelled = !1) {
      let drag = this.drag;
      if (!drag)
        return;
      this.drag = null, this.setDragging(!1), this.releaseCapture(drag), this.removePointerListeners(), this.removeMouseListeners();
      let info = {
        pointerId: drag.pointerId,
        clientX,
        clientY,
        dx: clientX - drag.startClientX,
        dy: clientY - drag.startClientY,
        velocityY: drag.velocityY
      }, tapMoveThreshold = this.tapMoveThreshold(), isTap = !drag.tapCancelled && Math.abs(info.dx) < tapMoveThreshold && Math.abs(info.dy) < tapMoveThreshold;
      if (!cancelled && !drag.active && isTap && this.callbacks().onTap?.({
        ...info,
        startTarget: drag.startTarget
      }, event), drag.active) {
        if (cancelled) {
          this.callbacks().onEnd?.({
            ...info,
            dx: 0,
            dy: 0,
            velocityY: 0
          }, event);
          return;
        }
        this.suppressNextClick(info.clientX, info.clientY), this.callbacks().onEnd?.(info, event);
      }
    }
    addPointerListeners() {
      document.addEventListener("pointermove", this.onPointerMove, !0), document.addEventListener("pointerup", this.onPointerUp, !0), document.addEventListener("pointercancel", this.onPointerCancel, !0);
    }
    removePointerListeners() {
      document.removeEventListener("pointermove", this.onPointerMove, !0), document.removeEventListener("pointerup", this.onPointerUp, !0), document.removeEventListener("pointercancel", this.onPointerCancel, !0);
    }
    addMouseListeners() {
      window.addEventListener("mousemove", this.onMouseMove, !0), window.addEventListener("mouseup", this.onMouseUp, !0);
    }
    removeMouseListeners() {
      window.removeEventListener("mousemove", this.onMouseMove, !0), window.removeEventListener("mouseup", this.onMouseUp, !0);
    }
    trackPinchPointerDown(event) {
      let callbacks = this.callbacks();
      if (!callbacks.onPinchStart || event.pointerType === "mouse" || (this.pinchPointers.set(event.pointerId, {
        clientX: event.clientX,
        clientY: event.clientY
      }), this.pinch || this.pinchPointers.size !== 2))
        return !1;
      let snapshot = this.pinchSnapshot();
      return snapshot ? callbacks.onPinchStart(snapshot, event) ? (this.cancel(), this.pinch = {
        startDistance: snapshot.distance
      }, this.addPinchListeners(), event.preventDefault(), event.stopPropagation(), !0) : (this.pinchPointers.delete(event.pointerId), !1) : !1;
    }
    addPinchListeners() {
      document.addEventListener("pointermove", this.onPinchPointerMove, !0), document.addEventListener("pointerup", this.onPinchPointerEnd, !0), document.addEventListener("pointercancel", this.onPinchPointerEnd, !0);
    }
    removePinchListeners() {
      document.removeEventListener("pointermove", this.onPinchPointerMove, !0), document.removeEventListener("pointerup", this.onPinchPointerEnd, !0), document.removeEventListener("pointercancel", this.onPinchPointerEnd, !0);
    }
    clearPinch() {
      this.pinch = null, this.pinchPointers.clear(), this.removePinchListeners();
    }
    releasePinchPointer(event) {
      this.pinch || this.pinchPointers.delete(event.pointerId);
    }
    pinchSnapshot() {
      let points = Array.from(this.pinchPointers.values()), first = points[0], second = points[1];
      if (!first || !second)
        return null;
      let dx = second.clientX - first.clientX, dy = second.clientY - first.clientY;
      return {
        clientX: (first.clientX + second.clientX) / 2,
        clientY: (first.clientY + second.clientY) / 2,
        distance: Math.hypot(dx, dy)
      };
    }
    tapMoveThreshold() {
      return this.callbacks().tapMoveThreshold ?? DEFAULT_TAP_MOVE_THRESHOLD_PX;
    }
    dragStartThreshold() {
      return this.callbacks().dragStartThreshold ?? DEFAULT_DRAG_START_THRESHOLD_PX;
    }
    dragIntentRatio() {
      return this.callbacks().dragIntentRatio ?? DEFAULT_DRAG_INTENT_RATIO;
    }
    dragAxis() {
      return this.callbacks().dragAxis ?? "any";
    }
    dragIntent(dx, dy) {
      let absX = Math.abs(dx), absY = Math.abs(dy), threshold = this.dragStartThreshold(), ratio = this.dragIntentRatio();
      return this.dragAxis() === "x" ? absY >= threshold && absY > absX ? "cancel" : absX >= threshold && absX >= absY * ratio ? "start" : "pending" : this.dragAxis() === "y" ? absX >= threshold && absX > absY ? "cancel" : absY >= threshold && absY >= absX * ratio ? "start" : "pending" : Math.hypot(dx, dy) >= threshold ? "start" : "pending";
    }
    activateDrag(drag, event) {
      drag.active = !0, this.setDragging(!0), drag.pointerType === "mouse" && window.getSelection()?.removeAllRanges(), this.callbacks().onStart?.({
        pointerId: drag.pointerId,
        clientX: drag.startClientX,
        clientY: drag.startClientY
      }, event), event.preventDefault();
    }
    updateLastMove(drag, clientX, clientY, event) {
      let elapsed = Math.max(1, event.timeStamp - drag.lastMoveTime);
      drag.velocityY = (clientY - drag.lastClientY) / elapsed, drag.lastClientX = clientX, drag.lastClientY = clientY, drag.lastMoveTime = event.timeStamp;
    }
    suppressNextClick(clientX, clientY) {
      this.suppressClick = !0, this.suppressClickPoint = {
        clientX,
        clientY
      }, window.addEventListener("click", this.onClick, !0), this.suppressClickTimer !== null && window.clearTimeout(this.suppressClickTimer), this.suppressClickTimer = window.setTimeout(() => {
        this.clearClickSuppression();
      }, 400);
    }
    clearClickSuppression() {
      this.suppressClick = !1, this.suppressClickPoint = null, window.removeEventListener("click", this.onClick, !0), this.suppressClickTimer !== null && (window.clearTimeout(this.suppressClickTimer), this.suppressClickTimer = null);
    }
    setDragging(dragging) {
      this.target.dataset.dragging = String(dragging);
    }
    releaseCapture(drag) {
      drag.captureTarget?.hasPointerCapture(drag.pointerId) && drag.captureTarget.releasePointerCapture(drag.pointerId);
    }
  };
  function createPointerGestureElement(target, callbacks) {
    let gesture = null;
    return createEffect(() => {
      let element = target();
      element && (gesture = new PointerGesture(element, callbacks), onCleanup(() => {
        gesture?.dispose(), gesture = null;
      }));
    }), () => gesture?.isDragging() ?? !1;
  }

  // src/components/Widgets/Icon.tsx
  var _tmpl$ = /* @__PURE__ */ template('<svg class="ehpeek-icon block flex-none"viewBox="0 0 24 24"stroke-linecap=round stroke-linejoin=round aria-hidden=true>'), _tmpl$2 = /* @__PURE__ */ template("<svg><path fill=currentColor stroke=none></svg>", !1, !0, !1), _tmpl$3 = /* @__PURE__ */ template("<svg><path></svg>", !1, !0, !1);
  function Icon(props) {
    let definition = createMemo(() => ICON_DEFINITIONS[props.name]), filled = createMemo(() => definition().solid || definition().fillable && props.filled);
    return (() => {
      var _el$ = _tmpl$();
      return insert(_el$, createComponent(For, {
        get each() {
          return definition().filledPaths;
        },
        children: (path) => (() => {
          var _el$2 = _tmpl$2();
          return setAttribute(_el$2, "d", path), _el$2;
        })()
      }), null), insert(_el$, createComponent(For, {
        get each() {
          return definition().paths;
        },
        children: (path) => (() => {
          var _el$3 = _tmpl$3();
          return setAttribute(_el$3, "d", path), _el$3;
        })()
      }), null), createRenderEffect((_p$) => {
        var _v$ = props.size ?? 24, _v$2 = props.size ?? 24, _v$3 = filled() ? "currentColor" : "none", _v$4 = filled() ? "none" : "currentColor", _v$5 = props.strokeWidth ?? 2, _v$6 = props.name;
        return _v$ !== _p$.e && setAttribute(_el$, "width", _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$, "height", _p$.t = _v$2), _v$3 !== _p$.a && setAttribute(_el$, "fill", _p$.a = _v$3), _v$4 !== _p$.o && setAttribute(_el$, "stroke", _p$.o = _v$4), _v$5 !== _p$.i && setAttribute(_el$, "stroke-width", _p$.i = _v$5), _v$6 !== _p$.n && setAttribute(_el$, "data-icon-name", _p$.n = _v$6), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0,
        n: void 0
      }), _el$;
    })();
  }
  var ICON_DEFINITIONS = {
    "arrow-left": {
      paths: ["M19 12H5", "m12 19-7-7 7-7"]
    },
    "arrow-right": {
      paths: ["M5 12h14", "m12 5 7 7-7 7"]
    },
    "arrow-up": {
      paths: ["m5 12 7-7 7 7", "M12 5v14"]
    },
    "arrows-horizontal": {
      paths: ["M3 12h18", "m7 8-4 4 4 4", "m17 8 4 4-4 4"]
    },
    "arrows-vertical": {
      paths: ["M12 3v18", "m8 7 4-4 4 4", "m8 17 4 4 4-4"]
    },
    check: {
      paths: ["m5 12.5 4.25 4.25L19.5 6.5"]
    },
    "chevron-left": {
      paths: ["m15 18-6-6 6-6"]
    },
    "chevron-right": {
      paths: ["m9 18 6-6-6-6"]
    },
    close: {
      paths: ["M6 6l12 12", "M18 6 6 18"]
    },
    download: {
      paths: ["M12 3v12", "m7 10 5 5 5-5", "M5 21h14"]
    },
    "external-link": {
      paths: ["M14 4h6v6", "m20 4-9 9", "M20 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5"]
    },
    fullscreen: {
      paths: ["M8 3H3v5", "M16 3h5v5", "M3 16v5h5", "M21 16v5h-5"]
    },
    "fullscreen-exit": {
      paths: ["M8 3v5H3", "M16 3v5h5", "M3 16h5v5", "M21 16h-5v5"]
    },
    heart: {
      fillable: !0,
      paths: ["M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78L12 21.23l8.84-8.84a5.5 5.5 0 0 0 0-7.78Z"]
    },
    home: {
      paths: ["m3 10.5 9-7.5 9 7.5", "M5.5 9v11h13V9", "M9.5 20v-6h5v6"]
    },
    menu: {
      paths: ["M12 4a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm0 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"],
      solid: !0
    },
    "panda-peek": {
      filledPaths: ["M7.2 3.2a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8Z", "M16.8 3.2a2.4 2.4 0 1 0 0 4.8 2.4 2.4 0 0 0 0-4.8Z", "M7.6 9.8c.5-1.2 1.6-1.8 2.6-1.3s1.3 1.8.8 3-1.6 1.8-2.6 1.3-1.3-1.8-.8-3Z", "M13.8 8.5c1-.5 2.1.1 2.6 1.3s.2 2.5-.8 3-2.1-.1-2.6-1.3-.2-2.5.8-3Z", "M10.9 13.6c0-.6.5-.9 1.1-.9s1.1.3 1.1.9-.5 1-1.1 1-1.1-.4-1.1-1Z", "M5.2 13.7a2.8 1.9 0 1 0 0 3.8 2.8 1.9 0 0 0 0-3.8Z", "M18.8 14.1a2.8 1.9 0 1 0 0 3.8 2.8 1.9 0 0 0 0-3.8Z"],
      paths: ["M5 17c-.8-6.4 2.1-10.8 7-10.8s7.8 4.4 7 10.8", "M12 14.6v.7c0 .7-.6 1.2-1.3 1.2m1.3-1.2c0 .7.6 1.2 1.3 1.2", "M2 17h20"]
    },
    search: {
      paths: ["M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14Z", "m16.2 16.2 4.3 4.3"]
    },
    settings: {
      paths: ["M12.2 2h-.4a2 2 0 0 0-2 2v.2a2 2 0 0 1-1 1.7l-.4.3a2 2 0 0 1-2 0l-.2-.1a2 2 0 0 0-2.7.7l-.2.4A2 2 0 0 0 4 9.9l.2.1a2 2 0 0 1 1 1.7v.6a2 2 0 0 1-1 1.7l-.2.1a2 2 0 0 0-.7 2.7l.2.4a2 2 0 0 0 2.7.7l.2-.1a2 2 0 0 1 2 0l.4.3a2 2 0 0 1 1 1.7v.2a2 2 0 0 0 2 2h.4a2 2 0 0 0 2-2v-.2a2 2 0 0 1 1-1.7l.4-.3a2 2 0 0 1 2 0l.2.1a2 2 0 0 0 2.7-.7l.2-.4a2 2 0 0 0-.7-2.7l-.2-.1a2 2 0 0 1-1-1.7v-.6a2 2 0 0 1 1-1.7l.2-.1a2 2 0 0 0 .7-2.7l-.2-.4a2 2 0 0 0-2.7-.7l-.2.1a2 2 0 0 1-2 0l-.4-.3a2 2 0 0 1-1-1.7V4a2 2 0 0 0-2-2Z", "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"]
    },
    star: {
      fillable: !0,
      paths: ["m12 2.75 2.85 5.77 6.37.93-4.61 4.49 1.09 6.34L12 17.24 6.3 20.23l1.09-6.34-4.61-4.49 6.37-.93Z"]
    }
  };

  // src/components/WelcomeIcon.tsx
  var _tmpl$4 = /* @__PURE__ */ template('<div role=status aria-live=polite><span class="inline-flex items-center justify-center gap-md ehp-color-site-text"><span class="inline-block box-border w-sm h-sm animate-spin rounded-full border-4 border-solid ehp-color-spinner"aria-hidden=true></span><span>');
  function WelcomeIcon(props) {
    let label = () => props.label ?? texts_default.reader.loading, placementClass = () => props.embedded ? "relative w-full border-0 bg-transparent px-lg py-md" : "fixed left-1/2 top-1/2 z-[1200] -translate-x-1/2 -translate-y-1/2 rounded-lg border ehp-color-site-border bg-[var(--color-loading)] px-xl py-lg shadow-[0_6px_20px_var(--color-shadow-floating)]";
    return (() => {
      var _el$ = _tmpl$4(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling;
      return insert(_el$, (() => {
        var _c$ = memo(() => props.showIcon !== !1);
        return () => _c$() ? createComponent(Icon, {
          name: "panda-peek",
          size: 80,
          strokeWidth: 1.6
        }) : null;
      })(), _el$2), insert(_el$4, label), createRenderEffect((_p$) => {
        var _v$ = `${placementClass()} flex select-none flex-col items-center gap-lg ehp-color-site-accent pointer-events-none`, _v$2 = label();
        return _v$ !== _p$.e && className(_el$, _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$, "aria-label", _p$.t = _v$2), _p$;
      }, {
        e: void 0,
        t: void 0
      }), _el$;
    })();
  }

  // src/components/Widgets/Loading.tsx
  function LoadingOverlay(props) {
    return createComponent(Show, {
      get when() {
        return props.visible;
      },
      get children() {
        return createComponent(WelcomeIcon, {
          get label() {
            return props.label;
          }
        });
      }
    });
  }

  // src/components/Widgets/SwipeIndicator.tsx
  var _tmpl$5 = /* @__PURE__ */ template('<div class="ehpeek-swipe-indicator fixed top-1/2 z-overlay flex w-42px h-108px items-center justify-center border border-[var(--color-site-swipe-border)] rounded-full bg-[var(--color-site-swipe-background)] text-[var(--color-site-text)] shadow-[0_6px_20px_var(--color-shadow-floating)] pointer-events-none select-none transition-opacity duration-120 ease-in-out"style=backdrop-filter:blur(8px)>'), HIDE_PROGRESS = 1e-3;
  function SwipeIndicator(props) {
    let progress = createMemo(() => Math.min(1, Math.max(0, props.state.progress))), hidden = createMemo(() => progress() <= HIDE_PROGRESS), pull = createMemo(() => Math.round(48 * progress())), offset = createMemo(() => props.state.direction === "left" ? 42 - pull() : pull() - 42), iconName = createMemo(() => props.state.blocked ? "close" : props.state.direction === "left" ? "chevron-left" : "chevron-right");
    return (() => {
      var _el$ = _tmpl$5();
      return insert(_el$, createComponent(Icon, {
        get name() {
          return iconName();
        },
        size: 36
      })), createRenderEffect((_p$) => {
        var _v$ = hidden() ? "true" : "false", _v$2 = hidden() ? "none" : "flex", _v$3 = props.state.direction === "right" ? "6px" : "", _v$4 = hidden() ? "0" : String(0.35 + progress() * 0.65), _v$5 = props.state.direction === "left" ? "6px" : "", _v$6 = `translate(${offset()}px, -50%)`;
        return _v$ !== _p$.e && setAttribute(_el$, "aria-hidden", _p$.e = _v$), _v$2 !== _p$.t && setStyleProperty(_el$, "display", _p$.t = _v$2), _v$3 !== _p$.a && setStyleProperty(_el$, "left", _p$.a = _v$3), _v$4 !== _p$.o && setStyleProperty(_el$, "opacity", _p$.o = _v$4), _v$5 !== _p$.i && setStyleProperty(_el$, "right", _p$.i = _v$5), _v$6 !== _p$.n && setStyleProperty(_el$, "transform", _p$.n = _v$6), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0,
        n: void 0
      }), _el$;
    })();
  }

  // src/components/Enhance/EnhanceSearchGrids.tsx
  var SWIPE_MIN_DISTANCE = 96, SWIPE_INTENT_DISTANCE = 28, HORIZONTAL_INTENT_RATIO = 2.2, SWIPE_MAX_VERTICAL_RATIO = 0.38;
  function EnhanceSearchGrids(props) {
    let [gestureTarget, setGestureTarget] = createSignal(null), [loading, setLoading] = createSignal(!1), [swipeIndicatorState, setSwipeIndicatorState] = createSignal({
      blocked: !1,
      direction: "left",
      progress: 0
    }), source = untrack(() => props.source), navigationLoading = !1, swipeUrlForDelta = (dx) => dx < 0 ? source.data.nextUrl : source.data.previousUrl, hideSwipeIndicator = () => {
      setSwipeIndicatorState((current) => ({
        ...current,
        blocked: !1,
        progress: 0
      }));
    }, updateSwipeIndicator = (info) => {
      setSwipeIndicatorState({
        blocked: !swipeUrlForDelta(info.dx),
        direction: info.dx < 0 ? "left" : "right",
        progress: Math.min(1, Math.max(0, (Math.abs(info.dx) - SWIPE_INTENT_DISTANCE) / (SWIPE_MIN_DISTANCE - SWIPE_INTENT_DISTANCE)))
      });
    }, navigate = async (url) => {
      if (!navigationLoading) {
        navigationLoading = !0, setLoading(!0), source.handle.updateSearchLoading(!0);
        try {
          await source.handle.loadSearchPage(url);
          let nextSource = manageSearchResults();
          if (!nextSource)
            throw new Error(texts_default.errors.searchPageContentNotFound);
          source = nextSource, props.onPageChange(source), source.handle.ensureSearchSwipeInput(), setGestureTarget(source.elems.resultList.Component()), source.handle.scrollSearchPageToInput();
        } catch (error) {
          console.error("[ehpeek]", error);
        } finally {
          navigationLoading = !1, setLoading(!1), source.handle.updateSearchLoading(!1);
        }
      }
    }, navigateBySwipe = (info, event) => {
      let absX = Math.abs(info.dx), absY = Math.abs(info.dy);
      if (absX < SWIPE_MIN_DISTANCE || absY > absX * SWIPE_MAX_VERTICAL_RATIO)
        return;
      let url = swipeUrlForDelta(info.dx);
      url && (event.preventDefault(), navigate(url));
    }, onNavigation = (url) => {
      navigate(url);
    };
    return onMount(() => {
      source.handle.ensureSearchSwipeInput(), setGestureTarget(source.elems.resultList.Component()), onCleanup(source.handle.interceptSearchNavigation(onNavigation));
    }), createPointerGestureElement(gestureTarget, () => ({
      onStart: () => {
        hideSwipeIndicator();
      },
      onMove: updateSwipeIndicator,
      onEnd: (info, event) => {
        navigateBySwipe(info, event), hideSwipeIndicator();
      },
      dragAxis: "x",
      dragIntentRatio: HORIZONTAL_INTENT_RATIO,
      dragStartThreshold: SWIPE_INTENT_DISTANCE
    })), [createComponent(SwipeIndicator, {
      get state() {
        return swipeIndicatorState();
      }
    }), createComponent(LoadingOverlay, {
      get label() {
        return texts_default.reader.loading;
      },
      get visible() {
        return loading();
      }
    })];
  }

  // src/components/Enhance/ScrollPageBar.tsx
  var _tmpl$6 = /* @__PURE__ */ template('<div class="w-full mb-xs text-center textsize-sm">'), _tmpl$22 = /* @__PURE__ */ template('<a class="flex !w-sm !h-sm coarse:!w-md coarse:!h-md items-center justify-center box-border !p-0 rounded-sm coarse:rounded-md !border textsize-sm font-inherit no-underline hover:no-underline active:no-underline !border-transparent !bg-transparent !text-[var(--color-site-text)] visited:!text-[var(--color-site-text)] hover:!bg-[var(--color-site-item-hover)] hover:!text-[var(--color-site-text)] active:!text-[var(--color-site-text)]">'), _tmpl$32 = /* @__PURE__ */ template('<td class="!w-sm !h-sm coarse:!w-md coarse:!h-md !p-0 rounded-sm coarse:rounded-md cursor-pointer text-center align-middle select-none">'), _tmpl$42 = /* @__PURE__ */ template("<span>"), _tmpl$52 = /* @__PURE__ */ template('<td class="!w-sm !h-sm coarse:!w-md coarse:!h-md !p-0 rounded-sm coarse:rounded-md cursor-pointer text-center align-middle select-none cursor-default"><span class="flex !w-sm !h-sm coarse:!w-md coarse:!h-md items-center justify-center box-border !p-0 rounded-sm coarse:rounded-md !border textsize-sm font-inherit no-underline hover:no-underline active:no-underline !border-transparent !bg-transparent !text-[var(--color-site-text)] visited:!text-[var(--color-site-text)] hover:!bg-[var(--color-site-item-hover)] hover:!text-[var(--color-site-text)] active:!text-[var(--color-site-text)] invisible">'), _tmpl$62 = /* @__PURE__ */ template('<div><table class="border-separate border-spacing-4px coarse:border-spacing-6px"><tbody><tr>'), _tmpl$7 = /* @__PURE__ */ template('<span class="flex !w-sm !h-sm coarse:!w-md coarse:!h-md items-center justify-center box-border !p-0 rounded-sm coarse:rounded-md !border textsize-sm font-inherit no-underline hover:no-underline active:no-underline !border-transparent !bg-[color-mix(in_srgb,var(--color-site-page)_82%,black)] !text-[var(--color-site-text)]"aria-current=page>'), DRAG_PIXEL_STEP = 18;
  var PAGE_BAR_LINK_CLASS = "flex !w-sm !h-sm coarse:!w-md coarse:!h-md items-center justify-center box-border !p-0 rounded-sm coarse:rounded-md !border textsize-sm font-inherit no-underline hover:no-underline active:no-underline";
  var PAGE_BAR_CURRENT_COLOR_CLASS = "!border-transparent !bg-[color-mix(in_srgb,var(--color-site-page)_82%,black)] !text-[var(--color-site-text)]", PAGE_BAR_DISABLED_COLOR_CLASS = "!border-transparent !bg-[color-mix(in_srgb,var(--color-site-page)_82%,black)] !text-[var(--color-site-text)] opacity-40 cursor-default";
  function GalleryPageDescription(props) {
    return (() => {
      var _el$ = _tmpl$6();
      return insert(_el$, () => props.text), _el$;
    })();
  }
  function ScrollPageBar(props) {
    let maxIndex = createMemo(() => Math.max(0, props.maxIndex)), currentIndex = createMemo(() => clamp(props.currentIndex, 0, maxIndex())), [visibleCenterIndex, setVisibleCenterIndex] = createSignal(untrack(currentIndex)), gestureHost, dragStartVisibleCenterIndex = untrack(visibleCenterIndex), draggable = () => maxIndex() + 1 > 7, slots = createMemo(() => pageSlots(visibleCenterIndex(), maxIndex())), firstSlotIndex = createMemo(() => slots()[0] ?? currentIndex()), lastSlotIndex = createMemo(() => slots()[slots().length - 1] ?? currentIndex()), currentBeforeWindow = () => currentIndex() < firstSlotIndex(), currentAfterWindow = () => currentIndex() > lastSlotIndex(), scrollTargetForIndex = (pageIndex) => pageIndex === currentIndex() - 1 || pageIndex === maxIndex() ? "bottom" : "top";
    createEffect(() => {
      setVisibleCenterIndex(currentIndex());
    });
    let linkCell = (text, pageIndex, itemState = () => "link") => {
      let resolvedText = () => typeof text == "function" ? text() : text, resolvedPageIndex = () => typeof pageIndex == "function" ? pageIndex() : pageIndex;
      return (() => {
        var _el$2 = _tmpl$32();
        return insert(_el$2, createComponent(Show, {
          get when() {
            return itemState() === "link";
          },
          get fallback() {
            return (() => {
              var _el$4 = _tmpl$42();
              return insert(_el$4, resolvedText), createRenderEffect((_p$) => {
                var _v$ = `${PAGE_BAR_LINK_CLASS} ${itemState() === "current" ? PAGE_BAR_CURRENT_COLOR_CLASS : PAGE_BAR_DISABLED_COLOR_CLASS}`, _v$2 = itemState() === "current" ? "page" : void 0, _v$3 = itemState() === "disabled" ? "true" : void 0;
                return _v$ !== _p$.e && className(_el$4, _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$4, "aria-current", _p$.t = _v$2), _v$3 !== _p$.a && setAttribute(_el$4, "aria-disabled", _p$.a = _v$3), _p$;
              }, {
                e: void 0,
                t: void 0,
                a: void 0
              }), _el$4;
            })();
          },
          get children() {
            var _el$3 = _tmpl$22();
            return _el$3.$$click = (event) => {
              event.preventDefault(), event.stopPropagation(), props.onNavigate(resolvedPageIndex(), scrollTargetForIndex(resolvedPageIndex()));
            }, setAttribute(_el$3, "draggable", !1), insert(_el$3, resolvedText), createRenderEffect(() => setAttribute(_el$3, "href", props.urlForIndex(resolvedPageIndex()))), _el$3;
          }
        })), _el$2;
      })();
    }, emptyCell = () => (() => {
      var _el$5 = _tmpl$52(), _el$6 = _el$5.firstChild;
      return _el$5;
    })();
    return createPointerGestureElement(() => gestureHost, () => ({
      shouldCaptureDrag: draggable,
      dragAxis: "x",
      onStart: () => {
        dragStartVisibleCenterIndex = visibleCenterIndex();
      },
      onMove: (info) => {
        if (Math.abs(info.dx) < Math.abs(info.dy))
          return;
        let nextIndex = clamp(dragStartVisibleCenterIndex - acceleratedPageOffset(info.dx), 0, maxIndex());
        nextIndex !== visibleCenterIndex() && setVisibleCenterIndex(nextIndex);
      }
    })), (() => {
      var _el$7 = _tmpl$62(), _el$8 = _el$7.firstChild, _el$9 = _el$8.firstChild, _el$0 = _el$9.firstChild, _ref$ = gestureHost;
      return typeof _ref$ == "function" ? use(_ref$, _el$7) : gestureHost = _el$7, insert(_el$0, () => linkCell("<<", 0, () => currentIndex() === 0 ? "disabled" : "link"), null), insert(_el$0, createComponent(Show, {
        get when() {
          return currentBeforeWindow();
        },
        get fallback() {
          return emptyCell();
        },
        get children() {
          return linkCell(() => String(currentIndex() + 1), currentIndex, () => "current");
        }
      }), null), insert(_el$0, () => linkCell("<", () => Math.max(0, currentIndex() - 1), () => currentIndex() === 0 ? "disabled" : "link"), null), insert(_el$0, createComponent(For, {
        get each() {
          return slots();
        },
        children: (pageIndex) => {
          let itemState = createMemo(() => pageIndex === currentIndex() ? "current" : "link");
          return pageIndex !== null ? (() => {
            var _el$1 = _tmpl$32();
            return insert(_el$1, createComponent(Show, {
              get when() {
                return itemState() === "link";
              },
              get fallback() {
                return (() => {
                  var _el$11 = _tmpl$7();
                  return insert(_el$11, pageIndex + 1), _el$11;
                })();
              },
              get children() {
                var _el$10 = _tmpl$22();
                return _el$10.$$click = (event) => {
                  event.preventDefault(), event.stopPropagation(), props.onNavigate(pageIndex, scrollTargetForIndex(pageIndex));
                }, setAttribute(_el$10, "draggable", !1), insert(_el$10, pageIndex + 1), createRenderEffect(() => setAttribute(_el$10, "href", props.urlForIndex(pageIndex))), _el$10;
              }
            })), _el$1;
          })() : emptyCell();
        }
      }), null), insert(_el$0, () => linkCell(">", () => Math.min(maxIndex(), currentIndex() + 1), () => currentIndex() === maxIndex() ? "disabled" : "link"), null), insert(_el$0, createComponent(Show, {
        get when() {
          return currentAfterWindow();
        },
        get fallback() {
          return emptyCell();
        },
        get children() {
          return linkCell(() => String(currentIndex() + 1), currentIndex, () => "current");
        }
      }), null), insert(_el$0, () => linkCell(">>", maxIndex, () => currentIndex() === maxIndex() ? "disabled" : "link"), null), _el$7;
    })();
  }
  function pageSlots(visibleCenterIndex, maxIndex) {
    if (maxIndex + 1 <= 7)
      return range(0, maxIndex);
    let visibleStartIndex = clamp(visibleCenterIndex - 3, -1, maxIndex - 5);
    return range(visibleStartIndex, visibleStartIndex + 6).map((pageIndex) => pageIndex >= 0 && pageIndex <= maxIndex ? pageIndex : null);
  }
  function range(start, end) {
    let output = [];
    for (let index = start; index <= end; index += 1)
      output.push(index);
    return output;
  }
  function acceleratedPageOffset(dx) {
    let distance = Math.abs(dx), direction = dx > 0 ? 1 : -1, pages = Math.floor((distance / DRAG_PIXEL_STEP) ** 1.35);
    return direction * pages;
  }
  delegateEvents(["click"]);

  // src/components/Enhance/EnhanceThumbsGrids.tsx
  var SWIPE_MIN_DISTANCE2 = 96, SWIPE_INTENT_DISTANCE2 = 28, HORIZONTAL_INTENT_RATIO2 = 2.2, SWIPE_MAX_VERTICAL_RATIO2 = 0.38;
  function ThumbsGrids(props) {
    let pageBarSource = untrack(() => props.previewCache.current()), [pageBarCurrentIndex, setPageBarCurrentIndex] = createSignal(pageBarSource.data.currentIndex), [swipeIndicatorState, setSwipeIndicatorState] = createSignal({
      blocked: !1,
      direction: "left",
      progress: 0
    }), pageBarMaxIndex = () => props.previewCache.current().data.maxIndex, requestPreviewPage = (previewIndex, scrollToPageBar) => {
      let current = props.previewCache.current(), onLoadError = props.onLoadError;
      pageBarSource.handle.scrollPreviewPageBarIntoView(scrollToPageBar), previewIndex !== current.data.currentIndex && (setPageBarCurrentIndex(previewIndex), props.previewCache.select(previewIndex).then((next) => {
        untrack(() => props.previewCache.current()) === next && pageBarSource.handle.scrollPreviewPageBarIntoView(scrollToPageBar);
      }, (error) => {
        let cacheIndex = untrack(() => props.previewCache.current().data.currentIndex);
        setPageBarCurrentIndex((currentIndex) => currentIndex === previewIndex ? cacheIndex : currentIndex), onLoadError(error);
      }));
    }, swipeIndexForDelta = (dx) => {
      let current = props.previewCache.current().data, nextIndex = dx < 0 ? current.currentIndex + 1 : current.currentIndex - 1;
      return nextIndex < 0 || nextIndex > current.maxIndex ? null : nextIndex;
    }, hideSwipeIndicator = () => {
      setSwipeIndicatorState((current) => ({
        ...current,
        blocked: !1,
        progress: 0
      }));
    }, updateSwipeIndicator = (info) => {
      setSwipeIndicatorState({
        blocked: swipeIndexForDelta(info.dx) === null,
        direction: info.dx < 0 ? "left" : "right",
        progress: Math.min(1, Math.max(0, (Math.abs(info.dx) - SWIPE_INTENT_DISTANCE2) / (SWIPE_MIN_DISTANCE2 - SWIPE_INTENT_DISTANCE2)))
      });
    }, navigateBySwipe = (info, event) => {
      let absX = Math.abs(info.dx), absY = Math.abs(info.dy);
      if (absX < SWIPE_MIN_DISTANCE2 || absY > absX * SWIPE_MAX_VERTICAL_RATIO2)
        return;
      let previewIndex = swipeIndexForDelta(info.dx);
      previewIndex !== null && (event.preventDefault(), requestPreviewPage(previewIndex, info.dx < 0 ? "top" : "bottom"));
    }, actions = {
      gotoPreview: setPageBarCurrentIndex
    };
    createEffect(() => {
      props.actionsRef(actions);
    }), createEffect((previous) => {
      let current = props.previewCache.current();
      return setPageBarCurrentIndex(current.data.currentIndex), current.handle.ensurePreviewSwipeInput(), current !== previous && pageBarSource.handle.replacePreviewThumbs(current.elems.thumbItems), current;
    }, pageBarSource), createEffect(() => {
      pageBarSource.handle.updatePreviewLoading(props.previewCache.loading());
    }), onCleanup(() => {
      pageBarSource.handle.updatePreviewLoading(!1);
    }), createPointerGestureElement(() => pageBarSource.elems.thumbs?.Component() ?? null, () => ({
      onStart: hideSwipeIndicator,
      onMove: updateSwipeIndicator,
      onEnd: (info, event) => {
        navigateBySwipe(info, event), hideSwipeIndicator();
      },
      dragAxis: "x",
      dragIntentRatio: HORIZONTAL_INTENT_RATIO2,
      dragStartThreshold: SWIPE_INTENT_DISTANCE2
    })), pageBarSource.handle.installPreviewPageBars(), pageBarSource.elems.pageBarDescription?.mount(() => createComponent(GalleryPageDescription, {
      get text() {
        return props.previewCache.current().data.descriptionText;
      }
    }));
    for (let element of [pageBarSource.elems.pageBarTop, pageBarSource.elems.pageBarBottom])
      element && element.mount(() => createComponent(ScrollPageBar, {
        get currentIndex() {
          return pageBarCurrentIndex();
        },
        get maxIndex() {
          return pageBarMaxIndex();
        },
        onNavigate: requestPreviewPage,
        urlForIndex: (index) => previewUrlForIndex(index, props.previewCache.current().data.currentUrl)
      }));
    return onCleanup(() => {
      pageBarSource.elems.pageBarDescription?.remove(), pageBarSource.elems.pageBarTop?.remove(), pageBarSource.elems.pageBarBottom?.remove();
    }), [createComponent(LoadingOverlay, {
      get label() {
        return texts_default.reader.loading;
      },
      get visible() {
        return props.previewCache.loading();
      }
    }), createComponent(SwipeIndicator, {
      get state() {
        return swipeIndicatorState();
      }
    })];
  }

  // src/components/Enhance/ReadHistory.tsx
  var _tmpl$8 = /* @__PURE__ */ template("<button type=button><span>");
  function ReadButton(props) {
    let buttonClassName = () => props.variant === "touchGallery" ? "ehpeek-continue-reading ehpeek-touch-gallery-primary-button flex min-w-0 w-full h-full min-h-xl flex-col items-center justify-center gap-md py-md px-lg border-0 bg-transparent ehp-color-site-accent text-center uppercase [touch-action:manipulation] textsize-md font-700" : "ehpeek-continue-reading block box-border w-full max-w-full mt-xs min-h-sm py-xs px-sm rounded-sm border ehp-color-site-border bg-transparent ehp-color-site-accent hover:bg-[var(--color-site-accent-hover)] shadow-none cursor-pointer text-center font-sans textsize-md font-700 leading-[1.15]", detailClassName = () => props.variant === "touchGallery" ? "ehpeek-continue-reading-page block mt-2px ehp-color-site-accent textsize-md font-600 opacity-78 normal-case" : "ehpeek-continue-reading-page block mt-1px opacity-72 textsize-sm font-600";
    return (() => {
      var _el$ = _tmpl$8(), _el$2 = _el$.firstChild;
      return _el$.$$click = (event) => {
        event.preventDefault(), event.stopPropagation(), props.onClick();
      }, insert(_el$, (() => {
        var _c$ = memo(() => !!props.hasHistory);
        return () => _c$() ? texts_default.reader.continueReading : texts_default.reader.startReading;
      })(), _el$2), insert(_el$2, (() => {
        var _c$2 = memo(() => !!props.totalPages);
        return () => _c$2() ? `${props.currentPage}/${props.totalPages}` : String(props.currentPage);
      })()), createRenderEffect((_p$) => {
        var _v$ = buttonClassName(), _v$2 = detailClassName();
        return _v$ !== _p$.e && className(_el$, _p$.e = _v$), _v$2 !== _p$.t && className(_el$2, _p$.t = _v$2), _p$;
      }, {
        e: void 0,
        t: void 0
      }), _el$;
    })();
  }
  delegateEvents(["click"]);

  // src/state/readHistory.ts
  var HISTORY_KEY_PREFIX = "ehpeek:history:", HISTORY_LIMIT = 2e3, HISTORY_PRUNE_COUNT = 1e3, SAVE_DELAY_MS = 1e4, ReadHistorySession = class {
    constructor(baseRecord) {
      this.baseRecord = baseRecord;
      this.pending = null;
      this.lastSaved = null;
      this.timer = null;
      this.flush = () => {
        this.timer !== null && (window.clearTimeout(this.timer), this.timer = null), this.pending && (this.sameProgress(this.pending, this.lastSaved) || (saveReadHistory(this.pending), this.lastSaved = this.pending), this.pending = null);
      };
      this.onVisibilityChange = () => {
        document.visibilityState === "hidden" && this.flush();
      };
      window.addEventListener("pagehide", this.flush), document.addEventListener("visibilitychange", this.onVisibilityChange);
    }
    update(pageNum, totalPages) {
      if (!pageNum || pageNum <= 0)
        return;
      let nextRecord = {
        ...this.baseRecord,
        pageNum,
        totalPages,
        updatedAt: Date.now()
      };
      this.sameProgress(nextRecord, this.lastSaved) || (this.pending = nextRecord, this.schedule());
    }
    dispose() {
      this.flush(), window.removeEventListener("pagehide", this.flush), document.removeEventListener("visibilitychange", this.onVisibilityChange);
    }
    schedule() {
      this.timer === null && (this.timer = window.setTimeout(this.flush, SAVE_DELAY_MS));
    }
    sameProgress(left, right) {
      return !!(left && right && left.galleryId === right.galleryId && left.token === right.token && left.pageNum === right.pageNum && left.totalPages === right.totalPages);
    }
  };
  function loadReadHistory(galleryId, token) {
    return GM_getValue(historyKey(galleryId, token), null);
  }
  function saveReadHistory(record) {
    let key = historyKey(record.galleryId, record.token), exists = GM_getValue(key, null) !== null;
    if (GM_setValue(key, record), !exists) {
      let count = state.gallery.readHistoryCount.reload() + 1;
      state.gallery.readHistoryCount.set(count), count > HISTORY_LIMIT && pruneReadHistory();
    }
  }
  function historyKey(galleryId, token) {
    return `${HISTORY_KEY_PREFIX}${galleryId}:${token}`;
  }
  function pruneReadHistory() {
    let records = GM_listValues().filter((key) => key.startsWith(HISTORY_KEY_PREFIX)).map((key) => ({ key, record: GM_getValue(key, null) })).filter((entry) => entry.record !== null).sort((left, right) => left.record.updatedAt - right.record.updatedAt);
    for (let entry of records.slice(0, HISTORY_PRUNE_COUNT))
      GM_deleteValue(entry.key);
    state.gallery.readHistoryCount.set(Math.max(0, records.length - HISTORY_PRUNE_COUNT));
  }

  // src/components/Enhance/SearchHistory.tsx
  var _tmpl$9 = /* @__PURE__ */ template('<section class="ehpeek-search-history fixed z-ui flex box-border max-h-[60dvh] flex-col overflow-hidden overflow-y-auto overscroll-contain rounded-md border ehp-color-site-border ehp-color-site-elevated ehp-color-site-text font-sans"role=list>'), _tmpl$23 = /* @__PURE__ */ template('<div class="flex min-w-0 flex-none items-stretch border-0 border-b ehp-color-site-border-subtle-b last:border-b-0"role=listitem><button type=button></button><button type=button class="appearance-none inline-flex w-60px min-h-lg flex-none items-center justify-center border-0 border-l ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text textsize-xl font-inherit leading-1 cursor-pointer [touch-action:manipulation] active:bg-[var(--color-site-item-hover)]">×');
  function SearchHistory(props) {
    let dropdown, [searchValue, setSearchValue] = createSignal(untrack(() => props.source.data.value)), [history, setHistory] = createSignal(loadSearchHistory()), [open, setOpen] = createSignal(!1), [activeIndex, setActiveIndex] = createSignal(-1), [position, setPosition] = createSignal(null), itemButtons = [], visiblePosition = () => open() && !searchValue().trim() && history().length > 0 ? position() : null, selectHistory = (item) => {
      props.source.handle.applySearchSelection(item), setOpen(!1);
    };
    return onMount(() => {
      let updatePosition = () => {
        setPosition(props.source.handle.readSearchOverlayPosition());
      }, showHistory = () => {
        updatePosition(), setActiveIndex(-1), setOpen(!0);
      }, moveSelection = (offset) => {
        let items = history();
        if (items.length === 0)
          return;
        let current = activeIndex(), next = current < 0 ? offset > 0 ? 0 : items.length - 1 : (current + offset + items.length) % items.length;
        setActiveIndex(next), window.requestAnimationFrame(() => itemButtons[next]?.scrollIntoView({
          block: "nearest"
        }));
      }, onInputKeyDown = (event) => {
        if (visiblePosition())
          if (event.key === "ArrowDown" || event.key === "ArrowUp")
            event.preventDefault(), moveSelection(event.key === "ArrowDown" ? 1 : -1);
          else if (event.key === "Enter" && activeIndex() >= 0) {
            event.preventDefault();
            let item = history()[activeIndex()];
            item !== void 0 && selectHistory(item);
          } else event.key === "Escape" && (event.preventDefault(), setOpen(!1));
      }, updateSearchValue = (value, focused) => {
        setSearchValue(value), !value.trim() && focused && showHistory();
      }, recordSearch = (sourceValue) => {
        let value = sourceValue.trim();
        value && setHistory(addSearchHistory(value));
      }, disconnect = props.source.handle.listenSearchHistoryOverlay({
        onFocus: showHistory,
        onInput: updateSearchValue,
        onKeyDown: onInputKeyDown,
        onOutsidePointer: () => setOpen(!1),
        onPositionChange: updatePosition,
        onSubmit: recordSearch
      }, () => dropdown ?? null);
      updateSearchValue(props.source.data.value, !1), onCleanup(disconnect);
    }), createComponent(Show, {
      get when() {
        return visiblePosition();
      },
      keyed: !0,
      children: (currentPosition) => (() => {
        var _el$ = _tmpl$9(), _ref$ = dropdown;
        return typeof _ref$ == "function" ? use(_ref$, _el$) : dropdown = _el$, insert(_el$, createComponent(For, {
          get each() {
            return history();
          },
          children: (item, index) => (() => {
            var _el$2 = _tmpl$23(), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling;
            return _el$3.$$click = () => selectHistory(item), _el$3.addEventListener("pointerenter", () => setActiveIndex(index())), use((button) => {
              itemButtons[index()] = button;
            }, _el$3), setAttribute(_el$3, "title", item), insert(_el$3, item), _el$4.$$click = () => {
              setHistory(removeSearchHistory(item));
            }, createRenderEffect(() => className(_el$3, `appearance-none block min-w-0 min-h-lg flex-1 overflow-hidden text-ellipsis whitespace-nowrap px-lg border-0 ehp-color-site-text text-left textsize-lg font-inherit cursor-pointer [touch-action:manipulation] active:bg-[var(--color-site-item-hover)] ${activeIndex() === index() ? "bg-[var(--color-site-item-hover)]" : "bg-transparent"}`)), _el$2;
          })()
        })), createRenderEffect((_p$) => {
          var _v$ = `${currentPosition.left}px`, _v$2 = `${currentPosition.top}px`, _v$3 = `${currentPosition.width}px`;
          return _v$ !== _p$.e && setStyleProperty(_el$, "left", _p$.e = _v$), _v$2 !== _p$.t && setStyleProperty(_el$, "top", _p$.t = _v$2), _v$3 !== _p$.a && setStyleProperty(_el$, "width", _p$.a = _v$3), _p$;
        }, {
          e: void 0,
          t: void 0,
          a: void 0
        }), _el$;
      })()
    });
  }
  delegateEvents(["click"]);

  // src/components/Enhance/MyTags.ts
  async function loadMyTagsPage(tagSet) {
    let url = new URL("/mytags", window.location.origin);
    tagSet && url.searchParams.set("tagset", tagSet);
    let response = await requestPage(url.href);
    return extractMyTagsPageData(response.document, tagSet);
  }
  function loadMyTagAppearances() {
    return state.gallery.myTagAppearances.stored() ? state.gallery.myTagAppearances.reload() : null;
  }
  async function refreshMyTags(initialPage) {
    try {
      let initialData = initialPage ?? await loadMyTagsPage(), options = initialData.options;
      state.gallery.myTagSets.set(options);
      let appearances = (options.length > 0 ? await Promise.all(options.map(async (option) => option.selected ? initialData : loadMyTagsPage(option.value))) : [initialData]).flatMap((page) => page.enabled ? page.appearances : []), unique = Array.from(new Map(appearances.map((appearance) => [appearance.name, appearance])).values());
      return state.gallery.myTagAppearances.set(unique), unique;
    } catch (error) {
      return console.error("[ehpeek] Could not load My Tags", error), null;
    }
  }

  // node_modules/.pnpm/[email protected]/node_modules/solid-js/store/dist/store.js
  var $RAW = /* @__PURE__ */ Symbol("store-raw"), $NODE = /* @__PURE__ */ Symbol("store-node"), $HAS = /* @__PURE__ */ Symbol("store-has"), $SELF = /* @__PURE__ */ Symbol("store-self");
  function wrap$1(value) {
    let p = value[$PROXY];
    if (!p && (Object.defineProperty(value, $PROXY, {
      value: p = new Proxy(value, proxyTraps$1)
    }), !Array.isArray(value))) {
      let keys = Object.keys(value), desc = Object.getOwnPropertyDescriptors(value), proto = Object.getPrototypeOf(value), isClass = proto !== null && value !== null && typeof value == "object" && !Array.isArray(value) && proto !== Object.prototype;
      if (isClass) {
        let descriptors = Object.getOwnPropertyDescriptors(proto);
        keys.push(...Object.keys(descriptors)), Object.assign(desc, descriptors);
      }
      for (let i = 0, l = keys.length; i < l; i++) {
        let prop = keys[i];
        isClass && prop === "constructor" || desc[prop].get && Object.defineProperty(value, prop, {
          configurable: !0,
          enumerable: desc[prop].enumerable,
          get: desc[prop].get.bind(p)
        });
      }
    }
    return p;
  }
  function isWrappable(obj) {
    let proto;
    return obj != null && typeof obj == "object" && (obj[$PROXY] || !(proto = Object.getPrototypeOf(obj)) || proto === Object.prototype || Array.isArray(obj));
  }
  function unwrap(item, set = /* @__PURE__ */ new Set()) {
    let result, unwrapped, v, prop;
    if (result = item != null && item[$RAW]) return result;
    if (!isWrappable(item) || set.has(item)) return item;
    if (Array.isArray(item)) {
      Object.isFrozen(item) ? item = item.slice(0) : set.add(item);
      for (let i = 0, l = item.length; i < l; i++)
        v = item[i], (unwrapped = unwrap(v, set)) !== v && (item[i] = unwrapped);
    } else {
      Object.isFrozen(item) ? item = Object.assign({}, item) : set.add(item);
      let keys = Object.keys(item), desc = Object.getOwnPropertyDescriptors(item);
      for (let i = 0, l = keys.length; i < l; i++)
        prop = keys[i], !desc[prop].get && (v = item[prop], (unwrapped = unwrap(v, set)) !== v && (item[prop] = unwrapped));
    }
    return item;
  }
  function getNodes(target, symbol) {
    let nodes = target[symbol];
    return nodes || Object.defineProperty(target, symbol, {
      value: nodes = /* @__PURE__ */ Object.create(null)
    }), nodes;
  }
  function getNode(nodes, property, value) {
    if (nodes[property]) return nodes[property];
    let [s, set] = createSignal(value, {
      equals: !1,
      internal: !0
    });
    return s.$ = set, nodes[property] = s;
  }
  function proxyDescriptor$1(target, property) {
    let desc = Reflect.getOwnPropertyDescriptor(target, property);
    return !desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE || (delete desc.value, delete desc.writable, desc.get = () => target[$PROXY][property]), desc;
  }
  function trackSelf(target) {
    getListener() && getNode(getNodes(target, $NODE), $SELF)();
  }
  function ownKeys(target) {
    return trackSelf(target), Reflect.ownKeys(target);
  }
  var proxyTraps$1 = {
    get(target, property, receiver) {
      if (property === $RAW) return target;
      if (property === $PROXY) return receiver;
      if (property === $TRACK)
        return trackSelf(target), receiver;
      let nodes = getNodes(target, $NODE), tracked = nodes[property], value = tracked ? tracked() : target[property];
      if (property === $NODE || property === $HAS || property === "__proto__") return value;
      if (!tracked) {
        let desc = Object.getOwnPropertyDescriptor(target, property);
        getListener() && (typeof value != "function" || Object.prototype.hasOwnProperty.call(target, property)) && !(desc && desc.get) && (value = getNode(nodes, property, value)());
      }
      return isWrappable(value) ? wrap$1(value) : value;
    },
    has(target, property) {
      return property === $RAW || property === $PROXY || property === $TRACK || property === $NODE || property === $HAS || property === "__proto__" ? !0 : (getListener() && getNode(getNodes(target, $HAS), property)(), property in target);
    },
    set() {
      return !0;
    },
    deleteProperty() {
      return !0;
    },
    ownKeys,
    getOwnPropertyDescriptor: proxyDescriptor$1
  };
  function setProperty(state2, property, value, deleting = !1) {
    if (property === "__proto__" || !deleting && state2[property] === value) return;
    let prev = state2[property], len = state2.length;
    value === void 0 ? (delete state2[property], state2[$HAS] && state2[$HAS][property] && prev !== void 0 && state2[$HAS][property].$()) : (state2[property] = value, state2[$HAS] && state2[$HAS][property] && prev === void 0 && state2[$HAS][property].$());
    let nodes = getNodes(state2, $NODE), node;
    if ((node = getNode(nodes, property, prev)) && node.$(() => value), Array.isArray(state2) && state2.length !== len) {
      for (let i = state2.length; i < len; i++) (node = nodes[i]) && node.$();
      (node = getNode(nodes, "length", len)) && node.$(state2.length);
    }
    (node = nodes[$SELF]) && node.$();
  }
  function mergeStoreNode(state2, value) {
    let keys = Object.keys(value);
    for (let i = 0; i < keys.length; i += 1) {
      let key = keys[i];
      isUnsafeKey$1(key) || setProperty(state2, key, value[key]);
    }
  }
  function isUnsafeKey$1(property) {
    return property === "__proto__" || property === "constructor" || property === "prototype";
  }
  function updateArray(current, next) {
    if (typeof next == "function" && (next = next(current)), next = unwrap(next), Array.isArray(next)) {
      if (current === next) return;
      let i = 0, len = next.length;
      for (; i < len; i++) {
        let value = next[i];
        current[i] !== value && setProperty(current, i, value);
      }
      setProperty(current, "length", len);
    } else mergeStoreNode(current, next);
  }
  function updatePath(current, path, traversed = []) {
    let part, prev = current;
    if (path.length > 1) {
      part = path.shift();
      let partType = typeof part, isArray = Array.isArray(current);
      if (partType === "string" && (part === "__proto__" || path.length > 1 && isUnsafeKey$1(part)))
        return;
      if (Array.isArray(part)) {
        for (let i = 0; i < part.length; i++)
          updatePath(current, [part[i]].concat(path), traversed);
        return;
      } else if (isArray && partType === "function") {
        for (let i = 0; i < current.length; i++)
          part(current[i], i) && updatePath(current, [i].concat(path), traversed);
        return;
      } else if (isArray && partType === "object") {
        let {
          from = 0,
          to = current.length - 1,
          by = 1
        } = part;
        for (let i = from; i <= to; i += by)
          updatePath(current, [i].concat(path), traversed);
        return;
      } else if (path.length > 1) {
        updatePath(current[part], path, [part].concat(traversed));
        return;
      }
      prev = current[part], traversed = [part].concat(traversed);
    }
    let value = path[0];
    typeof value == "function" && (value = value(prev, traversed), value === prev) || part === void 0 && value == null || (value = unwrap(value), part === void 0 || isWrappable(prev) && isWrappable(value) && !Array.isArray(value) ? mergeStoreNode(prev, value) : setProperty(current, part, value));
  }
  function createStore(...[store, options]) {
    let unwrappedStore = unwrap(store || {}), isArray = Array.isArray(unwrappedStore), wrappedStore = wrap$1(unwrappedStore);
    function setStore(...args) {
      batch(() => {
        isArray && args.length === 1 ? updateArray(unwrappedStore, args[0]) : updatePath(unwrappedStore, args);
      });
    }
    return [wrappedStore, setStore];
  }

  // src/components/SettingsMenu.tsx
  var _tmpl$10 = /* @__PURE__ */ template('<p class="box-border w-full m-0 px-md pb-md text-left whitespace-normal [overflow-wrap:anywhere] [contain:inline-size] textsize-sm leading-[1.35] opacity-75">'), _tmpl$24 = /* @__PURE__ */ template('<div class="border-0 border-b ehp-color-site-border-subtle-b"><div class="flex items-stretch"><button type=button class="flex min-w-0 flex-1 min-h-md coarse:min-h-88px items-center justify-between gap-md coarse:gap-xl py-sm coarse:py-lg pl-md pr-sm rounded-xs border-0 !bg-transparent hover:!bg-transparent active:!bg-transparent ehp-color-site-text font-inherit text-left textsize-md cursor-pointer [-webkit-tap-highlight-color:transparent]"><span></span><span class="flex flex-none items-center gap-sm"><span class="textsize-sm opacity-70"></span><span></span></span></button><button type=button class="flex flex-none w-32px coarse:w-48px min-h-md coarse:min-h-88px items-center justify-center p-0 rounded-xs border-0 !bg-transparent hover:!bg-[var(--color-site-item-hover)] active:!bg-[var(--color-site-item-hover)] ehp-color-site-text cursor-pointer font-inherit textsize-md font-700 [-webkit-tap-highlight-color:transparent]"><span class="flex w-20px h-20px items-center justify-center rounded-full border border-[var(--color-site-border-subtle)] leading-none">?'), _tmpl$33 = /* @__PURE__ */ template('<div class="ml-md border-0 border-l border-l-[var(--color-site-border-subtle)]">'), _tmpl$43 = /* @__PURE__ */ template('<div class="ehpeek-settings-menu pointer-events-auto fixed top-24px right-24px coarse:top-8px coarse:right-8px z-overlay box-border w-320px coarse:w-[calc(100vw-16px)] max-w-[calc(100vw-48px)] coarse:max-w-480px max-h-[calc(100vh-48px)] coarse:max-h-[calc(100dvh-16px)] overflow-x-hidden overflow-y-auto p-sm coarse:p-md border ehp-color-site-border rounded-sm ehp-color-site-elevated ehp-color-site-text textsize-md leading-[1.2]"><div class="border-0 border-b ehp-color-site-border-subtle-b"><button type=button class="flex w-full min-h-md coarse:min-h-88px items-center justify-between gap-md coarse:gap-xl py-sm coarse:py-lg px-md rounded-xs border-0 !bg-transparent hover:!bg-[var(--color-site-item-hover)] active:!bg-[var(--color-site-item-hover)] ehp-color-site-text cursor-pointer font-inherit text-left textsize-md font-700 [-webkit-tap-highlight-color:transparent]"><span></span><span class="flex w-20px h-20px items-center justify-center leading-none"aria-hidden=true></span></button></div><div class="border-0 border-b ehp-color-site-border-subtle-b"><button type=button class="flex w-full min-h-md coarse:min-h-88px items-center justify-between gap-md coarse:gap-xl py-sm coarse:py-lg px-md rounded-xs border-0 !bg-transparent hover:!bg-[var(--color-site-item-hover)] active:!bg-[var(--color-site-item-hover)] ehp-color-site-text cursor-pointer font-inherit text-left textsize-md font-700 [-webkit-tap-highlight-color:transparent]"><span></span><span class="flex w-20px h-20px items-center justify-center leading-none"aria-hidden=true></span></button></div><a class="flex w-full min-h-md coarse:min-h-88px items-center overflow-hidden text-ellipsis whitespace-nowrap px-md border-0 border-b ehp-color-site-border-subtle-b ehp-color-site-text no-underline textsize-md font-700 hover:bg-[var(--color-site-item-hover)]"href=https://github.com/yamipot/ehpeek target=_blank rel="noopener noreferrer">v</a><div class="ehpeek-settings-actions grid grid-cols-3 gap-sm mt-md pt-md border-0 border-t border-t-[var(--color-site-border-subtle)]"><button type=button class="ehpeek-settings-apply block w-full min-h-md coarse:min-h-64px py-xs coarse:py-md px-md coarse:px-lg rounded-md border cursor-pointer font-inherit text-center textsize-md font-700 leading-[1.1] transition-[filter,transform,box-shadow] duration-120 active:scale-98 border-[var(--color-site-accent)] bg-[var(--color-site-accent)] text-[var(--color-site-surface)] shadow-[0_2px_8px_var(--color-shadow-panel)] hover:brightness-108"></button><button type=button class="ehpeek-settings-default block w-full min-h-md coarse:min-h-64px py-xs coarse:py-md px-md coarse:px-lg rounded-md border cursor-pointer font-inherit text-center textsize-md font-700 leading-[1.1] transition-[filter,transform,box-shadow] duration-120 active:scale-98 border-[var(--color-site-border-subtle)] bg-[var(--color-site-surface)] text-[var(--color-site-text)] hover:bg-[var(--color-site-item-hover)]"></button><button type=button class="ehpeek-settings-close block w-full min-h-md coarse:min-h-64px py-xs coarse:py-md px-md coarse:px-lg rounded-md border cursor-pointer font-inherit text-center textsize-md font-700 leading-[1.1] transition-[filter,transform,box-shadow] duration-120 active:scale-98 border-[var(--color-site-border-subtle)] bg-[var(--color-site-surface)] text-[var(--color-site-text)] hover:bg-[var(--color-site-item-hover)]">');
  var SETTINGS_DOT_CLASS = "block flex-none w-10px h-10px coarse:w-18px coarse:h-18px rounded-full";
  function SwitchButton(props) {
    let [helpOpen, setHelpOpen] = createSignal(!1);
    return (() => {
      var _el$ = _tmpl$24(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.nextSibling, _el$6 = _el$5.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$3.nextSibling;
      return _el$3.$$click = (event) => {
        event.stopPropagation(), props.onChange(!props.checked);
      }, insert(_el$4, () => props.label), insert(_el$6, (() => {
        var _c$ = memo(() => !!props.checked);
        return () => _c$() ? texts_default.settings.on : texts_default.settings.off;
      })()), _el$8.$$click = (event) => {
        event.stopPropagation(), setHelpOpen((open) => !open);
      }, insert(_el$, createComponent(Show, {
        get when() {
          return helpOpen();
        },
        get children() {
          var _el$9 = _tmpl$10();
          return insert(_el$9, () => props.description), _el$9;
        }
      }), null), createRenderEffect(() => className(_el$7, `${SETTINGS_DOT_CLASS} ${props.checked ? "bg-[var(--color-state-on)]" : "bg-[var(--color-state-off)]"}`)), _el$;
    })();
  }
  function SettingsMenu(props) {
    let [draft, setDraft] = createStore(untrack(() => ({
      ...props.initState
    }))), [readerOptionsOpen, setReaderOptionsOpen] = createSignal(!1), [enhanceOpen, setEnhanceOpen] = createSignal(!1), menu, close = () => {
      props.onOpenChange(!1);
    };
    return createEffect(() => {
      props.open && (setDraft({
        ...props.initState
      }), setReaderOptionsOpen(!1), setEnhanceOpen(!1));
    }), onMount(() => {
      let onPointerDown = (event) => {
        props.open && (event.target instanceof Element && menu.contains(event.target) || close());
      }, onKeyDown = (event) => {
        props.open && event.key === "Escape" && close();
      };
      document.addEventListener("pointerdown", onPointerDown), document.addEventListener("keydown", onKeyDown), onCleanup(() => {
        document.removeEventListener("pointerdown", onPointerDown), document.removeEventListener("keydown", onKeyDown);
      });
    }), createComponent(Show, {
      get when() {
        return props.open;
      },
      get children() {
        var _el$0 = _tmpl$43(), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild, _el$11 = _el$10.firstChild, _el$12 = _el$11.nextSibling, _el$14 = _el$1.nextSibling, _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$16.nextSibling, _el$19 = _el$14.nextSibling, _el$20 = _el$19.firstChild, _el$21 = _el$19.nextSibling, _el$22 = _el$21.firstChild, _el$23 = _el$22.nextSibling, _el$24 = _el$23.nextSibling, _ref$ = menu;
        return typeof _ref$ == "function" ? use(_ref$, _el$0) : menu = _el$0, insert(_el$0, createComponent(SwitchButton, {
          get checked() {
            return draft.readerEnabled;
          },
          get description() {
            return texts_default.settings.readerHelp;
          },
          get label() {
            return texts_default.settings.readerLabel;
          },
          onChange: (value) => setDraft("readerEnabled", value)
        }), _el$1), insert(_el$0, createComponent(SwitchButton, {
          get checked() {
            return draft.touchUiEnabled;
          },
          get description() {
            return texts_default.settings.touchUiHelp;
          },
          get label() {
            return texts_default.settings.touchUiLabel;
          },
          onChange: (value) => setDraft("touchUiEnabled", value)
        }), _el$1), _el$10.$$click = (event) => {
          event.stopPropagation(), setReaderOptionsOpen((open) => !open);
        }, insert(_el$11, () => texts_default.settings.readerOptions), insert(_el$12, () => readerOptionsOpen() ? "−" : "+"), insert(_el$1, createComponent(Show, {
          get when() {
            return readerOptionsOpen();
          },
          get children() {
            var _el$13 = _tmpl$33();
            return insert(_el$13, createComponent(SwitchButton, {
              get checked() {
                return draft.readerFullscreenEnabled;
              },
              get description() {
                return texts_default.settings.readerFullscreenHelp;
              },
              get label() {
                return texts_default.settings.readerFullscreenLabel;
              },
              onChange: (value) => setDraft("readerFullscreenEnabled", value)
            }), null), insert(_el$13, createComponent(SwitchButton, {
              get checked() {
                return draft.openGalleryInNewTab;
              },
              get description() {
                return texts_default.settings.openGalleryInNewTabHelp;
              },
              get label() {
                return texts_default.settings.openGalleryInNewTabLabel;
              },
              onChange: (value) => setDraft("openGalleryInNewTab", value)
            }), null), _el$13;
          }
        }), null), _el$15.$$click = (event) => {
          event.stopPropagation(), setEnhanceOpen((open) => !open);
        }, insert(_el$16, () => texts_default.settings.enhance), insert(_el$17, () => enhanceOpen() ? "−" : "+"), insert(_el$14, createComponent(Show, {
          get when() {
            return enhanceOpen();
          },
          get children() {
            var _el$18 = _tmpl$33();
            return insert(_el$18, createComponent(SwitchButton, {
              get checked() {
                return draft.enhanceSearchGridsEnabled;
              },
              get description() {
                return texts_default.settings.enhanceSearchHelp;
              },
              get label() {
                return texts_default.settings.enhanceSearchLabel;
              },
              onChange: (value) => setDraft("enhanceSearchGridsEnabled", value)
            }), null), insert(_el$18, createComponent(SwitchButton, {
              get checked() {
                return draft.enhanceThumbsGridsEnabled;
              },
              get description() {
                return texts_default.settings.enhanceThumbsHelp;
              },
              get label() {
                return texts_default.settings.enhanceThumbsLabel;
              },
              onChange: (value) => setDraft("enhanceThumbsGridsEnabled", value)
            }), null), insert(_el$18, createComponent(SwitchButton, {
              get checked() {
                return draft.myTagsEnabled;
              },
              get description() {
                return texts_default.settings.myTagsHelp;
              },
              get label() {
                return texts_default.settings.myTagsLabel;
              },
              onChange: (value) => setDraft("myTagsEnabled", value)
            }), null), insert(_el$18, createComponent(SwitchButton, {
              get checked() {
                return draft.readHistoryEnabled;
              },
              get description() {
                return texts_default.settings.readHistoryHelp;
              },
              get label() {
                return texts_default.settings.readHistoryLabel;
              },
              onChange: (value) => setDraft("readHistoryEnabled", value)
            }), null), insert(_el$18, createComponent(SwitchButton, {
              get checked() {
                return draft.searchHistoryEnabled;
              },
              get description() {
                return texts_default.settings.searchHistoryHelp;
              },
              get label() {
                return texts_default.settings.searchHistoryLabel;
              },
              onChange: (value) => setDraft("searchHistoryEnabled", value)
            }), null), _el$18;
          }
        }), null), insert(_el$19, "260721.1622", null), _el$22.$$click = (event) => {
          event.stopPropagation(), props.onApply({
            ...draft
          });
        }, insert(_el$22, () => texts_default.button.apply), _el$23.$$click = (event) => {
          event.stopPropagation(), setDraft({
            ...props.defaultState
          });
        }, insert(_el$23, () => texts_default.button.default), _el$24.$$click = (event) => {
          event.stopPropagation(), close();
        }, insert(_el$24, () => texts_default.button.close), createRenderEffect((_p$) => {
          var _v$ = readerOptionsOpen(), _v$2 = enhanceOpen();
          return _v$ !== _p$.e && setAttribute(_el$10, "aria-expanded", _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$15, "aria-expanded", _p$.t = _v$2), _p$;
        }, {
          e: void 0,
          t: void 0
        }), _el$0;
      }
    });
  }
  delegateEvents(["click"]);

  // src/components/Widgets/BackToTop.tsx
  var _tmpl$11 = /* @__PURE__ */ template('<button type=button class="ehpeek-back-to-top fixed right-[max(16px,env(safe-area-inset-right,0px))] bottom-[calc(max(16px,env(safe-area-inset-bottom,0px))_+_64px)] z-ui inline-flex w-lg h-lg items-center justify-center rounded-full border-0 bg-[var(--color-site-elevated)] ehp-color-site-accent shadow-[0_4px_14px_var(--color-shadow-floating)] cursor-pointer [touch-action:none] active:scale-96">'), BACK_TO_TOP_POSITION_KEY = "ehpeek:back-to-top:position";
  function BackToTop() {
    let button, drag = null, dragged = !1, [visible, setVisible] = createSignal(!1), [position, setPosition] = createSignal(null), positionStyle = () => {
      let current = position();
      return current ? {
        bottom: `${current.bottom}px`,
        right: `${current.right}px`
      } : void 0;
    };
    return onMount(() => {
      let updateVisibility = () => {
        setVisible(window.scrollY > Math.max(320, window.innerHeight * 0.5));
      };
      updateVisibility();
      let savedPosition = GM_getValue(BACK_TO_TOP_POSITION_KEY, null);
      savedPosition && setPosition(savedPosition), window.addEventListener("scroll", updateVisibility, {
        passive: !0
      }), onCleanup(() => window.removeEventListener("scroll", updateVisibility));
    }), createComponent(Show, {
      get when() {
        return visible();
      },
      get children() {
        var _el$ = _tmpl$11();
        _el$.$$click = (event) => {
          if (dragged) {
            event.preventDefault(), dragged = !1;
            return;
          }
          window.scrollTo({
            top: 0,
            behavior: "smooth"
          });
        }, _el$.$$pointerup = (event) => {
          if (!drag || drag.pointerId !== event.pointerId)
            return;
          button.releasePointerCapture(event.pointerId), drag = null;
          let current = position();
          dragged && current && GM_setValue(BACK_TO_TOP_POSITION_KEY, current);
        }, _el$.$$pointermove = (event) => {
          if (!drag || drag.pointerId !== event.pointerId)
            return;
          let dx = event.clientX - drag.x, dy = event.clientY - drag.y;
          dragged || (dragged = Math.hypot(dx, dy) > 4), setPosition(clampPosition({
            bottom: drag.bottom - dy,
            right: drag.right - dx
          }, button));
        }, _el$.$$pointerdown = (event) => {
          let rect = button.getBoundingClientRect();
          dragged = !1, drag = {
            bottom: window.innerHeight - rect.bottom,
            pointerId: event.pointerId,
            right: window.innerWidth - rect.right,
            x: event.clientX,
            y: event.clientY
          }, button.setPointerCapture(event.pointerId);
        };
        var _ref$ = button;
        return typeof _ref$ == "function" ? use(_ref$, _el$) : button = _el$, insert(_el$, createComponent(Icon, {
          name: "arrow-up"
        })), createRenderEffect((_$p) => style(_el$, positionStyle(), _$p)), _el$;
      }
    });
  }
  function clampPosition(position, button) {
    return {
      bottom: Math.min(Math.max(0, position.bottom), Math.max(0, window.innerHeight - button.offsetHeight)),
      right: Math.min(Math.max(0, position.right), Math.max(0, window.innerWidth - button.offsetWidth))
    };
  }
  delegateEvents(["pointerdown", "pointermove", "pointerup", "click"]);

  // src/components/Widgets/ExternalDom.tsx
  function DomNode2(props) {
    let Component = createMemo(() => props.node?.Component);
    return createComponent(Show, {
      get when() {
        return Component();
      },
      children: (Current) => createComponent(Dynamic, {
        get component() {
          return Current();
        }
      })
    });
  }
  function DomNodes(props) {
    return createComponent(For, {
      get each() {
        return props.nodes;
      },
      children: (node) => {
        let Component = node.Component;
        return createComponent(Component, {});
      }
    });
  }

  // src/components/TouchUI/GalleryInfoPanel.tsx
  var _tmpl$12 = /* @__PURE__ */ template("<div>"), _tmpl$25 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-rating-dialog fixed inset-0 z-overlay flex items-center justify-center p-md bg-black/65"role=dialog aria-modal=true aria-label="Rate gallery"><div class="box-border flex w-[min(92vw,420px)] flex-col gap-lg rounded-lg border ehp-color-site-border p-lg ehp-color-site-elevated ehp-color-site-text shadow-xl"><div class="textsize-md font-700">Rate gallery</div><button type=button class="relative inline-flex self-center max-w-full overflow-hidden p-0 border-0 bg-transparent cursor-pointer select-none [touch-action:manipulation] [-webkit-tap-highlight-color:transparent] focus-visible:rounded-xs focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--color-site-accent)] focus-visible:outline-offset-3px"><span class="flex gap-1px pointer-events-none text-[var(--color-muted)] opacity-40"aria-hidden=true></span><span aria-hidden=true></span></button><div class="grid grid-cols-2 gap-sm pt-md border-0 border-t border-t-[var(--color-site-border-subtle)]"><button type=button class="block w-full min-h-md coarse:min-h-64px py-xs coarse:py-md px-md coarse:px-lg rounded-md border cursor-pointer font-inherit text-center textsize-md font-700 leading-[1.1] transition-[filter,transform,box-shadow] duration-120 active:scale-98 disabled:opacity-50 disabled:cursor-default border-[var(--color-site-accent)] bg-[var(--color-site-accent)] text-[var(--color-site-surface)] shadow-[0_2px_8px_var(--color-shadow-panel)] hover:brightness-108">Submit</button><button type=button class="block w-full min-h-md coarse:min-h-64px py-xs coarse:py-md px-md coarse:px-lg rounded-md border cursor-pointer font-inherit text-center textsize-md font-700 leading-[1.1] transition-[filter,transform,box-shadow] duration-120 active:scale-98 disabled:opacity-50 disabled:cursor-default border-[var(--color-site-border-subtle)] bg-[var(--color-site-surface)] text-[var(--color-site-text)] hover:bg-[var(--color-site-item-hover)]">'), _tmpl$34 = /* @__PURE__ */ template('<section class="ehpeek-touch-gallery flex box-border w-full flex-col mb-md ehp-color-site-text font-sans"><div class="ehpeek-touch-gallery-hero relative grid min-h-[clamp(260px,42vh,340px)] pt-lg pr-[max(16px,env(safe-area-inset-right,0px))] pb-48px pl-[max(16px,env(safe-area-inset-left,0px))] ehp-color-site-surface ehp-color-site-text"><div><div class="ehpeek-touch-gallery-hero-side flex self-stretch min-w-0 flex-col items-start gap-8px pt-2px"><div class="ehpeek-touch-gallery-heading flex min-w-0 w-full flex-none flex-col gap-sm items-start pb-xs"><div class="ehpeek-touch-gallery-title-main line-clamp-4 flex-none overflow-hidden textsize-lg font-400 leading-[1.16] text-left break-anywhere"></div><div class="ehpeek-touch-gallery-title-sub line-clamp-3 flex-none overflow-hidden opacity-82 textsize-md leading-[1.2] text-left break-anywhere"></div></div><div class="ehpeek-touch-gallery-category-row grid grid-cols-[minmax(0,35fr)_minmax(0,65fr)] w-full flex-none items-center gap-lg mt-auto pt-md"><div class="ehpeek-touch-gallery-category box-border w-full min-w-0 overflow-hidden text-ellipsis whitespace-nowrap rounded-xs border border-solid py-6px px-10px text-center textsize-md font-700 leading-[1.1] uppercase"></div></div></div></div></div><div class="ehpeek-touch-gallery-primary relative z-1 grid grid-cols-[1fr_1fr] min-h-87px mt--18px mr-[max(14px,env(safe-area-inset-right,0px))] ml-[max(14px,env(safe-area-inset-left,0px))] overflow-visible rounded-xs bg-[var(--color-site-elevated)] shadow-[0_2px_10px_var(--color-shadow-panel)]"><div class="ehpeek-touch-gallery-primary-actions flex min-w-0 border-0 border-l-8 border-solid border-l-[var(--color-site-page)]"></div></div><div class="ehpeek-touch-gallery-content flex flex-col gap-lg pt-xl pr-[max(16px,env(safe-area-inset-right,0px))] pb-lg pl-[max(16px,env(safe-area-inset-left,0px))] ehp-color-site-page ehp-color-site-text"><div class="ehpeek-touch-gallery-meta grid grid-cols-[repeat(3,minmax(0,1fr))] gap-y-md gap-x-lg items-center textsize-md leading-[1.2] text-center">'), _tmpl$44 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-cover flex self-center justify-self-stretch w-full max-h-full aspect-[2/3] items-center justify-center overflow-hidden rounded-3px">'), _tmpl$53 = /* @__PURE__ */ template('<button type=button class="ehpeek-touch-gallery-rating flex w-full min-w-0 flex-col items-center gap-4px p-0 border-0 bg-transparent ehp-color-site-text font-inherit text-center cursor-pointer select-none [touch-action:manipulation] [-webkit-tap-highlight-color:transparent] focus-visible:rounded-xs focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--color-site-accent)] focus-visible:outline-offset-3px"aria-label="Rate gallery"><div class="ehpeek-touch-gallery-rating-stars relative inline-flex max-w-full overflow-hidden"><span class="ehpeek-touch-gallery-rating-stars-empty flex gap-1px text-[var(--color-muted)] opacity-40"aria-hidden=true></span><span aria-hidden=true></span></div><div class="ehpeek-touch-gallery-rating-meta flex max-w-full min-w-0 items-center justify-center gap-6px text-[var(--color-muted)] textsize-md leading-[1.15] whitespace-nowrap"><span class="ehpeek-touch-gallery-rating-label min-w-0 overflow-hidden text-ellipsis"aria-live=polite>'), _tmpl$63 = /* @__PURE__ */ template('<span class="ehpeek-touch-gallery-rating-count flex-none pl-6px border-0 border-l border-[var(--color-site-border-subtle)] opacity-75">'), _tmpl$72 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-meta-value line-clamp-2 min-w-0 overflow-hidden whitespace-normal break-normal">'), _tmpl$82 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-tag-groups flow-root pt-2px"><button type=button><span>Tagging</span><span aria-hidden=true>'), _tmpl$92 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-actions-menu-panel absolute top-48px right-0 z-overlay flex min-w-285px max-w-[min(78vw,320px)] flex-col overflow-hidden border ehp-color-site-border rounded-sm ehp-color-site-elevated">'), _tmpl$0 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-actions-menu relative flex min-w-0 items-center justify-center"><button type=button class="ehpeek-touch-gallery-actions-menu-button inline-flex w-md h-md items-center justify-center border-0 bg-transparent ehp-color-site-text"aria-haspopup=menu>'), _tmpl$1 = /* @__PURE__ */ template('<section class="ehpeek-touch-gallery-tag-group grid grid-cols-[minmax(76px,20%)_minmax(0,1fr)] gap-sm items-start mb-md last:mb-0"><div class="ehpeek-touch-gallery-tag-group-name min-h-sm overflow-hidden text-ellipsis whitespace-nowrap rounded-xl bg-[var(--color-site-elevated)] py-sm px-md text-center lowercase ehp-color-site-accent textsize-md font-600"></div><div class="ehpeek-touch-gallery-tags flex flex-wrap gap-sm">'), _tmpl$102 = /* @__PURE__ */ template('<a class="ehpeek-touch-gallery-tag inline-flex max-w-full min-h-lg items-center overflow-hidden text-ellipsis whitespace-nowrap appearance-none m-0 py-0 rounded-xl border border-[var(--color-site-border-subtle)] bg-[var(--color-site-surface)] px-lg ehp-color-site-text font-inherit font-700 textsize-md cursor-pointer select-none no-underline transition-[border-color,background-color,color] duration-120 hover:border-[var(--color-site-border)] hover:bg-[var(--color-site-accent-hover)] hover:ehp-color-site-accent">'), _tmpl$112 = /* @__PURE__ */ template('<div role=dialog aria-modal=true><div class="ehpeek-touch-gallery-tag-menu-panel box-border flex w-full max-w-420px max-h-[calc(100dvh-32px)] flex-col overflow-x-hidden overflow-y-auto whitespace-nowrap border ehp-color-site-border rounded-md ehp-color-site-elevated shadow-xl"role=menu>'), _tmpl$122 = /* @__PURE__ */ template('<label class="flex flex-col gap-sm ehp-color-site-text textsize-md font-600"><span></span><select class="box-border min-h-md w-full rounded-xs border ehp-color-site-border ehp-color-site-surface ehp-color-site-text px-md font-inherit textsize-md">'), _tmpl$13 = /* @__PURE__ */ template('<label class="flex flex-col gap-sm ehp-color-site-text textsize-md font-600"><span></span><select class="box-border min-h-md w-full rounded-xs border ehp-color-site-border ehp-color-site-surface ehp-color-site-text px-md font-inherit textsize-md"><option value=marked></option><option value=watched></option><option value=hidden>'), _tmpl$14 = /* @__PURE__ */ template('<div class="grid grid-cols-2 gap-md"><button type=button class="min-h-md rounded-xs border-0 ehp-color-site-surface ehp-color-site-text font-inherit font-700 textsize-md cursor-pointer"></button><button type=button class="flex min-h-md items-center justify-center gap-md rounded-xs border-0 bg-[var(--color-site-accent)] text-[var(--color-background)] font-inherit font-700 textsize-md cursor-pointer"><span>'), _tmpl$15 = /* @__PURE__ */ template('<div class="fixed inset-0 z-overlay flex items-center justify-center p-lg bg-black/65"role=dialog aria-modal=true><div class="box-border flex w-full max-w-420px flex-col gap-lg rounded-md border ehp-color-site-border ehp-color-site-elevated p-lg shadow-xl"><div class="ehp-color-site-text textsize-lg font-700">'), _tmpl$16 = /* @__PURE__ */ template('<button type=button class="ehpeek-touch-gallery-tag-menu-item flex box-border w-full min-h-lg items-center gap-md py-md px-lg border-0 border-b ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text no-underline font-inherit textsize-md text-left cursor-pointer"role=menuitem><span>'), _tmpl$17 = /* @__PURE__ */ template("<option>"), _tmpl$18 = /* @__PURE__ */ template('<span class="contents [&amp;_*]:!bg-transparent [&amp;_*]:!text-inherit"translate=no>'), _tmpl$19 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-favorite-panel absolute top-[calc(100%+8px)] left-0 z-overlay flex w-[min(86vw,360px)] flex-col overflow-hidden border ehp-color-site-border rounded-sm ehp-color-site-elevated">'), _tmpl$20 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-favorite-menu relative z-2 min-w-0"><button type=button aria-haspopup=menu><span class="block leading-[1.15]"></span><span class="ehpeek-touch-gallery-favorite-icon block mt-2px opacity-78 normal-case"aria-hidden=true>'), _tmpl$21 = /* @__PURE__ */ template('<div class="ehpeek-touch-gallery-favorite-loading flex min-h-lg items-center gap-md py-md px-lg border-0 border-b ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text font-inherit textsize-md leading-[1.2] text-left">'), _tmpl$222 = /* @__PURE__ */ template('<button type=button><span class="ehpeek-touch-gallery-favorite-option-icon flex-none ehp-color-site-text"aria-hidden=true></span><span></span><span aria-hidden=true>'), TOUCH_GALLERY_ACTION_MENU_ITEM_CLASS = "ehpeek-touch-gallery-actions-menu-item block box-border w-full min-h-lg py-md px-lg border-0 border-b ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text text-left no-underline textsize-md leading-[1.2]", TOUCH_GALLERY_TAG_MENU_ITEM_CLASS = "ehpeek-touch-gallery-tag-menu-item flex box-border w-full min-h-lg items-center gap-md py-md px-lg border-0 border-b ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text no-underline font-inherit textsize-md text-left cursor-pointer", TOUCH_GALLERY_TAG_MENU_CLASS = "!box-border flex !h-auto !w-full !float-none !m-0 !p-0 flex-col !textsize-md", TOUCH_GALLERY_INFO_CLASSES = {
    actionItems: TOUCH_GALLERY_ACTION_MENU_ITEM_CLASS,
    cover: "block w-full max-w-full h-full max-h-full mx-auto object-contain object-center",
    host: "ehpeek-touch-gallery-host",
    newTag: {
      button: "box-border flex-none h-md px-lg rounded-xs border border-[var(--color-site-accent)] bg-[var(--color-site-accent)] text-[var(--color-background)] font-inherit textsize-md font-700 cursor-pointer",
      container: "ehpeek-touch-gallery-new-tag box-border w-full pt-md",
      field: "box-border min-w-0 flex-1 h-md px-md rounded-xs border ehp-color-site-border bg-[var(--color-site-surface)] ehp-color-site-text font-inherit textsize-md outline-none focus:border-[var(--color-site-accent)]",
      form: "flex w-full min-w-0 items-center gap-sm"
    }
  }, RATING_STAR_INDEXES = [0, 1, 2, 3, 4];
  function GalleryInfoPanel(props) {
    let source = untrack(() => props.source), rating = source.data.rating, hasCover = source.elems.cover !== null, [ratingValue, setRatingValue] = createSignal(rating?.value ?? 0), [ratingPreview, setRatingPreview] = createSignal(null), [ratingPickerOpen, setRatingPickerOpen] = createSignal(!1), [ratingSubmitted, setRatingSubmitted] = createSignal(rating?.rated ?? !1), [ratingCount] = createSignal(rating?.count ?? ""), [ratingValueLabel] = createSignal(rating?.label ?? ""), initialTagGroups = source.data.tagGroups.map((group) => ({
      ...group,
      tags: group.tags.flatMap(({
        contentSourceIndex,
        ...tag
      }) => {
        let contentSource = source.elems.tagContents[contentSourceIndex];
        return contentSource ? [{
          ...tag,
          contentSource
        }] : [];
      })
    })), [tagGroups, setTagGroups] = createSignal(initialTagGroups), [selectedTag, setSelectedTag] = createSignal(null), [tagging, setTagging] = createSignal(!1), hasNewTag = () => !!(source.elems.newTag && source.elems.newTagButton && source.elems.newTagField && source.elems.newTagForm), displayedRating = createMemo(() => ratingPreview() ?? ratingValue()), ratingLabel = createMemo(() => {
      let preview = ratingPreview();
      return preview !== null ? `Rate as ${preview.toFixed(1)} stars` : ratingValueLabel();
    });
    onMount(() => {
      let stopObservingTags = source.handle.observeGalleryTagGroups(setTagGroups);
      onCleanup(stopObservingTags);
    });
    let submitRating = (value) => {
      if (!rating)
        return !1;
      try {
        return source.handle.submitGalleryRating(value), setRatingValue(value), setRatingPreview(null), setRatingSubmitted(!0), !0;
      } catch (error) {
        return setRatingPreview(null), console.error("[ehpeek]", error), window.alert(error instanceof Error ? error.message : texts_default.errors.loadFailed), !1;
      }
    }, openTagMenu = (tag) => {
      try {
        source.handle.openGalleryTagMenu(tag, TOUCH_GALLERY_TAG_MENU_CLASS, TOUCH_GALLERY_TAG_MENU_ITEM_CLASS), setSelectedTag(tag);
      } catch (error) {
        console.error("[ehpeek] Gallery tag actions failed", error), window.alert(error instanceof Error ? error.message : texts_default.errors.loadFailed);
      }
    }, closeTagMenu = () => {
      selectedTag() && (source.handle.closeGalleryTagMenu(), setSelectedTag(null));
    }, updateTag = (updatedTag) => {
      setTagGroups((groups) => groups.map((group) => ({
        ...group,
        tags: group.tags.map((tag) => tag.url === updatedTag.url ? updatedTag : tag)
      }))), setSelectedTag((tag) => tag?.url === updatedTag.url ? updatedTag : tag);
    };
    return (() => {
      var _el$ = _tmpl$34(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.firstChild, _el$7 = _el$6.nextSibling, _el$8 = _el$5.nextSibling, _el$9 = _el$8.firstChild, _el$0 = _el$2.nextSibling, _el$1 = _el$0.firstChild, _el$10 = _el$0.nextSibling, _el$11 = _el$10.firstChild;
      return className(_el$3, `ehpeek-touch-gallery-summary grid gap-18px items-stretch ${hasCover ? "grid-cols-[minmax(120px,38%)_minmax(0,1fr)]" : "grid-cols-1"}`), insert(_el$3, hasCover && (() => {
        var _el$22 = _tmpl$44();
        return insert(_el$22, createComponent(DomNode2, {
          get node() {
            return source.elems.cover;
          }
        })), _el$22;
      })(), _el$4), insert(_el$6, () => source.data.titleMain), insert(_el$7, () => source.data.titleSub), insert(_el$9, () => source.data.category), insert(_el$8, rating && (() => {
        var _el$23 = _tmpl$53(), _el$24 = _el$23.firstChild, _el$25 = _el$24.firstChild, _el$26 = _el$25.nextSibling, _el$27 = _el$24.nextSibling, _el$28 = _el$27.firstChild;
        return _el$23.addEventListener("blur", () => {
          setRatingPreview(null);
        }), _el$23.addEventListener("pointerleave", () => {
          setRatingPreview(null);
        }), _el$23.$$click = () => {
          setRatingPreview(null), setRatingPickerOpen(!0);
        }, _el$24.$$pointermove = (event) => {
          event.pointerType === "mouse" && setRatingPreview(ratingFromPointer(event.clientX, event.currentTarget));
        }, insert(_el$25, createComponent(For, {
          each: RATING_STAR_INDEXES,
          children: () => createComponent(Icon, {
            name: "star"
          })
        })), insert(_el$26, createComponent(For, {
          each: RATING_STAR_INDEXES,
          children: () => createComponent(Icon, {
            name: "star",
            filled: !0
          })
        })), insert(_el$28, ratingLabel), insert(_el$27, (() => {
          var _c$2 = memo(() => !!ratingCount());
          return () => _c$2() && (() => {
            var _el$29 = _tmpl$63();
            return insert(_el$29, ratingCount), _el$29;
          })();
        })(), null), createRenderEffect((_p$) => {
          var _v$4 = `ehpeek-touch-gallery-rating-stars-fill absolute top-0 left-0 flex gap-1px overflow-hidden ${ratingSubmitted() ? "text-[var(--color-rating-submitted)]" : "ehp-color-site-accent"}`, _v$5 = `${displayedRating() / 5 * 100}%`;
          return _v$4 !== _p$.e && className(_el$26, _p$.e = _v$4), _v$5 !== _p$.t && setStyleProperty(_el$26, "width", _p$.t = _v$5), _p$;
        }, {
          e: void 0,
          t: void 0
        }), _el$23;
      })(), null), insert(_el$0, createComponent(TouchGalleryFavoriteButton, {
        source
      }), _el$1), insert(_el$1, () => props.primaryAction), insert(_el$11, createComponent(For, {
        get each() {
          return source.data.summary;
        },
        children: (item) => (() => {
          var _el$30 = _tmpl$72();
          return insert(_el$30, () => item.value), _el$30;
        })()
      }), null), insert(_el$11, createComponent(TouchGalleryActionsMenu, {
        get items() {
          return source.elems.actionItems;
        }
      }), null), insert(_el$10, (() => {
        var _c$ = memo(() => tagGroups().length > 0);
        return () => _c$() && (() => {
          var _el$31 = _tmpl$82(), _el$32 = _el$31.firstChild, _el$33 = _el$32.firstChild, _el$34 = _el$33.nextSibling;
          return _el$32.$$click = () => {
            let enabled = !tagging();
            setTagging(enabled), enabled && hasNewTag() && queueMicrotask(() => {
              source.elems.newTag?.scrollIntoView({
                block: "nearest"
              }), source.elems.newTagField?.focus();
            });
          }, insert(_el$31, createComponent(For, {
            get each() {
              return tagGroups();
            },
            children: (group) => createComponent(TouchGalleryTagGroup, {
              group,
              get tagging() {
                return tagging();
              },
              onTagOpen: openTagMenu
            })
          }), null), createRenderEffect((_p$) => {
            var _v$6 = `float-right inline-flex min-h-sm items-center justify-center gap-md ml-sm mb-sm rounded-xl border-0 px-lg font-inherit font-700 textsize-sm cursor-pointer transition-[background-color,color] duration-120 ${tagging() ? "bg-[var(--color-site-accent-hover)] ehp-color-site-accent" : "bg-[var(--color-site-surface)] ehp-color-site-text"}`, _v$7 = tagging(), _v$8 = `block flex-none w-10px h-10px coarse:w-18px coarse:h-18px rounded-full ${tagging() ? "bg-[var(--color-state-on)]" : "bg-[var(--color-state-off)]"}`;
            return _v$6 !== _p$.e && className(_el$32, _p$.e = _v$6), _v$7 !== _p$.t && setAttribute(_el$32, "aria-pressed", _p$.t = _v$7), _v$8 !== _p$.a && className(_el$34, _p$.a = _v$8), _p$;
          }, {
            e: void 0,
            t: void 0,
            a: void 0
          }), _el$31;
        })();
      })(), null), insert(_el$10, createComponent(Show, {
        get when() {
          return hasNewTag();
        },
        get children() {
          var _el$12 = _tmpl$12();
          return insert(_el$12, createComponent(TouchGalleryNewTag, {
            source
          })), createRenderEffect(() => className(_el$12, tagging() ? "block" : "hidden")), _el$12;
        }
      }), null), insert(_el$, createComponent(TouchGalleryTagMenu, {
        source,
        get tag() {
          return selectedTag();
        },
        onClose: closeTagMenu,
        onTagUpdated: updateTag
      }), null), insert(_el$, createComponent(Show, {
        get when() {
          return ratingPickerOpen();
        },
        get children() {
          var _el$13 = _tmpl$25(), _el$14 = _el$13.firstChild, _el$15 = _el$14.firstChild, _el$16 = _el$15.nextSibling, _el$17 = _el$16.firstChild, _el$18 = _el$17.nextSibling, _el$19 = _el$16.nextSibling, _el$20 = _el$19.firstChild, _el$21 = _el$20.nextSibling;
          return _el$13.$$click = (event) => {
            event.target === event.currentTarget && (setRatingPreview(null), setRatingPickerOpen(!1));
          }, _el$16.$$click = (event) => {
            setRatingPreview(ratingFromPointer(event.clientX, event.currentTarget));
          }, insert(_el$17, createComponent(For, {
            each: RATING_STAR_INDEXES,
            children: () => createComponent(Icon, {
              name: "star",
              size: 48
            })
          })), insert(_el$18, createComponent(For, {
            each: RATING_STAR_INDEXES,
            children: () => createComponent(Icon, {
              name: "star",
              size: 48,
              filled: !0
            })
          })), _el$20.$$click = () => {
            submitRating(displayedRating()) && setRatingPickerOpen(!1);
          }, _el$21.$$click = () => {
            setRatingPreview(null), setRatingPickerOpen(!1);
          }, insert(_el$21, () => texts_default.button.close), createRenderEffect((_p$) => {
            var _v$ = `Rate gallery: ${displayedRating().toFixed(1)} stars`, _v$2 = `absolute top-0 left-0 flex gap-1px overflow-hidden pointer-events-none ${ratingSubmitted() ? "text-[var(--color-rating-submitted)]" : "ehp-color-site-accent"}`, _v$3 = `${displayedRating() / 5 * 100}%`;
            return _v$ !== _p$.e && setAttribute(_el$16, "aria-label", _p$.e = _v$), _v$2 !== _p$.t && className(_el$18, _p$.t = _v$2), _v$3 !== _p$.a && setStyleProperty(_el$18, "width", _p$.a = _v$3), _p$;
          }, {
            e: void 0,
            t: void 0,
            a: void 0
          }), _el$13;
        }
      }), null), createRenderEffect((_$p) => style(_el$9, source.data.categoryAppearance, _$p)), _el$;
    })();
  }
  function TouchGalleryActionsMenu(props) {
    let [open, setOpen] = createSignal(!1), root;
    return onMount(() => {
      let onClick = (event) => {
        event.target instanceof Element && root.contains(event.target) || setOpen(!1);
      };
      document.addEventListener("click", onClick), onCleanup(() => {
        document.removeEventListener("click", onClick);
      });
    }), (() => {
      var _el$35 = _tmpl$0(), _el$36 = _el$35.firstChild, _ref$ = root;
      return typeof _ref$ == "function" ? use(_ref$, _el$35) : root = _el$35, _el$36.$$click = (event) => {
        event.stopPropagation(), setOpen((value) => !value);
      }, insert(_el$36, createComponent(Icon, {
        name: "menu"
      })), insert(_el$35, createComponent(Show, {
        get when() {
          return open();
        },
        get children() {
          var _el$37 = _tmpl$92();
          return insert(_el$37, createComponent(DomNodes, {
            get nodes() {
              return props.items;
            }
          })), _el$37;
        }
      }), null), createRenderEffect(() => setAttribute(_el$36, "aria-expanded", open())), _el$35;
    })();
  }
  function TouchGalleryTagGroup(props) {
    return (() => {
      var _el$38 = _tmpl$1(), _el$39 = _el$38.firstChild, _el$40 = _el$39.nextSibling;
      return insert(_el$39, () => props.group.namespace), _el$40.$$click = (event) => {
        if (!props.tagging || event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
          return;
        let href = (event.target instanceof Element ? event.target.closest("a.ehpeek-touch-gallery-tag") : null)?.getAttribute("href"), tag = props.group.tags.find((candidate) => candidate.url === href);
        tag && (event.preventDefault(), props.onTagOpen(tag));
      }, insert(_el$40, createComponent(For, {
        get each() {
          return props.group.tags;
        },
        children: (tag) => createComponent(TouchGalleryTag, {
          tag
        })
      })), _el$38;
    })();
  }
  function TouchGalleryTag(props) {
    return (() => {
      var _el$41 = _tmpl$102();
      return insert(_el$41, createComponent(TouchGalleryTagContent, {
        get tag() {
          return props.tag;
        }
      })), createRenderEffect((_p$) => {
        var _v$9 = props.tag.url, _v$0 = props.tag.appearance.backgroundColor, _v$1 = props.tag.appearance.borderColor, _v$10 = props.tag.appearance.color, _v$11 = props.tag.label;
        return _v$9 !== _p$.e && setAttribute(_el$41, "href", _p$.e = _v$9), _v$0 !== _p$.t && setStyleProperty(_el$41, "background-color", _p$.t = _v$0), _v$1 !== _p$.a && setStyleProperty(_el$41, "border-color", _p$.a = _v$1), _v$10 !== _p$.o && setStyleProperty(_el$41, "color", _p$.o = _v$10), _v$11 !== _p$.i && setAttribute(_el$41, "aria-label", _p$.i = _v$11), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0
      }), _el$41;
    })();
  }
  function TouchGalleryTagMenu(props) {
    let onClose = untrack(() => props.onClose), onTagUpdated = untrack(() => props.onTagUpdated), [favoriteDialogOpen, setFavoriteDialogOpen] = createSignal(!1), tagSets = state.gallery.myTagSets.reload(), [selectedTagSet, setSelectedTagSet] = createSignal(tagSets.find((option) => option.selected)?.value ?? tagSets[0]?.value ?? "1"), [tagMode, setTagMode] = createSignal("marked"), [updating, setUpdating] = createSignal(!1), [favoriteTag, setFavoriteTag] = createSignal(null);
    onMount(() => {
      let onKeyDown = (event) => {
        event.key === "Escape" && props.tag && props.onClose();
      };
      document.addEventListener("keydown", onKeyDown), onCleanup(() => {
        document.removeEventListener("keydown", onKeyDown);
      });
    });
    let updateFavoriteTag = async (tag) => {
      if (!updating()) {
        setUpdating(!0);
        try {
          let myTagsPage = tag.myTag ? await props.source.handle.removeFavoriteTag(tag) : await props.source.handle.submitFavoriteTag(tag, selectedTagSet(), tagMode()), updateAppearance = (appearance) => onTagUpdated({
            ...tag,
            appearance: appearance ? {
              ...tag.appearance,
              backgroundColor: appearance.backgroundColor,
              color: appearance.color
            } : {
              backgroundColor: "",
              borderColor: "",
              color: ""
            },
            myTag: appearance ? {
              id: appearance.id,
              tagSet: appearance.tagSet
            } : null
          });
          updateAppearance(myTagsPage.appearances.find((item) => item.name === tag.name)), setFavoriteDialogOpen(!1), onClose(), refreshMyTags(myTagsPage).then((appearances) => {
            appearances && updateAppearance(appearances.find((item) => item.name === tag.name));
          });
        } catch (error) {
          console.error("[ehpeek]", error), window.alert(error instanceof Error ? error.message : texts_default.errors.loadFailed);
        } finally {
          setUpdating(!1);
        }
      }
    };
    return [(() => {
      var _el$42 = _tmpl$112(), _el$43 = _el$42.firstChild;
      return _el$42.$$click = (event) => {
        event.target === event.currentTarget && props.onClose();
      }, _el$43.$$click = () => {
        updating() || props.onClose();
      }, insert(_el$43, createComponent(Show, {
        get when() {
          return !updating();
        },
        get fallback() {
          return createComponent(WelcomeIcon, {
            embedded: !0,
            get label() {
              return texts_default.reader.loading;
            },
            showIcon: !1
          });
        },
        get children() {
          return [createComponent(DomNode2, {
            get node() {
              return props.source.elems.tagMenuAction;
            }
          }), createComponent(Show, {
            get when() {
              return props.tag;
            },
            children: (tag) => createComponent(Show, {
              get when() {
                return !tag().myTag;
              },
              get fallback() {
                return (() => {
                  var _el$62 = _tmpl$16(), _el$63 = _el$62.firstChild;
                  return _el$62.$$click = (event) => {
                    event.stopPropagation(), updateFavoriteTag(tag());
                  }, insert(_el$62, createComponent(Icon, {
                    name: "heart",
                    filled: !0
                  }), _el$63), insert(_el$63, () => texts_default.gallery.removeFavoriteTag), _el$62;
                })();
              },
              get children() {
                var _el$60 = _tmpl$16(), _el$61 = _el$60.firstChild;
                return _el$60.$$click = () => {
                  setFavoriteTag(tag()), setFavoriteDialogOpen(!0);
                }, insert(_el$60, createComponent(Icon, {
                  name: "heart"
                }), _el$61), insert(_el$61, () => texts_default.gallery.favoriteTag), _el$60;
              }
            })
          })];
        }
      })), createRenderEffect((_p$) => {
        var _v$12 = `ehpeek-touch-gallery-tag-menu-dialog fixed inset-0 z-overlay flex items-center justify-center p-lg bg-black/65 transition-opacity duration-120 ${props.tag ? "visible opacity-100" : "invisible opacity-0 pointer-events-none"}`, _v$13 = !props.tag, _v$14 = props.tag?.label ?? "";
        return _v$12 !== _p$.e && className(_el$42, _p$.e = _v$12), _v$13 !== _p$.t && setAttribute(_el$42, "aria-hidden", _p$.t = _v$13), _v$14 !== _p$.a && setAttribute(_el$42, "aria-label", _p$.a = _v$14), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0
      }), _el$42;
    })(), createComponent(Show, {
      get when() {
        return favoriteDialogOpen();
      },
      get children() {
        var _el$44 = _tmpl$15(), _el$45 = _el$44.firstChild, _el$46 = _el$45.firstChild;
        return _el$44.$$click = (event) => {
          event.target === event.currentTarget && setFavoriteDialogOpen(!1);
        }, insert(_el$46, () => texts_default.gallery.favoriteTag), insert(_el$45, createComponent(Show, {
          get when() {
            return !updating();
          },
          get fallback() {
            return createComponent(WelcomeIcon, {
              embedded: !0,
              get label() {
                return texts_default.reader.loading;
              },
              showIcon: !1
            });
          },
          get children() {
            return [(() => {
              var _el$47 = _tmpl$122(), _el$48 = _el$47.firstChild, _el$49 = _el$48.nextSibling;
              return insert(_el$48, () => texts_default.gallery.tagCollection), _el$49.addEventListener("change", (event) => setSelectedTagSet(event.currentTarget.value)), insert(_el$49, createComponent(For, {
                each: tagSets,
                children: (option) => (() => {
                  var _el$64 = _tmpl$17();
                  return insert(_el$64, () => option.label), createRenderEffect(() => _el$64.value = option.value), _el$64;
                })()
              })), createRenderEffect(() => _el$49.value = selectedTagSet()), _el$47;
            })(), (() => {
              var _el$50 = _tmpl$13(), _el$51 = _el$50.firstChild, _el$52 = _el$51.nextSibling, _el$53 = _el$52.firstChild, _el$54 = _el$53.nextSibling, _el$55 = _el$54.nextSibling;
              return insert(_el$51, () => texts_default.gallery.tagBehavior), _el$52.addEventListener("change", (event) => setTagMode(event.currentTarget.value)), insert(_el$53, () => texts_default.gallery.markTag), insert(_el$54, () => texts_default.gallery.watchTag), insert(_el$55, () => texts_default.gallery.hideTag), createRenderEffect(() => _el$52.value = tagMode()), _el$50;
            })(), (() => {
              var _el$56 = _tmpl$14(), _el$57 = _el$56.firstChild, _el$58 = _el$57.nextSibling, _el$59 = _el$58.firstChild;
              return _el$57.$$click = () => setFavoriteDialogOpen(!1), insert(_el$57, () => texts_default.button.close), _el$58.$$click = () => {
                let tag = favoriteTag();
                tag && updateFavoriteTag(tag);
              }, insert(_el$58, createComponent(Icon, {
                name: "heart"
              }), _el$59), insert(_el$59, () => texts_default.button.confirm), _el$56;
            })()];
          }
        }), null), createRenderEffect(() => setAttribute(_el$44, "aria-label", texts_default.gallery.favoriteTag)), _el$44;
      }
    })];
  }
  function TouchGalleryNewTag(props) {
    return createComponent(DomNode2, {
      get node() {
        return props.source.elems.newTag;
      }
    });
  }
  function TouchGalleryTagContent(props) {
    let host;
    return onMount(() => {
      onCleanup(props.tag.contentSource.mirrorContentTo(host));
    }), (() => {
      var _el$65 = _tmpl$18(), _ref$2 = host;
      return typeof _ref$2 == "function" ? use(_ref$2, _el$65) : host = _el$65, _el$65;
    })();
  }
  function TouchGalleryFavoriteButton(props) {
    let [favorite, setFavorite] = createSignal(untrack(() => ({
      ...props.source.data.favorite
    }))), [open, setOpen] = createSignal(!1), [loadingState, setLoadingState] = createSignal("idle"), [options, setOptions] = createSignal([]), favorited = () => favorite().favorited, root;
    onMount(() => {
      let onClick = (event) => {
        event.target instanceof Element && root.contains(event.target) || setOpen(!1);
      };
      document.addEventListener("click", onClick), onCleanup(() => {
        document.removeEventListener("click", onClick);
      });
    });
    let openMenu = async () => {
      let currentFavorite = favorite();
      if (currentFavorite.actionUrl) {
        setOpen(!0), setLoadingState("loading");
        try {
          setOptions(await props.source.handle.loadGalleryFavoriteOptions(currentFavorite.actionUrl, currentFavorite.favorited)), setLoadingState("idle");
        } catch (error) {
          console.error("[ehpeek]", error), setLoadingState("failed");
        }
      }
    }, updateFavorite = async (option) => {
      let actionUrl = favorite().actionUrl;
      if (!(!actionUrl || loadingState() === "loading")) {
        setLoadingState("loading");
        try {
          await props.source.handle.updateGalleryFavorite(actionUrl, option.value), setFavorite({
            ...favorite(),
            color: option.color,
            favorited: option.value !== "favdel",
            label: option.value === "favdel" ? "Not Favorited" : option.label
          }), setLoadingState("idle"), setOpen(!1);
        } catch (error) {
          console.error("[ehpeek]", error), setLoadingState("failed");
        }
      }
    };
    return (() => {
      var _el$66 = _tmpl$20(), _el$67 = _el$66.firstChild, _el$68 = _el$67.firstChild, _el$69 = _el$68.nextSibling, _ref$3 = root;
      return typeof _ref$3 == "function" ? use(_ref$3, _el$66) : root = _el$66, _el$67.$$click = (event) => {
        event.stopPropagation(), open() ? setOpen(!1) : openMenu();
      }, insert(_el$68, () => favorite().label), insert(_el$69, createComponent(Icon, {
        name: "heart",
        get filled() {
          return favorited();
        }
      })), insert(_el$66, createComponent(Show, {
        get when() {
          return open();
        },
        get children() {
          var _el$70 = _tmpl$19();
          return insert(_el$70, createComponent(Show, {
            get when() {
              return loadingState() === "loading";
            },
            get children() {
              return createComponent(WelcomeIcon, {
                embedded: !0,
                get label() {
                  return texts_default.reader.loading;
                },
                showIcon: !1
              });
            }
          }), null), insert(_el$70, createComponent(Show, {
            get when() {
              return loadingState() === "failed";
            },
            get children() {
              return createComponent(TouchGalleryFavoriteStatus, {
                text: "Failed"
              });
            }
          }), null), insert(_el$70, createComponent(Show, {
            get when() {
              return loadingState() === "idle";
            },
            get children() {
              return createComponent(For, {
                get each() {
                  return options();
                },
                children: (option) => createComponent(TouchGalleryFavoriteOption, {
                  option,
                  onSelect: () => {
                    updateFavorite(option);
                  }
                })
              });
            }
          }), null), _el$70;
        }
      }), null), createRenderEffect((_p$) => {
        var _v$15 = `ehpeek-touch-gallery-primary-button ehpeek-touch-gallery-favorite-button flex min-w-0 w-full h-full min-h-xl flex-col items-center justify-center gap-md py-md px-lg border-0 bg-transparent ehp-color-site-text text-center uppercase [touch-action:manipulation] textsize-md font-700 normal-case ${favorited() ? "ehpeek-touch-gallery-favorite-on" : "ehpeek-touch-gallery-favorite-off"}`, _v$16 = favorite().color ?? void 0, _v$17 = open();
        return _v$15 !== _p$.e && className(_el$67, _p$.e = _v$15), _v$16 !== _p$.t && setStyleProperty(_el$67, "color", _p$.t = _v$16), _v$17 !== _p$.a && setAttribute(_el$67, "aria-expanded", _p$.a = _v$17), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0
      }), _el$66;
    })();
  }
  function TouchGalleryFavoriteStatus(props) {
    return (() => {
      var _el$71 = _tmpl$21();
      return insert(_el$71, () => props.text), _el$71;
    })();
  }
  function TouchGalleryFavoriteOption(props) {
    return (() => {
      var _el$72 = _tmpl$222(), _el$73 = _el$72.firstChild, _el$74 = _el$73.nextSibling, _el$75 = _el$74.nextSibling;
      return _el$72.$$click = (event) => {
        event.stopPropagation(), props.onSelect();
      }, insert(_el$73, createComponent(Icon, {
        name: "heart",
        get filled() {
          return props.option.value !== "favdel";
        }
      })), insert(_el$74, () => props.option.label), insert(_el$75, createComponent(Icon, {
        name: "check"
      })), createRenderEffect((_p$) => {
        var _v$18 = `ehpeek-touch-gallery-favorite-option flex min-h-lg items-center gap-md py-md px-lg border-0 border-b ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text font-inherit textsize-md leading-[1.2] text-left ${props.option.value === "favdel" ? "ehpeek-touch-gallery-favorite-option-remove" : ""}`, _v$19 = props.option.selected, _v$20 = props.option.color ?? void 0, _v$21 = `ml-auto flex-none ehp-color-site-text ${props.option.selected ? "visible" : "invisible"}`, _v$22 = props.option.color ?? void 0;
        return _v$18 !== _p$.e && className(_el$72, _p$.e = _v$18), _v$19 !== _p$.t && setAttribute(_el$72, "aria-pressed", _p$.t = _v$19), _v$20 !== _p$.a && setStyleProperty(_el$73, "color", _p$.a = _v$20), _v$21 !== _p$.o && className(_el$75, _p$.o = _v$21), _v$22 !== _p$.i && setStyleProperty(_el$75, "color", _p$.i = _v$22), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0
      }), _el$72;
    })();
  }
  function ratingFromPointer(clientX, element) {
    let rect = element.getBoundingClientRect(), progress = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));
    return Math.max(0.5, Math.ceil(progress * 10) / 2);
  }
  delegateEvents(["click", "pointermove"]);

  // src/components/TouchUI/FavoritesPanel.tsx
  var _tmpl$26 = /* @__PURE__ */ template('<div class="border-0 border-t border-t-[var(--color-site-border-subtle)]">'), _tmpl$27 = /* @__PURE__ */ template('<div class="box-border w-full min-w-0 overflow-hidden rounded-md border ehp-color-site-border bg-[var(--color-site-elevated)]"><button type=button class="flex box-border w-full min-h-md items-center justify-between gap-md px-md py-sm rounded-xs border-0 !bg-transparent ehp-color-site-text text-left textsize-md font-700 font-inherit cursor-pointer hover:!bg-[var(--color-site-item-hover)] active:!bg-[var(--color-site-item-hover)]"><span class="flex min-w-0 items-center gap-sm overflow-hidden"><span class="min-w-0 overflow-hidden text-ellipsis whitespace-nowrap"> [<!>]</span></span><span class="flex h-20px w-20px flex-none items-center justify-center leading-none"aria-hidden=true>'), _tmpl$35 = /* @__PURE__ */ template('<button type=button><span class="flex min-w-0 items-center gap-sm"><span class="min-w-0 overflow-hidden text-ellipsis whitespace-nowrap"> [<!>]'), _tmpl$45 = /* @__PURE__ */ template('<span class="block h-15px w-15px flex-none bg-no-repeat"aria-hidden=true>');
  function FavoritesCategorySelect(props) {
    let container, [open, setOpen] = createSignal(!1), selected = () => props.source.data.favoritesCategory?.categories.find((category) => category.selected) ?? props.source.data.favoritesCategory?.categories[0] ?? null;
    return onMount(() => {
      let closeOnOutsidePointer = (event) => {
        event.target instanceof Node && !container.contains(event.target) && setOpen(!1);
      };
      document.addEventListener("pointerdown", closeOnOutsidePointer, !0), onCleanup(() => document.removeEventListener("pointerdown", closeOnOutsidePointer, !0));
    }), (() => {
      var _el$ = _tmpl$27(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.firstChild, _el$7 = _el$5.nextSibling, _el$6 = _el$7.nextSibling, _el$8 = _el$3.nextSibling, _ref$ = container;
      return typeof _ref$ == "function" ? use(_ref$, _el$) : container = _el$, _el$2.$$click = () => setOpen((value) => !value), insert(_el$3, () => categoryIndicator(selected()?.appearance), _el$4), insert(_el$4, () => selected()?.label, _el$5), insert(_el$4, () => selected()?.count, _el$7), insert(_el$8, () => open() ? "−" : "+"), insert(_el$, createComponent(Show, {
        get when() {
          return open();
        },
        get children() {
          var _el$9 = _tmpl$26();
          return insert(_el$9, createComponent(For, {
            get each() {
              return props.source.data.favoritesCategory?.categories;
            },
            children: (category, index) => (() => {
              var _el$0 = _tmpl$35(), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild, _el$11 = _el$10.firstChild, _el$13 = _el$11.nextSibling, _el$12 = _el$13.nextSibling;
              return _el$0.$$click = () => props.source.handle.activateFavoriteCategory(index()), insert(_el$1, () => categoryIndicator(category.appearance), _el$10), insert(_el$10, () => category.label, _el$11), insert(_el$10, () => category.count, _el$13), createRenderEffect(() => className(_el$0, `flex box-border w-full min-h-md items-center px-md py-sm border-0 border-b ehp-color-site-border-subtle-b last:border-b-0 text-left textsize-md font-inherit no-underline cursor-pointer ${category.selected ? "bg-[var(--color-site-accent-hover)] ehp-color-site-accent font-700" : "!bg-transparent ehp-color-site-text hover:!bg-[var(--color-site-item-hover)]"}`)), _el$0;
            })()
          })), _el$9;
        }
      }), null), createRenderEffect(() => setAttribute(_el$2, "aria-expanded", open())), _el$;
    })();
  }
  function categoryIndicator(appearance) {
    return (() => {
      var _el$14 = _tmpl$45();
      return createRenderEffect((_$p) => style(_el$14, appearance ? {
        "background-image": appearance.backgroundImage,
        "background-position": appearance.backgroundPosition,
        "background-size": appearance.backgroundSize
      } : void 0, _$p)), _el$14;
    })();
  }
  delegateEvents(["click"]);

  // src/components/TouchUI/SearchPanel.tsx
  var _tmpl$28 = /* @__PURE__ */ template('<section class="ehpeek-touch-search-panel box-border flex w-[calc(100%_-_32px)] max-w-960px flex-col gap-md mx-auto mb-lg p-lg border ehp-color-site-border rounded-lg ehp-color-site-surface ehp-color-site-text shadow-[0_8px_24px_var(--color-shadow-panel)] font-sans">'), _tmpl$29 = /* @__PURE__ */ template('<button type=button class="appearance-none inline-flex min-h-md items-center px-md border-0 rounded-md bg-transparent ehp-color-site-accent text-left textsize-md font-700 font-inherit leading-[1.2] no-underline cursor-pointer [touch-action:manipulation] active:bg-[var(--color-site-accent-hover)]">'), _tmpl$36 = /* @__PURE__ */ template("<button>"), _tmpl$46 = /* @__PURE__ */ template('<span class="contents [&amp;>*:not([hidden])]:col-span-full">'), TOUCH_SEARCH_OPTION_CLASS = "appearance-none inline-flex min-h-md items-center px-md border-0 rounded-md bg-transparent ehp-color-site-accent text-left textsize-md font-700 font-inherit leading-[1.2] no-underline cursor-pointer [touch-action:manipulation] active:bg-[var(--color-site-accent-hover)]", TOUCH_SEARCH_ACTION_CLASS = "appearance-none inline-flex box-border w-60px h-60px items-center justify-center p-0 rounded-md border-0 bg-transparent cursor-pointer transition-[background-color,transform] duration-120 [touch-action:manipulation] active:scale-96 active:bg-[var(--color-site-item-hover)]";
  function touchSearchPanelClasses(hasClear) {
    return {
      actionMount: "contents",
      advancedPanel: "box-border w-full !p-0 ehp-color-site-text",
      category: "flex box-border w-full min-w-0 !h-lg items-center justify-center px-md border rounded-md text-white text-center textsize-md font-700 leading-[1.15] whitespace-nowrap shadow-[0_2px_6px_var(--color-shadow-control)] cursor-pointer select-none transition-opacity [touch-action:manipulation] [-webkit-tap-highlight-color:transparent] active:opacity-70 [&[data-disabled]]:opacity-40",
      categoryCell: "!p-0",
      categoryRow: "contents",
      categoryTable: "!w-full !m-0 border-collapse [&>tbody]:grid [&>tbody]:grid-cols-[repeat(auto-fit,minmax(140px,1fr))] [&>tbody]:gap-xs",
      controls: `${hasClear ? "grid-cols-[minmax(0,1fr)_60px_60px]" : "grid-cols-[minmax(0,1fr)_60px]"} grid w-full items-start gap-0 !p-0`,
      fileSearch: "box-border !w-full !m-0 !mt-0 p-lg border ehp-color-site-border rounded-md bg-[var(--color-site-elevated)] ehp-color-site-text !textsize-md text-left [&_form]:flex [&_form]:flex-col [&_form]:gap-sm [&_form>div]:!p-0 [&_.searchadv>div]:!flex-wrap [&_.searchadv>div]:!justify-start [&_.searchadv>div]:!gap-sm [&_.searchadv>div>div]:!p-sm",
      form: "flex w-full flex-col gap-md m-0 p-0",
      input: `appearance-none !box-border !w-full !h-60px min-w-0 col-span-full row-start-1 !m-0 !py-0 !pl-lg ${hasClear ? "!pr-[132px]" : "!pr-[72px]"} !border !border-[var(--color-site-border)] rounded-md !bg-[var(--color-site-elevated)] !text-[var(--color-site-text)] !text-[length:var(--font-size-md)] leading-[1.2] outline-none !focus:border-[var(--color-site-accent)] !focus:bg-[var(--color-site-elevated)] focus:shadow-[0_0_0_3px_var(--color-site-accent-hover)]`,
      optionLink: TOUCH_SEARCH_OPTION_CLASS,
      optionLinks: "flex w-full flex-wrap items-center justify-start gap-x-md gap-y-sm !p-0 !text-0",
      searchBox: "box-border !w-full !m-0 !p-0 !border-0 !text-left !textsize-md [&_.searchadv]:box-border [&_.searchadv]:!w-full [&_.searchadv]:!pt-md [&_.searchadv]:!textsize-md [&_.searchadv>div]:!flex-wrap [&_.searchadv>div]:!justify-start [&_.searchadv>div]:!gap-sm [&_.searchadv>div>div]:!p-sm"
    };
  }
  function TouchSearchPanel(props) {
    return (() => {
      var _el$ = _tmpl$28();
      return insert(_el$, createComponent(DomNode2, {
        get node() {
          return props.source.elems.searchBox;
        }
      }), null), insert(_el$, createComponent(DomNode2, {
        get node() {
          return props.source.elems.fileSearch;
        }
      }), null), insert(_el$, () => props.after, null), _el$;
    })();
  }
  function TouchSearchCategoryToggle(props) {
    let [open, setOpen] = createSignal(!1);
    return createEffect(() => props.source.handle.updateCategoryVisibility(open())), createComponent(ToggleButton, {
      get expanded() {
        return open();
      },
      get label() {
        return texts_default.search.categories;
      },
      onClick: () => setOpen((value) => !value)
    });
  }
  function TouchSearchOptionToggle(props) {
    let [open, setOpen] = createSignal(!1);
    return createComponent(ToggleButton, {
      get expanded() {
        return open();
      },
      get label() {
        return texts_default.search[props.option];
      },
      onClick: () => {
        props.option === "advancedOptions" ? props.source.handle.toggleAdvancedOptions() : props.source.handle.toggleFileSearch(), setOpen((value) => !value);
      }
    });
  }
  function ToggleButton(props) {
    return (() => {
      var _el$2 = _tmpl$29();
      return _el$2.$$click = () => props.onClick(), insert(_el$2, () => props.label), createRenderEffect(() => setAttribute(_el$2, "aria-expanded", props.expanded)), _el$2;
    })();
  }
  function TouchSearchAction(props) {
    let source = untrack(() => props.source), search = untrack(() => props.action === "search"), label = search ? source.data.searchLabel : source.data.clearLabel ?? "", original = search ? source.elems.searchSubmit : source.elems.clearButton;
    return [(() => {
      var _el$3 = _tmpl$36();
      return _el$3.$$click = (event) => {
        event.preventDefault(), search ? source.handle.activateSearch() : source.handle.clearSearchText();
      }, setAttribute(_el$3, "type", search ? "submit" : "button"), setAttribute(_el$3, "aria-label", label), setAttribute(_el$3, "title", label), insert(_el$3, createComponent(Icon, {
        name: search ? "search" : "close",
        size: 32
      })), createRenderEffect(() => className(_el$3, search ? `${TOUCH_SEARCH_ACTION_CLASS} z-1 ${source.data.hasClear ? "col-start-3" : "col-start-2"} row-start-1 ehp-color-site-accent` : `${TOUCH_SEARCH_ACTION_CLASS} z-1 col-start-2 row-start-1 ehp-color-site-text`)), _el$3;
    })(), (() => {
      var _el$4 = _tmpl$46();
      return insert(_el$4, createComponent(DomNode2, {
        node: original
      })), _el$4;
    })()];
  }
  delegateEvents(["click"]);

  // src/components/TouchUI/TopBar.tsx
  var _tmpl$30 = /* @__PURE__ */ template('<div class="ehpeek-touch-top-bar-menu-panel absolute top-[calc(100%+8px)] right-0 z-overlay flex w-240px coarse:w-[calc(100vw-32px)] max-w-[calc(100vw-24px)] coarse:max-w-360px flex-col overflow-hidden border ehp-color-site-border rounded-sm ehp-color-site-elevated">'), _tmpl$210 = /* @__PURE__ */ template('<div class="ehpeek-touch-top-bar-menu relative"><button type=button class="ehpeek-touch-top-bar-menu-button inline-flex w-68px h-68px items-center justify-center rounded-md border-0 bg-transparent ehp-color-site-text no-underline [touch-action:manipulation] active:bg-[var(--color-site-item-hover)]"aria-haspopup=menu>'), _tmpl$37 = /* @__PURE__ */ template('<nav class="ehpeek-touch-top-bar relative z-ui flex box-border w-full min-h-xl items-center justify-between py-lg pl-[max(12px,env(safe-area-inset-left,0px))] pr-[max(12px,env(safe-area-inset-right,0px))] ehp-color-site-surface ehp-color-site-text font-sans"><a class="ehpeek-touch-top-bar-project inline-flex w-68px h-68px items-center justify-center rounded-md border-0 bg-transparent ehp-color-site-text no-underline [touch-action:manipulation] active:bg-[var(--color-site-item-hover)]"></a><div class="flex items-center gap-sm"><a class="ehpeek-touch-top-bar-home inline-flex w-68px h-68px items-center justify-center rounded-md border-0 bg-transparent ehp-color-site-text no-underline [touch-action:manipulation] active:bg-[var(--color-site-item-hover)]"></a><a class="ehpeek-touch-top-bar-favorites inline-flex w-68px h-68px items-center justify-center rounded-md border-0 bg-transparent ehp-color-site-text no-underline [touch-action:manipulation] active:bg-[var(--color-site-item-hover)]"></a><button type=button class="ehpeek-touch-top-bar-settings inline-flex w-68px h-68px items-center justify-center rounded-md border-0 bg-transparent ehp-color-site-text no-underline [touch-action:manipulation] active:bg-[var(--color-site-item-hover)]">'), TOUCH_TOP_BAR_ICON_SIZE = 41;
  var TOUCH_TOP_BAR_MENU_ITEM_CLASS = "ehpeek-touch-top-bar-menu-item block box-border w-full min-h-lg coarse:min-h-88px py-md coarse:py-xl px-lg coarse:px-xl border-0 border-b ehp-color-site-border-subtle-b bg-transparent ehp-color-site-text text-left no-underline textsize-md leading-[1.2]", TOUCH_TOP_BAR_NAV_ITEM_CLASS = TOUCH_TOP_BAR_MENU_ITEM_CLASS;
  function TouchTopBarMenu(props) {
    let [open, setOpen] = createSignal(!1), root;
    return onMount(() => {
      let onClick = (event) => {
        event.target instanceof Element && root.contains(event.target) || setOpen(!1);
      };
      document.addEventListener("click", onClick), onCleanup(() => {
        document.removeEventListener("click", onClick);
      });
    }), (() => {
      var _el$ = _tmpl$210(), _el$2 = _el$.firstChild, _ref$ = root;
      return typeof _ref$ == "function" ? use(_ref$, _el$) : root = _el$, _el$2.$$click = (event) => {
        event.stopPropagation(), setOpen((value) => !value);
      }, insert(_el$2, createComponent(Icon, {
        name: "menu",
        size: TOUCH_TOP_BAR_ICON_SIZE
      })), insert(_el$, createComponent(Show, {
        get when() {
          return open();
        },
        get children() {
          var _el$3 = _tmpl$30();
          return insert(_el$3, createComponent(For, {
            get each() {
              return props.navItems;
            },
            children: (item) => {
              let Component = item.Component;
              return createComponent(Component, {});
            }
          })), _el$3;
        }
      }), null), createRenderEffect(() => setAttribute(_el$2, "aria-expanded", open())), _el$;
    })();
  }
  function TouchTopBar(props) {
    return (() => {
      var _el$4 = _tmpl$37(), _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$9 = _el$8.nextSibling;
      return insert(_el$5, createComponent(Icon, {
        name: "panda-peek",
        size: 58,
        strokeWidth: 1.8
      })), insert(_el$7, createComponent(Icon, {
        name: "home",
        size: TOUCH_TOP_BAR_ICON_SIZE
      })), insert(_el$8, createComponent(Icon, {
        name: "heart",
        size: TOUCH_TOP_BAR_ICON_SIZE
      })), _el$9.$$click = (event) => {
        event.stopPropagation(), props.onSettingsMenuOpen();
      }, insert(_el$9, createComponent(Icon, {
        name: "settings",
        size: TOUCH_TOP_BAR_ICON_SIZE
      })), insert(_el$6, createComponent(TouchTopBarMenu, {
        get navItems() {
          return props.source.elems.navItems;
        }
      }), null), createRenderEffect((_p$) => {
        var _v$ = props.source.data.homeHref, _v$2 = props.source.data.homeHref, _v$3 = props.source.data.favoritesHref;
        return _v$ !== _p$.e && setAttribute(_el$5, "href", _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$7, "href", _p$.t = _v$2), _v$3 !== _p$.a && setAttribute(_el$8, "href", _p$.a = _v$3), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0
      }), _el$4;
    })();
  }
  delegateEvents(["click"]);

  // src/eh/galleryRearrange.css
  var galleryRearrange_default = `/* Shared content gutter for the touch gallery page. */
:root {
  --touch-gallery-gutter: clamp(16px, 2.5vw, 36px);
}

/* Remove the original desktop page's minimum width and horizontal page overflow. */
html,
body {
  min-width: 0 !important;
  overflow-x: hidden !important;
  text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

/* Reset the original page shell and apply the active E-H/ExH page palette. */
body {
  box-sizing: border-box;
  padding-left: 0 !important;
  padding-right: 0 !important;
  background: var(--color-site-page) !important;
  font-size: var(--font-size-sm) !important;
  line-height: 1.35 !important;
}

/* Align enhanced and original gallery sections to one responsive content column. */
.ehpeek-touch-gallery-host,
.gpc,
body #gdt[class],
#cdiv,
.ptt,
.ptb {
  box-sizing: border-box !important;
  width: calc(100% - (var(--touch-gallery-gutter) * 2)) !important;
  max-width: none !important;
  margin-left: auto !important;
  margin-right: auto !important;
  padding-left: 0 !important;
  padding-right: 0 !important;
}

/* Keep wide thumbnail and pagination rows scrollable inside the viewport. */
body #gdt[class],
.ptt,
.ptb {
  overflow-x: auto !important;
  -webkit-overflow-scrolling: touch;
}

/* Give original thumbnail cells a consistent mobile-friendly width. */
#gdt .gdtm,
#gdt .gdtl,
#gdt > div {
  display: inline-flex !important;
  min-width: 132px !important;
  align-items: center !important;
  justify-content: center !important;
  vertical-align: top;
}

/* Center thumbnails inside their enlarged cells. */
#gdt a {
  display: flex !important;
  min-height: 150px;
  align-items: center;
  justify-content: center;
}

/* Establish readable base typography for the original comments section. */
#cdiv {
  font-size: var(--font-size-md) !important;
  line-height: 1.5 !important;
}

/* Enlarge comment bodies while allowing long content to wrap safely. */
#cdiv .c6 {
  font-size: var(--font-size-md) !important;
  line-height: 1.5 !important;
  overflow-wrap: anywhere;
}

/* Keep comment metadata and form hints visually secondary to comment bodies. */
#cdiv .c3,
#cdiv .c4,
#cdiv .c5,
#cdiv .c7,
#formdiv {
  font-size: var(--font-size-sm) !important;
  line-height: 1.4 !important;
}

/* Space the new-comment entry point and suppress the original bracket decoration. */
#postnewcomment {
  margin: 16px 0 !important;
  font-size: 0 !important;
}

/* Present the new-comment entry point as a full touch target. */
#postnewcomment a {
  display: inline-flex !important;
  min-height: 52px;
  align-items: center;
  padding: 12px 16px;
  border: 1px solid var(--color-site-border);
  border-radius: 6px;
  background: var(--color-site-elevated);
  color: var(--color-site-accent);
  font-size: var(--font-size-md);
  text-decoration: none;
}

/* Let comment form fields wrap into a vertical layout on narrow screens. */
#cdiv form {
  display: flex !important;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

/* Make the comment editor full-width and comfortable for touch typing. */
#cdiv textarea,
#commenttext {
  display: block !important;
  box-sizing: border-box !important;
  width: 100% !important;
  min-height: 160px !important;
  flex: 1 0 100%;
  padding: 16px !important;
  border-radius: 6px !important;
  font: inherit !important;
  font-size: var(--font-size-md) !important;
  line-height: 1.5 !important;
}

/* Normalize original form controls to the touch sizing scale. */
#cdiv button,
#cdiv input[type="button"],
#cdiv input[type="submit"],
#cdiv input[type="text"],
#cdiv select {
  box-sizing: border-box !important;
  min-height: 52px !important;
  padding: 12px 16px !important;
  border-radius: 6px !important;
  font: inherit !important;
  font-size: var(--font-size-md) !important;
}

/* Let comment form actions share the available row width evenly. */
#cdiv button,
#cdiv input[type="button"],
#cdiv input[type="submit"] {
  flex: 1 1 180px;
  cursor: pointer;
}

/* Keep short text fields useful without forcing them wider than the viewport. */
#cdiv input[type="text"] {
  min-width: min(100%, 240px);
}
`;

  // ehpeek-uno-css:ehpeek:uno.css
  var ehpeek_uno_default = `/* layer: preflights */
*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;}::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;}
/* layer: shortcuts */
.scrollbar-hidden{scrollbar-width:none;-ms-overflow-style:none;}
.container{width:100%;}
.container\\!{width:100% !important;}
.z-overlay{z-index:1100;}
.z-reader{z-index:1200;}
.z-ui{z-index:1000;}
.mb-lg{margin-bottom:16px;}
.mb-md{margin-bottom:12px;}
.mb-sm{margin-bottom:8px;}
.mb-xs{margin-bottom:4px;}
.ml-md{margin-left:12px;}
.ml-sm{margin-left:8px;}
.mt-md{margin-top:12px;}
.mt-xs{margin-top:4px;}
.scrollbar-hidden::-webkit-scrollbar{display:none;}
.\\!h-lg{height:52px !important;}
.h-lg{height:52px;}
.h-md{height:40px;}
.\\!h-sm{height:32px !important;}
.h-sm{height:32px;}
.\\[\\&_\\.auto-complete-item\\]\\:min-h-lg .auto-complete-item,
.min-h-lg{min-height:52px;}
.min-h-md{min-height:40px;}
.min-h-sm{min-height:32px;}
.min-h-xl{min-height:80px;}
.w-lg{width:52px;}
.w-md{width:40px;}
.\\!w-sm{width:32px !important;}
.w-sm{width:32px;}
.gap-lg{gap:16px;}
.gap-md{gap:12px;}
.\\[\\&_\\.searchadv\\>div\\]\\:\\!gap-sm .searchadv>div{gap:8px !important;}
.\\[\\&_form\\]\\:gap-sm form,
.gap-sm{gap:8px;}
.gap-xl{gap:24px;}
.\\[\\&\\>tbody\\]\\:gap-xs>tbody,
.gap-xs{gap:4px;}
.gap-x-lg{column-gap:16px;}
.gap-x-md{column-gap:12px;}
.gap-y-md{row-gap:12px;}
.gap-y-sm{row-gap:8px;}
.ehp-color-site-border{border-color:var(--color-site-border);}
.ehp-color-spinner{border-color:var(--color-border);border-top-color:var(--color-accent);}
.ehp-color-site-border-subtle-b{border-bottom-color:var(--color-site-border-subtle);}
.rounded-lg{border-radius:8px;}
.rounded-md{border-radius:6px;}
.rounded-sm{border-radius:4px;}
.rounded-xl{border-radius:10px;}
.rounded-xs{border-radius:3px;}
.focus-visible\\:rounded-xs:focus-visible{border-radius:3px;}
.ehp-color-reader{background-color:var(--color-reader-background);color:var(--color-reader-text);}
.ehp-color-site-elevated{background-color:var(--color-site-elevated);--un-shadow:0 8px 24px var(--un-shadow-color, var(--color-shadow-elevated));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.ehp-color-site-page{background-color:var(--color-site-page);}
.ehp-color-site-surface{background-color:var(--color-site-surface);}
.p-lg{padding:16px;}
.p-md{padding:12px;}
.\\[\\&_\\.searchadv\\>div\\>div\\]\\:\\!p-sm .searchadv>div>div{padding:8px !important;}
.p-sm{padding:8px;}
.p-xl{padding:24px;}
.\\[\\&_\\.auto-complete-item\\]\\:\\!px-lg .auto-complete-item{padding-left:16px !important;padding-right:16px !important;}
.px-lg{padding-left:16px;padding-right:16px;}
.px-md{padding-left:12px;padding-right:12px;}
.px-sm{padding-left:8px;padding-right:8px;}
.px-xl{padding-left:24px;padding-right:24px;}
.py-lg{padding-top:16px;padding-bottom:16px;}
.py-md{padding-top:12px;padding-bottom:12px;}
.\\!py-sm,
.\\[\\&_\\.auto-complete-item\\]\\:\\!py-sm .auto-complete-item{padding-top:8px !important;padding-bottom:8px !important;}
.py-sm{padding-top:8px;padding-bottom:8px;}
.py-xs{padding-top:4px;padding-bottom:4px;}
.pb-lg{padding-bottom:16px;}
.pb-md{padding-bottom:12px;}
.pb-sm{padding-bottom:8px;}
.pb-xs{padding-bottom:4px;}
.\\!pl-lg{padding-left:16px !important;}
.pl-md{padding-left:12px;}
.pr-sm{padding-right:8px;}
.pt-lg{padding-top:16px;}
.\\[\\&_\\.searchadv\\]\\:\\!pt-md .searchadv{padding-top:12px !important;}
.pt-md{padding-top:12px;}
.pt-xl{padding-top:24px;}
.textsize-lg{font-size:var(--font-size-lg);}
.\\!textsize-md,
.\\[\\&_\\.searchadv\\]\\:\\!textsize-md .searchadv{font-size:var(--font-size-md) !important;}
.textsize-md{font-size:var(--font-size-md);}
.textsize-sm{font-size:var(--font-size-sm);}
.textsize-xl{font-size:var(--font-size-xl);}
.ehp-color-site-accent{color:var(--color-site-accent);}
.ehp-color-site-text{color:var(--color-site-text);}
.ehp-color-text{color:var(--color-text);}
.hover\\:ehp-color-site-accent:hover{color:var(--color-site-accent);}
@media (pointer: coarse){
.coarse\\:\\!h-md{height:40px !important;}
.coarse\\:\\!w-md{width:40px !important;}
.coarse\\:gap-lg{gap:16px;}
.coarse\\:gap-xl{gap:24px;}
.coarse\\:rounded-lg{border-radius:8px;}
.coarse\\:rounded-md{border-radius:6px;}
.coarse\\:p-md{padding:12px;}
.coarse\\:px-lg{padding-left:16px;padding-right:16px;}
.coarse\\:px-xl{padding-left:24px;padding-right:24px;}
.coarse\\:py-lg{padding-top:16px;padding-bottom:16px;}
.coarse\\:py-md{padding-top:12px;padding-bottom:12px;}
.coarse\\:py-xl{padding-top:24px;padding-bottom:24px;}
}
@media (min-width: 640px){
.container{max-width:640px;}
.container\\!{max-width:640px !important;}
}
@media (min-width: 768px){
.container{max-width:768px;}
.container\\!{max-width:768px !important;}
}
@media (min-width: 1024px){
.container{max-width:1024px;}
.container\\!{max-width:1024px !important;}
}
@media (min-width: 1280px){
.container{max-width:1280px;}
.container\\!{max-width:1280px !important;}
}
@media (min-width: 1536px){
.container{max-width:1536px;}
.container\\!{max-width:1536px !important;}
}
/* layer: default */
.\\[--progress-bar-fill\\:0\\%\\]{--progress-bar-fill:0%;}
.\\[--progress-bar-track-direction\\:to_right\\]{--progress-bar-track-direction:to right;}
.\\[-webkit-appearance\\:none\\]{-webkit-appearance:none;}
.\\[-webkit-overflow-scrolling\\:touch\\]{-webkit-overflow-scrolling:touch;}
.\\[-webkit-tap-highlight-color\\:transparent\\]{-webkit-tap-highlight-color:transparent;}
.\\[-webkit-user-drag\\:none\\]{-webkit-user-drag:none;}
.\\[accent-color\\:var\\(--color-text\\)\\]{accent-color:var(--color-text);}
.\\[appearance\\:none\\]{appearance:none;}
.\\[contain\\:inline-size\\]{contain:inline-size;}
.\\[direction\\:ltr\\]{direction:ltr;}
.\\[font-variant-numeric\\:tabular-nums\\]{font-variant-numeric:tabular-nums;}
.\\[overflow-wrap\\:anywhere\\],
.break-anywhere{overflow-wrap:anywhere;}
.\\[touch-action\\:manipulation\\]{touch-action:manipulation;}
.\\[touch-action\\:none\\],
.touch-none,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:touch-none{touch-action:none;}
.\\[unicode-bidi\\:plaintext\\]{unicode-bidi:plaintext;}
.pointer-events-auto{pointer-events:auto;}
.\\[\\&\\[data-open\\=false\\]\\]\\:pointer-events-none[data-open=false],
.pointer-events-none{pointer-events:none;}
.visible{visibility:visible;}
.invisible{visibility:hidden;}
.absolute{position:absolute;}
.fixed{position:fixed;}
.relative{position:relative;}
.static{position:static;}
.inset-0{inset:0;}
.inset-y-0{top:0;bottom:0;}
.bottom-\\[calc\\(12px\\+env\\(safe-area-inset-bottom\\,0px\\)\\)\\]{bottom:calc(12px + env(safe-area-inset-bottom,0px));}
.bottom-\\[calc\\(max\\(16px\\,env\\(safe-area-inset-bottom\\,0px\\)\\)_\\+_64px\\)\\]{bottom:calc(max(16px,env(safe-area-inset-bottom,0px)) + 64px);}
.left-\\[max\\(10px\\,env\\(safe-area-inset-left\\,0px\\)\\)\\]{left:max(10px,env(safe-area-inset-left,0px));}
.left-\\[max\\(12px\\,env\\(safe-area-inset-left\\,0px\\)\\)\\]{left:max(12px,env(safe-area-inset-left,0px));}
.left-0{left:0;}
.left-1\\/2{left:50%;}
.right-\\[max\\(12px\\,env\\(safe-area-inset-right\\,0px\\)\\)\\]{right:max(12px,env(safe-area-inset-right,0px));}
.right-\\[max\\(16px\\,env\\(safe-area-inset-right\\,0px\\)\\)\\]{right:max(16px,env(safe-area-inset-right,0px));}
.right-0{right:0;}
.right-10px{right:10px;}
.right-24px{right:24px;}
.right-2px{right:2px;}
.right-auto{right:auto;}
.top-\\[calc\\(100\\%\\+8px\\)\\]{top:calc(100% + 8px);}
.top-\\[calc\\(10px\\+env\\(safe-area-inset-top\\,0px\\)\\)\\]{top:calc(10px + env(safe-area-inset-top,0px));}
.top-\\[calc\\(70px\\+env\\(safe-area-inset-top\\,0px\\)\\)\\]{top:calc(70px + env(safe-area-inset-top,0px));}
.top-0{top:0;}
.top-1\\/2{top:50%;}
.top-24px{top:24px;}
.top-48px{top:48px;}
.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;}
.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;line-clamp:3;}
.line-clamp-4{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;line-clamp:4;}
.z-\\[1150\\]{z-index:1150;}
.z-\\[1200\\]{z-index:1200;}
.z-1{z-index:1;}
.z-2{z-index:2;}
.z-3{z-index:3;}
.z-4{z-index:4;}
.\\[\\&\\>tbody\\]\\:grid>tbody,
.grid{display:grid;}
.\\[\\&\\>\\*\\:not\\(\\[hidden\\]\\)\\]\\:col-span-full>*:not([hidden]),
.col-span-full{grid-column:1/-1;}
.col-start-2{grid-column-start:2;}
.col-start-3{grid-column-start:3;}
.row-start-1{grid-row-start:1;}
.\\[\\&\\>tbody\\]\\:grid-cols-\\[repeat\\(auto-fit\\,minmax\\(140px\\,1fr\\)\\)\\]>tbody{grid-template-columns:repeat(auto-fit,minmax(140px,1fr));}
.grid-cols-\\[1fr_1fr\\]{grid-template-columns:1fr 1fr;}
.grid-cols-\\[minmax\\(0\\,1fr\\)_60px_60px\\]{grid-template-columns:minmax(0,1fr) 60px 60px;}
.grid-cols-\\[minmax\\(0\\,1fr\\)_60px\\]{grid-template-columns:minmax(0,1fr) 60px;}
.grid-cols-\\[minmax\\(0\\,35fr\\)_minmax\\(0\\,65fr\\)\\]{grid-template-columns:minmax(0,35fr) minmax(0,65fr);}
.grid-cols-\\[minmax\\(120px\\,38\\%\\)_minmax\\(0\\,1fr\\)\\]{grid-template-columns:minmax(120px,38%) minmax(0,1fr);}
.grid-cols-\\[minmax\\(76px\\,20\\%\\)_minmax\\(0\\,1fr\\)\\]{grid-template-columns:minmax(76px,20%) minmax(0,1fr);}
.grid-cols-\\[repeat\\(3\\,minmax\\(0\\,1fr\\)\\)\\],
.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr));}
.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr));}
.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr));}
.float-right{float:right;}
.\\!float-none{float:none !important;}
.\\!m-0{margin:0 !important;}
.m-0{margin:0;}
.m12{margin:3rem;}
.m15{margin:3.75rem;}
.m16\\.2{margin:4.05rem;}
.m17{margin:4.25rem;}
.m20{margin:5rem;}
.m3{margin:0.75rem;}
.m5{margin:1.25rem;}
.m7{margin:1.75rem;}
.m8{margin:2rem;}
.m9{margin:2.25rem;}
.mx-auto{margin-left:auto;margin-right:auto;}
.\\!mt-0{margin-top:0 !important;}
.mb-0{margin-bottom:0;}
.mb-10px{margin-bottom:10px;}
.ml-\\[max\\(14px\\,env\\(safe-area-inset-left\\,0px\\)\\)\\]{margin-left:max(14px,env(safe-area-inset-left,0px));}
.ml-auto{margin-left:auto;}
.mr-\\[max\\(14px\\,env\\(safe-area-inset-right\\,0px\\)\\)\\]{margin-right:max(14px,env(safe-area-inset-right,0px));}
.mt--18px{margin-top:-18px;}
.mt-0{margin-top:0;}
.mt-1px{margin-top:1px;}
.mt-2px{margin-top:2px;}
.mt-auto{margin-top:auto;}
.last\\:mb-0:last-child{margin-bottom:0;}
.\\!box-border{box-sizing:border-box !important;}
.\\[\\&_\\.auto-complete-item\\]\\:box-border .auto-complete-item,
.\\[\\&_\\.searchadv\\]\\:box-border .searchadv,
.box-border{box-sizing:border-box;}
.block{display:block;}
.inline-block{display:inline-block;}
.contents{display:contents;}
.flow-root{display:flow-root;}
.\\!hidden{display:none !important;}
.hidden{display:none;}
.aspect-\\[2\\/3\\]{aspect-ratio:2/3;}
.\\!h-60px{height:60px !important;}
.\\!h-auto{height:auto !important;}
.\\!max-h-\\[60dvh\\]{max-height:60dvh !important;}
.\\!max-w-full{max-width:100% !important;}
.\\!min-w-0{min-width:0 !important;}
.\\!w-full,
.\\[\\&_\\.searchadv\\]\\:\\!w-full .searchadv{width:100% !important;}
.h-\\[120px\\]{height:120px;}
.h-\\[2\\.4em\\]{height:2.4em;}
.h-\\[var\\(--reader-frame-height\\)\\]{height:var(--reader-frame-height);}
.h-\\[var\\(--reader-page-height\\)\\]{height:var(--reader-page-height);}
.h-108px{height:108px;}
.h-10px{height:10px;}
.h-15px{height:15px;}
.h-20px{height:20px;}
.h-48px{height:48px;}
.h-60px{height:60px;}
.h-64px{height:64px;}
.h-68px{height:68px;}
.h-full,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:h-full{height:100%;}
.h1{height:0.25rem;}
.max-h-\\[60dvh\\]{max-height:60dvh;}
.max-h-\\[calc\\(100dvh-32px\\)\\]{max-height:calc(100dvh - 32px);}
.max-h-\\[calc\\(100vh-48px\\)\\]{max-height:calc(100vh - 48px);}
.max-h-full{max-height:100%;}
.max-h-screen{max-height:100vh;}
.max-w-\\[calc\\(100vw-24px\\)\\]{max-width:calc(100vw - 24px);}
.max-w-\\[calc\\(100vw-48px\\)\\]{max-width:calc(100vw - 48px);}
.max-w-\\[min\\(78vw\\,320px\\)\\]{max-width:min(78vw,320px);}
.max-w-\\[min\\(86vw\\,760px\\)\\]{max-width:min(86vw,760px);}
.max-w-420px{max-width:420px;}
.max-w-480px{max-width:480px;}
.max-w-960px{max-width:960px;}
.max-w-full{max-width:100%;}
.max-w-none{max-width:none;}
.max-w-screen{max-width:100vw;}
.min-h-\\[clamp\\(260px\\,42vh\\,340px\\)\\]{min-height:clamp(260px,42vh,340px);}
.min-h-87px{min-height:87px;}
.min-h-full{min-height:100%;}
.min-w-0{min-width:0;}
.min-w-285px{min-width:285px;}
.min-w-48px{min-width:48px;}
.min-w-64px{min-width:64px;}
.w-\\[calc\\(100\\%_-_32px\\)\\]{width:calc(100% - 32px);}
.w-\\[min\\(86vw\\,360px\\)\\]{width:min(86vw,360px);}
.w-\\[min\\(92vw\\,420px\\)\\]{width:min(92vw,420px);}
.w-\\[var\\(--reader-frame-width\\)\\]{width:var(--reader-frame-width);}
.w-10px{width:10px;}
.w-15px{width:15px;}
.w-18px{width:18px;}
.w-20px{width:20px;}
.w-240px{width:240px;}
.w-320px{width:320px;}
.w-32px{width:32px;}
.w-3px{width:3px;}
.w-42px{width:42px;}
.w-60px{width:60px;}
.w-64px{width:64px;}
.w-68px{width:68px;}
.w-full,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:w-full{width:100%;}
.w-max{width:max-content;}
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:min-h-0{min-height:0;}
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:w-auto{width:auto;}
.\\[\\&_form\\]\\:flex form,
.flex{display:flex;}
.inline-flex{display:inline-flex;}
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:flex-\\[0_0_100\\%\\]{flex:0 0 100%;}
.flex-1{flex:1 1 0%;}
.flex-none{flex:none;}
.flex-row,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:flex-row{flex-direction:row;}
.\\[\\&_form\\]\\:flex-col form,
.flex-col{flex-direction:column;}
.\\[\\&_\\.searchadv\\>div\\]\\:\\!flex-wrap .searchadv>div{flex-wrap:wrap !important;}
.flex-wrap{flex-wrap:wrap;}
.table{display:table;}
.border-collapse{border-collapse:collapse;}
.border-separate{border-collapse:separate;}
.border-spacing-4px{--un-border-spacing-x:4px;--un-border-spacing-y:4px;border-spacing:var(--un-border-spacing-x) var(--un-border-spacing-y);}
.origin-center{transform-origin:center;}
.-translate-x-1\\/2{--un-translate-x:-50%;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
.-translate-y-1\\/2{--un-translate-y:-50%;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
.\\[\\&\\[data-open\\=false\\]\\]\\:translate-y-\\[calc\\(100\\%\\+16px\\)\\][data-open=false]{--un-translate-y:calc(100% + 16px);transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
.active\\:scale-96:active{--un-scale-x:0.96;--un-scale-y:0.96;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
.active\\:scale-98:active{--un-scale-x:0.98;--un-scale-y:0.98;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
.transform{transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}
.animate-spin{animation:spin 1s linear infinite;}
.cursor-default{cursor:default;}
.disabled\\:cursor-default:disabled{cursor:default;}
.cursor-pointer{cursor:pointer;}
.cursor-grab{cursor:grab;}
.\\[\\&\\[data-dragging\\=true\\]\\]\\:cursor-grabbing[data-dragging=true]{cursor:grabbing;}
.active\\:cursor-grabbing:active{cursor:grabbing;}
.touch-pan-y{--un-pan-y:pan-y;touch-action:var(--un-pan-x) var(--un-pan-y) var(--un-pinch-zoom);}
.\\[\\&\\[data-dragging\\=true\\]\\]\\:select-none[data-dragging=true],
.select-none,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:select-none{-webkit-user-select:none;user-select:none;}
.resize{resize:both;}
.appearance-none{-webkit-appearance:none;appearance:none;}
.items-start{align-items:flex-start;}
.items-center,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:items-center{align-items:center;}
.items-stretch{align-items:stretch;}
.self-center{align-self:center;}
.self-stretch{align-self:stretch;}
.\\[\\&_\\.searchadv\\>div\\]\\:\\!justify-start .searchadv>div{justify-content:flex-start !important;}
.justify-start{justify-content:flex-start;}
.justify-end{justify-content:flex-end;}
.justify-center{justify-content:center;}
.justify-between{justify-content:space-between;}
.justify-self-stretch{justify-self:stretch;}
.gap-0{gap:0;}
.gap-18px{gap:18px;}
.gap-1px{gap:1px;}
.gap-4px{gap:4px;}
.gap-6px{gap:6px;}
.gap-8px{gap:8px;}
.overflow-auto{overflow:auto;}
.overflow-hidden,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:overflow-hidden{overflow:hidden;}
.overflow-visible{overflow:visible;}
.\\!overflow-x-hidden{overflow-x:hidden !important;}
.overflow-x-auto{overflow-x:auto;}
.overflow-x-hidden{overflow-x:hidden;}
.overflow-y-auto{overflow-y:auto;}
.overscroll-contain{overscroll-behavior:contain;}
.overscroll-x-contain{overscroll-behavior-x:contain;}
.scroll-auto{scroll-behavior:auto;}
.text-ellipsis{text-overflow:ellipsis;}
.whitespace-normal{white-space:normal;}
.whitespace-nowrap{white-space:nowrap;}
.break-normal{overflow-wrap:normal;word-break:normal;}
.\\!border{border-width:1px !important;}
.\\!border-0{border-width:0px !important;}
.border{border-width:1px;}
.border-0{border-width:0px;}
.border-4,
.border-4px{border-width:4px;}
.border-b{border-bottom-width:1px;}
.border-l{border-left-width:1px;}
.border-l-8{border-left-width:8px;}
.border-t{border-top-width:1px;}
.last\\:border-b-0:last-child{border-bottom-width:0px;}
.\\!border-\\[var\\(--color-site-border\\)\\]{border-color:var(--color-site-border) !important;}
.\\!border-transparent{border-color:transparent !important;}
.border-\\[var\\(--color-border\\)\\]{border-color:var(--color-border);}
.border-\\[var\\(--color-danger-border\\)\\]{border-color:var(--color-danger-border);}
.border-\\[var\\(--color-reader-border\\)\\]{border-color:var(--color-reader-border);}
.border-\\[var\\(--color-site-accent\\)\\]{border-color:var(--color-site-accent);}
.border-\\[var\\(--color-site-border-subtle\\)\\]{border-color:var(--color-site-border-subtle);}
.border-\\[var\\(--color-site-swipe-border\\)\\]{border-color:var(--color-site-swipe-border);}
.hover\\:border-\\[var\\(--color-site-border\\)\\]:hover{border-color:var(--color-site-border);}
.\\!focus\\:border-\\[var\\(--color-site-accent\\)\\]:focus{border-color:var(--color-site-accent) !important;}
.focus\\:border-\\[var\\(--color-site-accent\\)\\]:focus{border-color:var(--color-site-accent);}
.border-l-\\[var\\(--color-site-border-subtle\\)\\]{border-left-color:var(--color-site-border-subtle);}
.border-l-\\[var\\(--color-site-page\\)\\]{border-left-color:var(--color-site-page);}
.border-t-\\[var\\(--color-reader-accent\\)\\]{border-top-color:var(--color-reader-accent);}
.border-t-\\[var\\(--color-site-border-subtle\\)\\]{border-top-color:var(--color-site-border-subtle);}
.rounded-3px{border-radius:3px;}
.rounded-full{border-radius:9999px;}
.rounded-l-md{border-top-left-radius:0.375rem;border-bottom-left-radius:0.375rem;}
.border-solid{border-style:solid;}
.\\!bg-\\[color-mix\\(in_srgb\\,var\\(--color-site-page\\)_82\\%\\,black\\)\\]{background-color:color-mix(in srgb,var(--color-site-page) 82%,black) !important;}
.\\!bg-\\[var\\(--color-site-elevated\\)\\]{background-color:var(--color-site-elevated) !important;}
.\\!bg-transparent,
.\\[\\&_\\*\\]\\:\\!bg-transparent *{background-color:transparent !important;}
.bg-\\[var\\(--color-background\\)\\]{background-color:var(--color-background);}
.bg-\\[var\\(--color-badge\\)\\]{background-color:var(--color-badge);}
.bg-\\[var\\(--color-control\\)\\]{background-color:var(--color-control);}
.bg-\\[var\\(--color-danger-soft\\)\\]{background-color:var(--color-danger-soft);}
.bg-\\[var\\(--color-loading\\)\\]{background-color:var(--color-loading);}
.bg-\\[var\\(--color-reader-border\\)\\]{background-color:var(--color-reader-border);}
.bg-\\[var\\(--color-reader-scrollbar\\)\\]{background-color:var(--color-reader-scrollbar);}
.bg-\\[var\\(--color-reader-surface\\)\\]{background-color:var(--color-reader-surface);}
.bg-\\[var\\(--color-site-accent-hover\\)\\]{background-color:var(--color-site-accent-hover);}
.bg-\\[var\\(--color-site-accent\\)\\]{background-color:var(--color-site-accent);}
.bg-\\[var\\(--color-site-elevated\\)\\]{background-color:var(--color-site-elevated);}
.bg-\\[var\\(--color-site-item-hover\\)\\]{background-color:var(--color-site-item-hover);}
.bg-\\[var\\(--color-site-surface\\)\\]{background-color:var(--color-site-surface);}
.bg-\\[var\\(--color-site-swipe-background\\)\\]{background-color:var(--color-site-swipe-background);}
.bg-\\[var\\(--color-state-off\\)\\]{background-color:var(--color-state-off);}
.bg-\\[var\\(--color-state-on\\)\\]{background-color:var(--color-state-on);}
.bg-black\\/65{background-color:rgb(0 0 0 / 0.65);}
.bg-transparent{background-color:transparent;}
.hover\\:\\!bg-\\[var\\(--color-site-item-hover\\)\\]:hover{background-color:var(--color-site-item-hover) !important;}
.hover\\:\\!bg-transparent:hover{background-color:transparent !important;}
.hover\\:bg-\\[var\\(--color-badge\\)\\]:hover{background-color:var(--color-badge);}
.hover\\:bg-\\[var\\(--color-site-accent-hover\\)\\]:hover{background-color:var(--color-site-accent-hover);}
.hover\\:bg-\\[var\\(--color-site-item-hover\\)\\]:hover{background-color:var(--color-site-item-hover);}
.\\!focus\\:bg-\\[var\\(--color-site-elevated\\)\\]:focus{background-color:var(--color-site-elevated) !important;}
.active\\:\\!bg-\\[var\\(--color-site-item-hover\\)\\]:active{background-color:var(--color-site-item-hover) !important;}
.active\\:\\!bg-transparent:active{background-color:transparent !important;}
.active\\:bg-\\[var\\(--color-site-accent-hover\\)\\]:active{background-color:var(--color-site-accent-hover);}
.active\\:bg-\\[var\\(--color-site-item-hover\\)\\]:active{background-color:var(--color-site-item-hover);}
.bg-no-repeat{background-repeat:no-repeat;}
.object-contain{object-fit:contain;}
.object-center{object-position:center;}
.\\!p-0,
.\\[\\&_form\\>div\\]\\:\\!p-0 form>div{padding:0 !important;}
.p-0,
#ehpeek-reader[data-view-mode=paged] .\\[\\#ehpeek-reader\\[data-view-mode\\=paged\\]_\\&\\]\\:p-0{padding:0;}
.\\!py-0{padding-top:0 !important;padding-bottom:0 !important;}
.px{padding-left:1rem;padding-right:1rem;}
.px-\\[0\\.6em\\]{padding-left:0.6em;padding-right:0.6em;}
.px-0{padding-left:0;padding-right:0;}
.px-10px{padding-left:10px;padding-right:10px;}
.py-0{padding-top:0;padding-bottom:0;}
.py-56px{padding-top:56px;padding-bottom:56px;}
.py-6px{padding-top:6px;padding-bottom:6px;}
.\\!pr-\\[132px\\]{padding-right:132px !important;}
.\\!pr-\\[72px\\]{padding-right:72px !important;}
.pb-48px{padding-bottom:48px;}
.pb-72px{padding-bottom:72px;}
.pl-\\[max\\(12px\\,env\\(safe-area-inset-left\\,0px\\)\\)\\]{padding-left:max(12px,env(safe-area-inset-left,0px));}
.pl-\\[max\\(16px\\,env\\(safe-area-inset-left\\,0px\\)\\)\\]{padding-left:max(16px,env(safe-area-inset-left,0px));}
.pl-6px{padding-left:6px;}
.pr-\\[max\\(12px\\,env\\(safe-area-inset-right\\,0px\\)\\)\\]{padding-right:max(12px,env(safe-area-inset-right,0px));}
.pr-\\[max\\(16px\\,env\\(safe-area-inset-right\\,0px\\)\\)\\]{padding-right:max(16px,env(safe-area-inset-right,0px));}
.pt-2px{padding-top:2px;}
.text-center{text-align:center;}
.\\!text-left{text-align:left !important;}
.text-left{text-align:left;}
.align-middle{vertical-align:middle;}
.\\!text-\\[length\\:var\\(--font-size-md\\)\\]{font-size:var(--font-size-md) !important;}
.\\!text-0{font-size:0 !important;}
.\\[\\&_\\.auto-complete-item\\]\\:\\!text-\\[length\\:var\\(--font-size-lg\\)\\] .auto-complete-item{font-size:var(--font-size-lg) !important;}
.\\!text-\\[var\\(--color-site-text\\)\\]{color:var(--color-site-text) !important;}
.\\!text-\\[var\\(--ehpeek-my-tag-color\\)\\],
.\\[\\&\\>a\\]\\:\\!text-\\[var\\(--ehpeek-my-tag-color\\)\\]>a{color:var(--ehpeek-my-tag-color) !important;}
.text-\\[clamp\\(88px\\,25vw\\,180px\\)\\]{font-size:clamp(88px,25vw,180px);}
.text-\\[var\\(--color-background\\)\\]{color:var(--color-background);}
.text-\\[var\\(--color-danger\\)\\]{color:var(--color-danger);}
.text-\\[var\\(--color-muted\\)\\]{color:var(--color-muted);}
.text-\\[var\\(--color-rating-submitted\\)\\]{color:var(--color-rating-submitted);}
.text-\\[var\\(--color-reader-muted\\)\\]{color:var(--color-reader-muted);}
.text-\\[var\\(--color-site-surface\\)\\]{color:var(--color-site-surface);}
.text-\\[var\\(--color-site-text\\)\\]{color:var(--color-site-text);}
.text-\\[var\\(--color-text\\)\\]{color:var(--color-text);}
.text-white{--un-text-opacity:1;color:rgb(255 255 255 / var(--un-text-opacity));}
.visited\\:\\!text-\\[var\\(--color-site-text\\)\\]:visited{color:var(--color-site-text) !important;}
.hover\\:\\!text-\\[var\\(--color-site-text\\)\\]:hover{color:var(--color-site-text) !important;}
.active\\:\\!text-\\[var\\(--color-site-text\\)\\]:active{color:var(--color-site-text) !important;}
.\\[\\&_\\.auto-complete-text\\]\\:\\!text-inherit .auto-complete-text,
.\\[\\&_\\*\\]\\:\\!text-inherit *{color:inherit !important;}
.font-400{font-weight:400;}
.font-600{font-weight:600;}
.font-700{font-weight:700;}
.font-850{font-weight:850;}
.\\[\\&_\\.auto-complete-item\\]\\:\\!leading-\\[1\\.25\\] .auto-complete-item{line-height:1.25 !important;}
.\\[\\&_\\.auto-complete-text\\]\\:\\!leading-inherit .auto-complete-text{line-height:inherit !important;}
.leading-\\[1\\.1\\]{line-height:1.1;}
.leading-\\[1\\.15\\]{line-height:1.15;}
.leading-\\[1\\.16\\]{line-height:1.16;}
.leading-\\[1\\.2\\]{line-height:1.2;}
.leading-\\[1\\.3\\]{line-height:1.3;}
.leading-\\[1\\.35\\]{line-height:1.35;}
.leading-\\[1\\.4\\]{line-height:1.4;}
.leading-\\[1\\],
.leading-none{line-height:1;}
.leading-1{line-height:0.25rem;}
.font-inherit{font-family:inherit;}
.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;}
.font-sans{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";}
.uppercase{text-transform:uppercase;}
.lowercase{text-transform:lowercase;}
.normal-case{text-transform:none;}
.no-underline{text-decoration:none;}
.hover\\:no-underline:hover{text-decoration:none;}
.active\\:no-underline:active{text-decoration:none;}
.\\[\\&\\[data-disabled\\]\\]\\:opacity-40[data-disabled],
.opacity-40{opacity:0.4;}
.\\[\\&\\[data-open\\=false\\]\\]\\:opacity-0[data-open=false],
.opacity-0{opacity:0;}
.opacity-100{opacity:1;}
.opacity-70{opacity:0.7;}
.opacity-72{opacity:0.72;}
.opacity-75{opacity:0.75;}
.opacity-78{opacity:0.78;}
.opacity-82{opacity:0.82;}
.active\\:opacity-70:active{opacity:0.7;}
.disabled\\:opacity-40:disabled{opacity:0.4;}
.disabled\\:opacity-50:disabled{opacity:0.5;}
.shadow-\\[0_2px_10px_var\\(--color-shadow-control\\)\\]{--un-shadow:0 2px 10px var(--un-shadow-color, var(--color-shadow-control));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-\\[0_2px_10px_var\\(--color-shadow-panel\\)\\]{--un-shadow:0 2px 10px var(--un-shadow-color, var(--color-shadow-panel));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-\\[0_2px_6px_var\\(--color-shadow-control\\)\\]{--un-shadow:0 2px 6px var(--un-shadow-color, var(--color-shadow-control));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-\\[0_2px_8px_var\\(--color-shadow-panel\\)\\]{--un-shadow:0 2px 8px var(--un-shadow-color, var(--color-shadow-panel));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-\\[0_4px_14px_var\\(--color-shadow-floating\\)\\]{--un-shadow:0 4px 14px var(--un-shadow-color, var(--color-shadow-floating));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-\\[0_6px_20px_var\\(--color-shadow-floating\\)\\]{--un-shadow:0 6px 20px var(--un-shadow-color, var(--color-shadow-floating));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-\\[0_8px_24px_var\\(--color-shadow-panel\\)\\]{--un-shadow:0 8px 24px var(--un-shadow-color, var(--color-shadow-panel));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-none{--un-shadow:0 0 var(--un-shadow-color, rgb(0 0 0 / 0));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.shadow-xl{--un-shadow:var(--un-shadow-inset) 0 20px 25px -5px var(--un-shadow-color, rgb(0 0 0 / 0.1)),var(--un-shadow-inset) 0 8px 10px -6px var(--un-shadow-color, rgb(0 0 0 / 0.1));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.focus\\:shadow-\\[0_0_0_3px_var\\(--color-site-accent-hover\\)\\]:focus{--un-shadow:0 0 0 3px var(--un-shadow-color, var(--color-site-accent-hover));box-shadow:var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);}
.focus-visible\\:outline-2:focus-visible{outline-width:2px;}
.focus-visible\\:outline-\\[var\\(--color-site-accent\\)\\]:focus-visible{outline-color:var(--color-site-accent);}
.focus-visible\\:outline-offset-3px:focus-visible{outline-offset:3px;}
.focus-visible\\:outline:focus-visible{outline-style:solid;}
.outline-none{outline:2px solid transparent;outline-offset:2px;}
.hover\\:brightness-108:hover{--un-brightness:brightness(1.08);filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia);}
.filter{filter:var(--un-blur) var(--un-brightness) var(--un-contrast) var(--un-drop-shadow) var(--un-grayscale) var(--un-hue-rotate) var(--un-invert) var(--un-saturate) var(--un-sepia);}
.backdrop-filter{-webkit-backdrop-filter:var(--un-backdrop-blur) var(--un-backdrop-brightness) var(--un-backdrop-contrast) var(--un-backdrop-grayscale) var(--un-backdrop-hue-rotate) var(--un-backdrop-invert) var(--un-backdrop-opacity) var(--un-backdrop-saturate) var(--un-backdrop-sepia);backdrop-filter:var(--un-backdrop-blur) var(--un-backdrop-brightness) var(--un-backdrop-contrast) var(--un-backdrop-grayscale) var(--un-backdrop-hue-rotate) var(--un-backdrop-invert) var(--un-backdrop-opacity) var(--un-backdrop-saturate) var(--un-backdrop-sepia);}
.transition-\\[background-color\\,color\\]{transition-property:background-color,color;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-\\[background-color\\,transform\\]{transition-property:background-color,transform;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-\\[border-color\\,background-color\\,color\\]{transition-property:border-color,background-color,color;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-\\[filter\\,transform\\,box-shadow\\]{transition-property:filter,transform,box-shadow;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-\\[opacity\\,transform\\]{transition-property:opacity,transform;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-\\[width\\,opacity\\]{transition-property:width,opacity;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-\\[width\\]{transition-property:width;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms;}
.duration-120{transition-duration:120ms;}
.duration-160{transition-duration:160ms;}
.ease-in-out{transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);}
.will-change-transform{will-change:transform;}
@media (min-width: 760px){
.desktop\\:text-\\[clamp\\(72px\\,10vw\\,140px\\)\\]{font-size:clamp(72px,10vw,140px);}
}
@media (orientation: landscape){
.landscape\\:left-auto{left:auto;}
.landscape\\:right-10px{right:10px;}
.landscape\\:top-\\[calc\\(62px\\+env\\(safe-area-inset-top\\,0px\\)\\)\\]{top:calc(62px + env(safe-area-inset-top,0px));}
.landscape\\:max-w-\\[calc\\(100vw-20px\\)\\]{max-width:calc(100vw - 20px);}
.landscape\\:min-w-0{min-width:0;}
.landscape\\:translate-x-0{--un-translate-x:0;transform:translateX(var(--un-translate-x)) translateY(var(--un-translate-y)) translateZ(var(--un-translate-z)) rotate(var(--un-rotate)) rotateX(var(--un-rotate-x)) rotateY(var(--un-rotate-y)) rotateZ(var(--un-rotate-z)) skewX(var(--un-skew-x)) skewY(var(--un-skew-y)) scaleX(var(--un-scale-x)) scaleY(var(--un-scale-y)) scaleZ(var(--un-scale-z));}
.landscape\\:text-right{text-align:right;}
}
@media (orientation: landscape) and (pointer: coarse){
.coarse-landscape\\:right-8px{right:8px;}
.coarse-landscape\\:top-\\[calc\\(74px\\+env\\(safe-area-inset-top\\,0px\\)\\)\\]{top:calc(74px + env(safe-area-inset-top,0px));}
.coarse-landscape\\:max-w-\\[calc\\(100vw-16px\\)\\]{max-width:calc(100vw - 16px);}
}
@media (pointer: coarse){
.coarse\\:right-8px{right:8px;}
.coarse\\:top-\\[calc\\(80px\\+env\\(safe-area-inset-top\\,0px\\)\\)\\]{top:calc(80px + env(safe-area-inset-top,0px));}
.coarse\\:top-\\[calc\\(8px\\+env\\(safe-area-inset-top\\,0px\\)\\)\\]{top:calc(8px + env(safe-area-inset-top,0px));}
.coarse\\:top-8px{top:8px;}
.coarse\\:block{display:block;}
.coarse\\:h-\\[200px\\]{height:200px;}
.coarse\\:h-18px{height:18px;}
.coarse\\:h-64px{height:64px;}
.coarse\\:max-h-\\[calc\\(100dvh-16px\\)\\]{max-height:calc(100dvh - 16px);}
.coarse\\:max-w-360px{max-width:360px;}
.coarse\\:max-w-480px{max-width:480px;}
.coarse\\:min-h-64px{min-height:64px;}
.coarse\\:min-h-88px{min-height:88px;}
.coarse\\:min-w-64px{min-width:64px;}
.coarse\\:w-\\[calc\\(100vw-16px\\)\\]{width:calc(100vw - 16px);}
.coarse\\:w-\\[calc\\(100vw-32px\\)\\]{width:calc(100vw - 32px);}
.coarse\\:w-12px{width:12px;}
.coarse\\:w-18px{width:18px;}
.coarse\\:w-24px{width:24px;}
.coarse\\:w-48px{width:48px;}
.coarse\\:border-spacing-6px{--un-border-spacing-x:6px;--un-border-spacing-y:6px;border-spacing:var(--un-border-spacing-x) var(--un-border-spacing-y);}
}`;

  // src/theme.css
  var theme_default = `:root {
  --font-size-xs: 10px;
  --font-size-sm: 13px;
  --font-size-md: 16px;
  --font-size-lg: 24px;
  --font-size-xl: 32px;

  /* --color-background: #070707;
  --color-surface: #151515;
  --color-elevated: #232323;
  --color-text: #f3f3f3;
  --color-accent: #4da3ff; */
  --color-background: var(--color-site-page);
  --color-surface: var(--color-site-surface);
  --color-elevated: var(--color-site-elevated);
  --color-text: var(--color-site-text);
  --color-accent: var(--color-site-accent);
  --color-danger: #ffb2a7;
  --color-state-on: #4ec46a;
  --color-state-off: #8c8f96;
  --color-rating-submitted: #8595ff;
  --color-shadow: #000000;

  --color-site-favorite-0: #646464;
  --color-site-favorite-1: #ff6868;
  --color-site-favorite-2: #ffa561;
  --color-site-favorite-3: #fff56b;
  --color-site-favorite-4: #68ff8b;
  --color-site-favorite-5: #cdff84;
  --color-site-favorite-6: #8afeff;
  --color-site-favorite-7: #7268ff;
  --color-site-favorite-8: #ac57fe;
  --color-site-favorite-9: #fe50c8;

  --color-muted: color-mix(in srgb, var(--color-text) 72%, transparent);
  /* --color-border: color-mix(in srgb, var(--color-text) 18%, transparent); */
  --color-border: var(--color-site-border);
  --color-track: color-mix(in srgb, var(--color-text) 34%, var(--color-background));
  --color-danger-soft: color-mix(in srgb, var(--color-danger) 12%, transparent);
  --color-danger-border: color-mix(in srgb, var(--color-danger) 64%, transparent);
  --color-control: color-mix(in srgb, var(--color-elevated) 88%, transparent);
  --color-badge: color-mix(in srgb, var(--color-background) 34%, transparent);
  --color-shadow-panel: color-mix(in srgb, var(--color-shadow) 32%, transparent);
  --color-shadow-elevated: color-mix(in srgb, var(--color-shadow) 38%, transparent);
  --color-shadow-control: color-mix(in srgb, var(--color-shadow) 40%, transparent);
  --color-shadow-floating: color-mix(in srgb, var(--color-shadow) 42%, transparent);

  --color-site-accent-hover: color-mix(in srgb, var(--color-site-accent) 12%, transparent);
  --color-site-border-subtle: color-mix(in srgb, var(--color-site-border) 16%, transparent);
  --color-site-item-hover: color-mix(in srgb, var(--color-site-text) 8%, transparent);
  --color-loading: color-mix(
    in srgb,
    color-mix(in srgb, var(--color-site-elevated) 80%, var(--color-site-surface)) 92%,
    transparent
  );
  --color-site-swipe-background: color-mix(in srgb, var(--color-site-elevated) 94%, transparent);
  --color-site-swipe-border: color-mix(in srgb, var(--color-site-border) 38%, transparent);
}

@media (pointer: coarse) {
  :root {
    --font-size-xs: 12px;
    --font-size-sm: 18px;
    --font-size-md: 26px;
    --font-size-lg: 32px;
    --font-size-xl: 42px;
  }
}

:root[data-ehpeek-site="e-hentai"] {
  --color-site-page: #e3e0d1;
  --color-site-surface: #edebdf;
  --color-site-elevated: #f3f0e0;
  --color-site-text: #5c0d11;
  --color-site-accent: #8f4701;
  --color-site-border: #5c0d12;
}

:root[data-ehpeek-site="exhentai"] {
  --color-site-page: #34353b;
  --color-site-surface: #4f535b;
  --color-site-elevated: #3f4249;
  --color-site-text: #f1f1f1;
  --color-site-accent: #f0b35a;
  --color-site-border: #8d7454;
}
`;

  // src/components/animation.ts
  var SCROLL_ANIMATION_MS = 180, SCROLL_EASING_POWER = 3, ANIMATION_FRAME_MIN_DELTA_MS = 1, ANIMATION_FRAME_MAX_DELTA_MS = 32, SCROLL_FLING_MIN_VELOCITY = 0.35, SCROLL_FLING_STOP_VELOCITY = 0.02, SCROLL_FLING_DECAY = 45e-4, ScrollAnimator = class {
    constructor(axis) {
      this.axis = axis;
      this.frame = null;
    }
    scrollTo(scroller, target, motion = "instant", onComplete) {
      if (this.cancel(), motion !== "animated") {
        this.setScrollPosition(scroller, target), onComplete?.();
        return;
      }
      this.scrollWithRaf(scroller, target, onComplete);
    }
    cancel() {
      this.frame !== null && (window.cancelAnimationFrame(this.frame), this.frame = null);
    }
    scrollWithRaf(scroller, target, onComplete) {
      let start = this.scrollPosition(scroller), delta = target - start, lastFrameTime = performance.now(), animationTime = 0, step = (time) => {
        let elapsed = clamp(time - lastFrameTime, ANIMATION_FRAME_MIN_DELTA_MS, ANIMATION_FRAME_MAX_DELTA_MS);
        lastFrameTime = time, animationTime += elapsed;
        let progress = clamp(animationTime / SCROLL_ANIMATION_MS, 0, 1), eased = 1 - Math.pow(1 - progress, SCROLL_EASING_POWER);
        if (this.setScrollPosition(scroller, start + delta * eased), progress >= 1) {
          this.frame = null, onComplete?.();
          return;
        }
        this.frame = window.requestAnimationFrame(step);
      };
      this.frame = window.requestAnimationFrame(step);
    }
    scrollPosition(scroller) {
      return this.axis === "x" ? scroller.scrollLeft : scroller.scrollTop;
    }
    setScrollPosition(scroller, value) {
      this.axis === "x" ? scroller.scrollLeft = value : scroller.scrollTop = value;
    }
  }, ScrollFlingAnimator = class {
    constructor() {
      this.frame = null;
      this.velocityY = 0;
      this.lastFrameTime = 0;
    }
    start(options) {
      if (this.cancel(), Math.abs(options.initialVelocityY) < SCROLL_FLING_MIN_VELOCITY)
        return;
      this.velocityY = options.initialVelocityY, this.lastFrameTime = performance.now();
      let step = (time) => {
        if (!options.canRun()) {
          this.cancel();
          return;
        }
        let elapsed = clamp(time - this.lastFrameTime, ANIMATION_FRAME_MIN_DELTA_MS, ANIMATION_FRAME_MAX_DELTA_MS);
        this.lastFrameTime = time;
        let previousScrollTop = options.scroller.scrollTop;
        if (options.setScrollTop(previousScrollTop + this.velocityY * elapsed), options.scroller.scrollTop === previousScrollTop) {
          this.cancel(), options.onStop();
          return;
        }
        if (this.velocityY *= Math.exp(-SCROLL_FLING_DECAY * elapsed), Math.abs(this.velocityY) < SCROLL_FLING_STOP_VELOCITY) {
          this.cancel(), options.onStop();
          return;
        }
        this.frame = window.requestAnimationFrame(step);
      };
      this.frame = window.requestAnimationFrame(step);
    }
    cancel() {
      this.frame !== null && (window.cancelAnimationFrame(this.frame), this.frame = null), this.velocityY = 0;
    }
  };

  // src/components/Reader/Viewport.tsx
  var _tmpl$31 = /* @__PURE__ */ template('<div class="w-full h-full overflow-auto overscroll-contain scroll-auto touch-pan-y cursor-grab scrollbar-hidden [&amp;[data-dragging=true]]:cursor-grabbing [&amp;[data-dragging=true]]:select-none [#ehpeek-reader[data-view-mode=paged]_&amp;]:overflow-hidden [#ehpeek-reader[data-view-mode=paged]_&amp;]:touch-none [#ehpeek-reader[data-view-mode=paged]_&amp;]:select-none"tabindex=-1><main class="ehpeek-reader-page-strip flex flex-col w-full min-h-full py-56px px-0 pb-72px [#ehpeek-reader[data-view-mode=paged]_&amp;]:flex-row [#ehpeek-reader[data-view-mode=paged]_&amp;]:w-auto [#ehpeek-reader[data-view-mode=paged]_&amp;]:h-full [#ehpeek-reader[data-view-mode=paged]_&amp;]:min-h-0 [#ehpeek-reader[data-view-mode=paged]_&amp;]:p-0">'), _tmpl$211 = /* @__PURE__ */ template('<section class="ehpeek-page flex w-full h-[var(--reader-page-height)] items-start justify-center pb-sm [#ehpeek-reader[data-view-mode=paged]_&amp;]:flex-[0_0_100%] [#ehpeek-reader[data-view-mode=paged]_&amp;]:w-full [#ehpeek-reader[data-view-mode=paged]_&amp;]:h-full [#ehpeek-reader[data-view-mode=paged]_&amp;]:items-center [#ehpeek-reader[data-view-mode=paged]_&amp;]:p-0"><div class="flex w-[var(--reader-frame-width)] h-[var(--reader-frame-height)] items-center justify-center overflow-hidden [#ehpeek-reader[data-view-mode=paged]_&amp;]:w-full [#ehpeek-reader[data-view-mode=paged]_&amp;]:h-full">'), _tmpl$38 = /* @__PURE__ */ template('<div class="max-w-[min(86vw,760px)] break-anywhere [direction:ltr] [unicode-bidi:plaintext]">'), _tmpl$47 = /* @__PURE__ */ template('<button type=button class="ehpeek-reader-page-reload inline-flex w-64px h-64px items-center justify-center border border-[var(--color-danger-border)] rounded-full bg-[var(--color-danger-soft)] text-[var(--color-danger)] cursor-pointer font-sans textsize-lg font-700 leading-1 active:scale-96 [touch-action:manipulation]"><span aria-hidden=true>↻'), _tmpl$54 = /* @__PURE__ */ template("<div>"), _tmpl$64 = /* @__PURE__ */ template('<span class="flex w-full h-full flex-col items-center justify-center gap-xl overflow-hidden"aria-hidden=true><span class="block max-w-full flex-none m-0 p-0 text-center leading-[1] whitespace-nowrap [direction:ltr] [unicode-bidi:plaintext]"></span><span class="block w-md h-md flex-none box-border animate-spin rounded-full border-4px border-solid border-[var(--color-reader-border)] border-t-[var(--color-reader-accent)]">'), FALLBACK_ASPECT_RATIO = 1.42, DEFAULT_DECODED_IMAGE_CACHE_LIMIT = 24, DECODED_IMAGE_CACHE_BYTES = 96 * 1024 * 1024;
  function pageWindowNumbers(currentPageNum, windowSize) {
    let numbers = [];
    for (let offset = -windowSize; offset <= windowSize; offset += 1)
      numbers.push(currentPageNum + offset);
    return numbers;
  }
  function PagesViewport(props) {
    let [slots, setSlots] = createSignal([]), [revision, setRevision] = createSignal(0), horizontalAnimator = new ScrollAnimator("x"), flingAnimator = new ScrollFlingAnimator(), pageSlots2 = [], scroller, scrollerApi, dragStartPosition = null, resizeFrame = null, moveRequestToken = 0, disposed = !1, decodedImageCacheLimit = Math.max(0, Math.floor(untrack(() => props.decodedImageCacheLimit) ?? DEFAULT_DECODED_IMAGE_CACHE_LIMIT)), cachedImages = /* @__PURE__ */ new Map(), cachedImageBytes = 0, refresh = () => setRevision((value) => value + 1), slotFor = (pageNum) => pageSlots2.find((slot) => slot.pageNum === pageNum), viewportWidth = () => scrollerApi.viewportWidth(), viewportHeight = () => scrollerApi.viewportHeight(), scrollTop = () => scrollerApi.scrollTop(), visualSlotIndex = (index, slotCount) => props.mode === "paged" && props.readDirection === "rtl" ? slotCount - 1 - index : index, applySlotSize = (slot) => {
      let frameWidth = Math.max(1, viewportWidth());
      slot.frameWidth = frameWidth, slot.frameHeight = Math.ceil(frameWidth * pageSlotAspectRatio(slot));
    }, renderSlots = () => {
      for (let slot of pageSlots2)
        applySlotSize(slot);
      setSlots(pageSlots2.slice()), refresh();
    }, refreshSlot = (slot) => {
      applySlotSize(slot), refresh();
    }, pageOffset = (pageNum) => {
      let elements = slotFor(pageNum)?.elements;
      return elements ? scrollerApi.slotOffset(elements, props.mode) : null;
    }, verticalScrollBoundsForElements = (firstElements, lastElements) => {
      let bounds = {};
      if (firstElements && (bounds.min = scrollerApi.slotTop(firstElements)), lastElements) {
        let lastElementsRect = lastElements.node.getBoundingClientRect(), lastElementsTop = scrollerApi.slotTop(lastElements);
        bounds.max = lastElementsTop + lastElementsRect.height - viewportHeight();
      }
      return bounds.min === void 0 && bounds.max === void 0 ? null : (bounds.min !== void 0 && bounds.max !== void 0 && (bounds.max = Math.max(bounds.min, bounds.max)), bounds);
    }, verticalScrollBounds = () => props.mode !== "scroll" ? null : verticalScrollBoundsForElements(slotFor(1)?.elements, props.window.totalPages ? slotFor(props.window.totalPages + 1)?.elements : null), moveToTop = (nextScrollTop) => {
      scrollerApi.moveToTop(nextScrollTop, verticalScrollBounds());
    }, pageNumAtPoint = (point) => {
      let element = document.elementFromPoint(point.clientX, point.clientY), pageNode = element instanceof Element ? element.closest(".ehpeek-page") : null;
      if (!pageNode || !scroller.contains(pageNode))
        return null;
      let pageNum = Number(pageNode.dataset.ehpeekPageNum || "");
      return Number.isFinite(pageNum) ? pageNum : null;
    }, stopMotion = () => {
      moveRequestToken += 1, dragStartPosition = null, flingAnimator.cancel(), horizontalAnimator.cancel();
    }, performPageMove = (pageNum, motion, onComplete) => {
      let delta = pageOffset(pageNum);
      return delta === null ? !1 : (props.mode === "paged" ? horizontalAnimator.scrollTo(scroller, scrollerApi.scrollLeft() + delta, motion, onComplete) : (moveToTop(scrollTop() + delta), onComplete?.()), !0);
    }, moveToPage = (pageNum, motion = "instant", onComplete) => {
      let requestToken = ++moveRequestToken;
      performPageMove(pageNum, motion, onComplete) || queueMicrotask(() => {
        untrack(() => {
          !disposed && requestToken === moveRequestToken && performPageMove(pageNum, motion, onComplete);
        });
      });
    }, resizePages = () => {
      for (let slot of pageSlots2)
        applySlotSize(slot);
      refresh();
    }, gestureDragging = createPointerGestureElement(() => scroller ?? null, () => props.callbacks.pointer), syncWindow = (options) => {
      let oldSlots = new Map(pageSlots2.map((slot) => [slot.pageNum, slot])), nextSlots = [];
      for (let pageNum of pageWindowNumbers(options.currentPageNum, options.windowSize)) {
        let kind = pageSlotKind(pageNum, options.totalPages), oldSlot = oldSlots.get(pageNum), slot = oldSlot && oldSlot.kind === kind ? oldSlot : pageSlot(pageNum, kind);
        if (!oldSlot && kind === "page") {
          let cached = cachedImages.get(pageNum);
          cached && (cachedImages.delete(pageNum), cachedImageBytes -= cached.bytes, slot.state = "ready", slot.image = cached.image, slot.width = cached.width, slot.height = cached.height);
        }
        if (kind === "page") {
          let page = options.pages.get(pageNum);
          page && applyPageMetaToSlot(slot, page);
        } else
          clearNonPageSlotMeta(slot);
        nextSlots.push(slot);
      }
      let nextSet = new Set(nextSlots);
      for (let slot of pageSlots2)
        if (!nextSet.has(slot)) {
          if (slot.kind === "page" && slot.state === "ready" && slot.image) {
            let width = positiveNumber(slot.image.naturalWidth) ?? slot.width, height = positiveNumber(slot.image.naturalHeight) ?? slot.height, cached = {
              bytes: width && height ? width * height * 4 : 0,
              height,
              image: slot.image,
              width
            }, previous = cachedImages.get(slot.pageNum);
            previous && (cachedImageBytes -= previous.bytes), cachedImages.delete(slot.pageNum), cachedImages.set(slot.pageNum, cached), cachedImageBytes += cached.bytes;
          }
          slot.token += 1;
        }
      for (; cachedImages.size > decodedImageCacheLimit || cachedImageBytes > DECODED_IMAGE_CACHE_BYTES; ) {
        let oldest = cachedImages.entries().next().value;
        if (!oldest)
          break;
        cachedImages.delete(oldest[0]), cachedImageBytes -= oldest[1].bytes, oldest[1].image.removeAttribute("src");
      }
      pageSlots2 = nextSlots, pageSlots2.forEach((slot, index) => {
        slot.index = index;
      }), renderSlots();
    }, actions = {
      focus: () => scroller.focus({
        preventScroll: !0
      }),
      isDragging: gestureDragging,
      beginDrag() {
        stopMotion(), dragStartPosition = props.mode === "paged" ? scrollerApi.scrollLeft() : scrollTop();
      },
      cancelDrag: () => {
        dragStartPosition = null;
      },
      moveDrag(delta) {
        return dragStartPosition === null ? !1 : (props.mode === "paged" ? scrollerApi.moveToLeft(dragStartPosition - delta.dx) : moveToTop(dragStartPosition - delta.dy), !0);
      },
      resetPosition: () => scrollerApi.resetPosition(),
      stopMotion,
      markPageLoading(pageNum) {
        let slot = slotFor(pageNum);
        return !slot || slot.kind !== "page" || slot.state !== "idle" ? null : (slot.state = "loading", slot.errorMessage = null, slot.token += 1, refreshSlot(slot), slot.token);
      },
      async loadPageImage(pageNum, token, slotImage) {
        let image = pageImageDom(pageNum, slotImage);
        await loadImage(image);
        let slot = slotFor(pageNum);
        return !slot || slot.token !== token || !slot.elements ? !1 : (slot.state = "ready", slot.image = image, slot.errorMessage = null, slot.width = positiveNumber(image.naturalWidth) ?? slotImage.width, slot.height = positiveNumber(image.naturalHeight) ?? slotImage.height, refreshSlot(slot), !0);
      },
      setPageError(pageNum, token, errorMessage) {
        let slot = slotFor(pageNum);
        return !slot || slot.token !== token ? !1 : (slot.state = "error", slot.image = null, slot.errorMessage = errorMessage, refresh(), !0);
      },
      resetPageError(pageNum) {
        let slot = slotFor(pageNum);
        return !slot || slot.kind !== "page" || slot.state !== "error" ? !1 : (slot.state = "idle", slot.errorMessage = null, refreshSlot(slot), !0);
      },
      moveToPage,
      moveToTop,
      scrollTop,
      viewportWidth,
      pageOffset,
      centerPageNum() {
        for (let slot of pageSlots2)
          if (slot.elements && slot.kind !== "blank" && scrollerApi.slotContainsViewportTarget(slot.elements))
            return slot.pageNum;
        return null;
      },
      isHitEndPage(point) {
        let pageNum = pageNumAtPoint(point);
        return pageNum !== null && slotFor(pageNum)?.kind === "end";
      },
      pageNumAtPoint,
      startVerticalFlingFromDragVelocity(dragVelocityY, onStop) {
        flingAnimator.start({
          scroller,
          initialVelocityY: -dragVelocityY,
          setScrollTop: moveToTop,
          canRun: () => !disposed && props.mode === "scroll",
          onStop
        });
      }
    };
    return untrack(() => props.actionsRef(actions)), createEffect(() => syncWindow(props.window)), onMount(() => {
      let observer = new ResizeObserver(() => {
        resizeFrame === null && (resizeFrame = window.requestAnimationFrame(() => {
          resizeFrame = null, resizePages();
        }));
      });
      observer.observe(scroller), onCleanup(() => observer.disconnect());
    }), onCleanup(() => {
      disposed = !0, stopMotion();
      for (let cached of cachedImages.values())
        cached.image.removeAttribute("src");
      cachedImages.clear(), cachedImageBytes = 0, resizeFrame !== null && (window.cancelAnimationFrame(resizeFrame), resizeFrame = null);
    }), (() => {
      var _el$ = _tmpl$31(), _el$2 = _el$.firstChild;
      return _el$.addEventListener("wheel", (event) => {
        let delta = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
        props.callbacks.onWheel(delta, event);
      }), _el$.addEventListener("scroll", () => props.callbacks.onNativeScroll()), use((element) => {
        scroller = element, scrollerApi = createPagesScroller(element);
      }, _el$), insert(_el$2, createComponent(For, {
        get each() {
          return slots();
        },
        children: (slot) => createComponent(PageSlotView, {
          slot,
          get revision() {
            return revision();
          },
          get visualIndex() {
            return visualSlotIndex(slot.index, slots().length);
          },
          onReloadPage: (pageNum) => props.callbacks.onReloadPage(pageNum)
        })
      })), _el$;
    })();
  }
  function PageSlotView(props) {
    let node, frame, content = createMemo(() => (props.revision, {
      pageNum: props.slot.pageNum,
      kind: props.slot.kind,
      state: props.slot.state,
      errorMessage: props.slot.errorMessage ?? void 0
    })), image = createMemo(() => (props.revision, props.slot.state === "ready" ? props.slot.image : null)), slotStyle = createMemo(() => (props.revision, {
      "--reader-page-height": `${props.slot.frameHeight + 8}px`,
      "--reader-frame-width": `${props.slot.frameWidth}px`,
      "--reader-frame-height": `${props.slot.frameHeight}px`,
      order: String(props.visualIndex)
    }));
    return onCleanup(() => {
      props.slot.elements?.node === node && (props.slot.elements = null);
    }), (() => {
      var _el$3 = _tmpl$211(), _el$4 = _el$3.firstChild, _ref$ = node;
      return typeof _ref$ == "function" ? use(_ref$, _el$3) : node = _el$3, use((element) => {
        frame = element, props.slot.elements = {
          node,
          frame
        };
      }, _el$4), insert(_el$4, createComponent(Show, {
        get when() {
          return image();
        },
        keyed: !0,
        get fallback() {
          return createComponent(PageSlotPlaceholder, {
            get content() {
              return content();
            },
            get text() {
              return slotPlaceholderText(content());
            },
            get onReloadPage() {
              return props.onReloadPage;
            }
          });
        },
        children: (currentImage) => currentImage
      })), createRenderEffect((_p$) => {
        var _v$ = String(props.slot.pageNum), _v$2 = slotStyle();
        return _v$ !== _p$.e && setAttribute(_el$3, "data-ehpeek-page-num", _p$.e = _v$), _p$.t = style(_el$3, _v$2, _p$.t), _p$;
      }, {
        e: void 0,
        t: void 0
      }), _el$3;
    })();
  }
  function PageSlotPlaceholder(props) {
    let stop = (event) => {
      event.preventDefault(), event.stopPropagation();
    };
    return (() => {
      var _el$5 = _tmpl$54();
      return insert(_el$5, createComponent(Show, {
        get when() {
          return props.content.state === "error";
        },
        get fallback() {
          return createComponent(Show, {
            get when() {
              return props.content.state === "loading";
            },
            get fallback() {
              return props.text;
            },
            get children() {
              var _el$8 = _tmpl$64(), _el$9 = _el$8.firstChild;
              return insert(_el$9, () => props.text), _el$8;
            }
          });
        },
        get children() {
          return [(() => {
            var _el$6 = _tmpl$38();
            return insert(_el$6, () => props.text), _el$6;
          })(), (() => {
            var _el$7 = _tmpl$47();
            return _el$7.$$click = (event) => {
              stop(event), props.onReloadPage(props.content.pageNum);
            }, _el$7.$$pointerdown = stop, _el$7;
          })()];
        }
      })), createRenderEffect((_p$) => {
        var _v$3 = props.content.state === "error" ? "flex w-full h-full flex-col items-center justify-center gap-lg bg-[var(--color-reader-surface)] p-xl text-[var(--color-danger)] text-center textsize-md font-700 leading-1" : "relative flex w-full h-full items-center justify-center bg-[var(--color-reader-surface)] text-[var(--color-reader-muted)] text-center " + (props.content.kind === "end" ? "p-xl [direction:ltr] textsize-xl font-700 leading-[1.3] [unicode-bidi:plaintext]" : "text-[clamp(88px,25vw,180px)] desktop:text-[clamp(72px,10vw,140px)] font-mono font-850 leading-[1] [font-variant-numeric:tabular-nums]"), _v$4 = props.content.state === "loading" ? "status" : void 0, _v$5 = props.content.state === "loading" ? `${texts_default.reader.loading} ${props.text}` : void 0;
        return _v$3 !== _p$.e && className(_el$5, _p$.e = _v$3), _v$4 !== _p$.t && setAttribute(_el$5, "role", _p$.t = _v$4), _v$5 !== _p$.a && setAttribute(_el$5, "aria-label", _p$.a = _v$5), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0
      }), _el$5;
    })();
  }
  function pageImageDom(pageNum, slotImage) {
    let image = document.createElement("img");
    return image.className = "block w-[var(--reader-frame-width)] h-[var(--reader-frame-height)] object-contain select-none [-webkit-user-drag:none] [#ehpeek-reader[data-view-mode=paged]_&]:w-full [#ehpeek-reader[data-view-mode=paged]_&]:h-full", image.alt = `Page ${pageNum}`, image.decoding = "async", image.loading = "eager", image.draggable = !1, image.setAttribute("fetchpriority", slotImage.highPriority ? "high" : "low"), image.src = slotImage.imageUrl, slotImage.width && slotImage.height && (image.width = slotImage.width, image.height = slotImage.height), image;
  }
  async function loadImage(image) {
    if (!(image.complete && image.naturalWidth > 0)) {
      await new Promise((resolve, reject) => {
        image.addEventListener("load", () => resolve(), {
          once: !0
        }), image.addEventListener("error", () => reject(new Error(texts_default.errors.imageLoadFailed)), {
          once: !0
        });
      });
      try {
        await image.decode();
      } catch {
      }
    }
  }
  function createPagesScroller(element) {
    let clampedTop = (scrollTop, bounds) => bounds ? clamp(scrollTop, bounds.min ?? Number.NEGATIVE_INFINITY, bounds.max ?? Number.POSITIVE_INFINITY) : scrollTop;
    return {
      element,
      resetPosition() {
        element.scrollLeft = 0, element.scrollTop = 0;
      },
      scrollLeft() {
        return element.scrollLeft;
      },
      scrollTop() {
        return element.scrollTop;
      },
      viewportWidth() {
        return element.clientWidth || window.innerWidth || 1;
      },
      viewportHeight() {
        return element.clientHeight;
      },
      moveToLeft(scrollLeft) {
        element.scrollLeft = scrollLeft;
      },
      moveToTop(scrollTop, bounds) {
        element.scrollTop = clampedTop(scrollTop, bounds);
      },
      slotTop(elements) {
        let elementsRect = elements.node.getBoundingClientRect(), scrollerRect = element.getBoundingClientRect();
        return element.scrollTop + elementsRect.top - scrollerRect.top;
      },
      slotOffset(elements, mode) {
        let pageRect = elements.node.getBoundingClientRect(), scrollerRect = element.getBoundingClientRect();
        return mode === "paged" ? pageRect.left - scrollerRect.left : pageRect.top - scrollerRect.top;
      },
      slotContainsViewportTarget(elements) {
        let scrollerRect = element.getBoundingClientRect(), target = scrollerRect.top + Math.min(80, scrollerRect.height * 0.14), rect = elements.node.getBoundingClientRect();
        return rect.top <= target && rect.bottom > target;
      }
    };
  }
  function slotPlaceholderText(content) {
    if (content.state === "error") {
      let suffix = content.errorMessage ? `: ${content.errorMessage}` : "";
      return `${texts_default.reader.failedPrefix} ${content.pageNum}${suffix}`;
    }
    return content.kind === "end" ? texts_default.reader.end : content.kind === "blank" ? "" : String(content.pageNum);
  }
  function pageSlotKind(pageNum, totalPages) {
    return pageNum < 1 ? "blank" : totalPages && pageNum === totalPages + 1 ? "end" : totalPages && pageNum > totalPages + 1 ? "blank" : "page";
  }
  function pageSlot(pageNum, kind) {
    return {
      pageNum,
      index: 0,
      kind,
      state: kind === "page" ? "idle" : "ready",
      aspectRatio: FALLBACK_ASPECT_RATIO,
      width: null,
      height: null,
      image: null,
      errorMessage: null,
      frameWidth: 1,
      frameHeight: Math.ceil(FALLBACK_ASPECT_RATIO),
      elements: null,
      token: 0
    };
  }
  function applyPageMetaToSlot(slot, page) {
    let aspectRatio = normalizedAspectRatio(page.aspectRatio, FALLBACK_ASPECT_RATIO);
    slot.aspectRatio === aspectRatio && slot.state !== "error" || (slot.aspectRatio = aspectRatio, slot.kind = "page", slot.state = "idle", slot.image = null, slot.errorMessage = null, slot.width = null, slot.height = null, slot.token += 1);
  }
  function clearNonPageSlotMeta(slot) {
    slot.kind !== "blank" && slot.kind !== "end" || (slot.state = "ready", slot.image = null, slot.errorMessage = null, slot.width = null, slot.height = null, slot.token += 1);
  }
  function pageSlotAspectRatio(slot) {
    return slot.width && slot.height && slot.width > 0 && slot.height > 0 ? slot.height / slot.width : normalizedAspectRatio(slot.aspectRatio, FALLBACK_ASPECT_RATIO);
  }
  delegateEvents(["pointerdown", "click"]);

  // src/components/Widgets/ProgressBar.css
  var ProgressBar_default = `.ehpeek-progress-bar::-webkit-slider-runnable-track {
  height: 0.4em;
  border-radius: 9999px;
  background: linear-gradient(
    var(--progress-bar-track-direction),
    var(--color-accent) 0 var(--progress-bar-fill),
    var(--color-track) var(--progress-bar-fill) 100%
  );
}

.ehpeek-progress-bar::-webkit-slider-thumb {
  width: 1.4em;
  height: 1.4em;
  margin-top: -0.5em;
  border: 2px solid var(--color-border);
  border-radius: 9999px;
  background: var(--color-text);
  box-shadow: 0 2px 10px var(--color-shadow-control);
  appearance: none;
  -webkit-appearance: none;
}

.ehpeek-progress-bar::-moz-range-track,
.ehpeek-progress-bar::-moz-range-progress {
  height: 0.4em;
  border-radius: 9999px;
}

.ehpeek-progress-bar::-moz-range-track {
  background: var(--color-track);
}

.ehpeek-progress-bar::-moz-range-progress {
  background: var(--color-accent);
}

.ehpeek-progress-bar::-moz-range-thumb {
  width: 1.4em;
  height: 1.4em;
  border: 2px solid var(--color-border);
  border-radius: 9999px;
  background: var(--color-text);
  box-shadow: 0 2px 10px var(--color-shadow-control);
}
`;

  // src/components/Widgets/ProgressBar.tsx
  var _tmpl$39 = /* @__PURE__ */ template("<input type=range>"), PROGRESS_BAR_CLASS = "ehpeek-progress-bar", PROGRESS_BAR_CLASS_NAME = [PROGRESS_BAR_CLASS, "w-full h-[2.4em] px-[0.6em] py-0 m-0", "bg-transparent", "cursor-grab active:cursor-grabbing touch-none select-none", "[-webkit-appearance:none] [appearance:none]", "[--progress-bar-fill:0%] [--progress-bar-track-direction:to_right]", "[accent-color:var(--color-text)]"].join(" ");
  registerGlobalStyle(PROGRESS_BAR_CLASS, ProgressBar_default);
  function ProgressBar(props) {
    let input;
    createEffect(() => {
      let direction = props.direction ?? "ltr";
      input.style.setProperty("--progress-bar-track-direction", direction === "rtl" ? "to left" : "to right"), input.style.setProperty("--progress-bar-fill", `${Math.min(100, Math.max(0, props.fillPercent ?? 0))}%`), !props.keepInputValue && props.value !== void 0 && (input.value = String(props.value));
    });
    let currentValue = (event) => Number(event.currentTarget.value || "");
    return (() => {
      var _el$ = _tmpl$39();
      return _el$.addEventListener("pointercancel", (event) => {
        props.onCommit?.(currentValue(event));
      }), _el$.$$pointerup = (event) => {
        props.onCommit?.(currentValue(event));
      }, _el$.addEventListener("change", (event) => {
        props.onCommit?.(currentValue(event));
      }), _el$.$$input = (event) => {
        props.onInput?.(currentValue(event));
      }, _el$.$$pointerdown = (event) => {
        props.onPointerDown?.(event);
      }, use((element) => {
        input = element, element.value = String(props.value ?? props.min);
      }, _el$), createRenderEffect((_p$) => {
        var _v$ = `${PROGRESS_BAR_CLASS_NAME}${props.class ? ` ${props.class}` : ""}`, _v$2 = String(props.min), _v$3 = String(Math.max(1, props.max ?? props.min)), _v$4 = String(props.step), _v$5 = props.direction ?? "ltr";
        return _v$ !== _p$.e && className(_el$, _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$, "min", _p$.t = _v$2), _v$3 !== _p$.a && setAttribute(_el$, "max", _p$.a = _v$3), _v$4 !== _p$.o && setAttribute(_el$, "step", _p$.o = _v$4), _v$5 !== _p$.i && setAttribute(_el$, "dir", _p$.i = _v$5), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0
      }), _el$;
    })();
  }
  delegateEvents(["pointerdown", "input", "pointerup"]);

  // src/components/Reader/Toolbar.tsx
  var _tmpl$40 = /* @__PURE__ */ template('<div class="ehpeek-reader-fullscreen-status fixed z-3 flex items-center gap-sm pointer-events-none top-[calc(10px+env(safe-area-inset-top,0px))] left-[max(10px,env(safe-area-inset-left,0px))] py-xs px-md rounded-md bg-[var(--color-badge)] ehp-color-text font-sans textsize-sm font-600 leading-[1.4] whitespace-nowrap"role=status><span>'), _tmpl$212 = /* @__PURE__ */ template('<div class=contents><div class="ehpeek-reader-toolbar fixed z-3 flex justify-end pointer-events-none top-[calc(10px+env(safe-area-inset-top,0px))] right-10px coarse:top-[calc(8px+env(safe-area-inset-top,0px))] coarse:right-8px"><div><button type=button></button><button type=button></button><button type=button></button><button type=button></button><button type=button></button><button type=button></button><button type=button></button></div></div><div class="ehpeek-reader-page-number fixed z-3 pointer-events-none top-[calc(70px+env(safe-area-inset-top,0px))] left-1/2 right-auto -translate-x-1/2 coarse:top-[calc(80px+env(safe-area-inset-top,0px))] landscape:top-[calc(62px+env(safe-area-inset-top,0px))] landscape:left-auto landscape:right-10px landscape:translate-x-0 coarse-landscape:top-[calc(74px+env(safe-area-inset-top,0px))] coarse-landscape:right-8px min-w-64px landscape:min-w-0 max-w-none landscape:max-w-[calc(100vw-20px)] coarse-landscape:max-w-[calc(100vw-16px)] py-xs px-md rounded-md bg-[var(--color-badge)] ehp-color-text font-sans textsize-sm font-600 leading-[1.4] whitespace-nowrap text-center landscape:text-right"></div><div class="fixed z-2 flex items-center p-0 transition-[opacity,transform] duration-160 ease-in-out right-[max(12px,env(safe-area-inset-right,0px))] bottom-[calc(12px+env(safe-area-inset-bottom,0px))] left-[max(12px,env(safe-area-inset-left,0px))] [&amp;[data-open=false]]:opacity-0 [&amp;[data-open=false]]:translate-y-[calc(100%+16px)] [&amp;[data-open=false]]:pointer-events-none">'), _tmpl$310 = /* @__PURE__ */ template('<div class="fixed inset-0 z-overlay flex items-center justify-center p-lg bg-black/65 pointer-events-auto"role=dialog aria-modal=true><div class="ehpeek-reader-download-dialog-panel w-full max-w-480px p-lg rounded-lg border border-[var(--color-border)] bg-[var(--color-background)] text-[var(--color-text)] shadow-xl"><div class="flex items-center justify-between gap-md mb-lg"><div class="font-sans textsize-md font-700"></div><button type=button></button></div><div class="grid gap-md font-sans textsize-md"><button type=button><span class="textsize-md font-700"></span><span class="max-w-full overflow-hidden text-ellipsis whitespace-nowrap textsize-sm opacity-75"></span></button><button type=button><span class="textsize-md font-700"></span><span class="textsize-sm opacity-75">'), READER_BUTTON_CLASS = ["inline-flex min-w-48px h-48px items-center justify-center px-md py-0 rounded-md coarse:min-w-64px coarse:h-64px coarse:px-lg coarse:rounded-lg", "border border-[var(--color-border)] bg-[var(--color-control)] text-[var(--color-text)] cursor-pointer font-sans textsize-md font-700 leading-1 disabled:opacity-40 disabled:cursor-default"].join(" "), READER_ICON_SIZE = "1.4em", TIME_FORMATTER = new Intl.DateTimeFormat(void 0, {
    hour: "2-digit",
    minute: "2-digit"
  }), DOWNLOAD_OPTION_CLASS = ["flex w-full min-h-lg flex-col items-start justify-center gap-xs px-lg py-md rounded-md", "border border-[var(--color-border)] bg-[var(--color-control)] text-[var(--color-text)] cursor-pointer text-left", "hover:bg-[var(--color-badge)] disabled:opacity-40 disabled:cursor-default"].join(" ");
  function Toolbar(props) {
    let [dialogDownloadInfo, setDialogDownloadInfo] = createSignal(null), fullscreenTime = createFullscreenTime(() => props.fullscreenActive);
    return createEffect(() => {
      dialogDownloadInfo()?.pageNum !== props.progress.pageNum && setDialogDownloadInfo(null);
    }), createEffect(() => {
      if (!dialogDownloadInfo())
        return;
      let closeOnEscape = (event) => {
        event.key === "Escape" && (event.preventDefault(), event.stopPropagation(), setDialogDownloadInfo(null));
      };
      window.addEventListener("keydown", closeOnEscape, !0), onCleanup(() => window.removeEventListener("keydown", closeOnEscape, !0));
    }), (() => {
      var _el$ = _tmpl$212(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.nextSibling, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$9 = _el$8.nextSibling, _el$0 = _el$9.nextSibling, _el$1 = _el$2.nextSibling, _el$12 = _el$1.nextSibling;
      return addEventListener(_el$2, "wheel", stopEvent), addEventListener(_el$2, "pointerdown", stopEvent, !0), addEventListener(_el$2, "click", stopEvent, !0), _el$4.$$click = () => props.callbacks.onControlsChange({
        ...props.controls,
        rightTapAction: props.controls.rightTapAction === "previous" ? "next" : "previous"
      }), className(_el$4, READER_BUTTON_CLASS), insert(_el$4, () => props.controls.rightTapAction === "previous" ? "R-" : "R+"), _el$5.$$click = () => props.callbacks.onControlsChange({
        ...props.controls,
        readDirection: props.controls.readDirection === "rtl" ? "ltr" : "rtl"
      }), className(_el$5, READER_BUTTON_CLASS), insert(_el$5, createComponent(Icon, {
        get name() {
          return props.controls.readDirection === "rtl" ? "arrow-left" : "arrow-right";
        },
        size: READER_ICON_SIZE
      })), _el$6.$$click = () => props.callbacks.onControlsChange({
        ...props.controls,
        mode: props.controls.mode === "paged" ? "scroll" : "paged"
      }), className(_el$6, READER_BUTTON_CLASS), insert(_el$6, createComponent(Icon, {
        get name() {
          return props.controls.mode === "paged" ? "arrows-horizontal" : "arrows-vertical";
        },
        size: READER_ICON_SIZE
      })), _el$7.$$click = () => setDialogDownloadInfo(props.downloadInfo), className(_el$7, READER_BUTTON_CLASS), insert(_el$7, createComponent(Icon, {
        name: "download",
        size: READER_ICON_SIZE
      })), _el$8.$$click = () => props.callbacks.onOpenOriginalPageClick(), className(_el$8, READER_BUTTON_CLASS), insert(_el$8, createComponent(Icon, {
        name: "external-link",
        size: READER_ICON_SIZE
      })), _el$9.$$click = () => props.callbacks.onFullscreenClick(), className(_el$9, READER_BUTTON_CLASS), insert(_el$9, createComponent(Icon, {
        get name() {
          return props.fullscreenActive ? "fullscreen-exit" : "fullscreen";
        },
        size: READER_ICON_SIZE
      })), _el$0.$$click = () => props.callbacks.onCloseClick(), className(_el$0, READER_BUTTON_CLASS), insert(_el$0, createComponent(Icon, {
        name: "close",
        size: READER_ICON_SIZE
      })), insert(_el$1, () => pageNumberText(props.progress.pageNum, props.progress.totalPages)), insert(_el$, createComponent(Show, {
        get when() {
          return props.fullscreenActive;
        },
        get children() {
          var _el$10 = _tmpl$40(), _el$11 = _el$10.firstChild;
          return insert(_el$11, fullscreenTime), _el$10;
        }
      }), _el$12), addEventListener(_el$12, "wheel", stopEvent), addEventListener(_el$12, "pointerdown", stopEvent, !0), addEventListener(_el$12, "click", stopEvent, !0), insert(_el$12, createComponent(ProgressBar, {
        class: "ehpeek-reader-progress textsize-lg",
        get direction() {
          return props.controls.readDirection === "rtl" ? "rtl" : "ltr";
        },
        get fillPercent() {
          return progressFillPercent(props.progress);
        },
        get keepInputValue() {
          return props.progress.keepInputValue;
        },
        get max() {
          return Math.max(1, props.progress.maxProgressPageNum);
        },
        min: 1,
        step: 1,
        get value() {
          return props.progress.pageNum;
        },
        get onPointerDown() {
          return props.callbacks.onProgressPointerDown;
        },
        get onInput() {
          return props.callbacks.onProgressInput;
        },
        get onCommit() {
          return props.callbacks.onProgressCommit;
        }
      })), insert(_el$, createComponent(Show, {
        get when() {
          return dialogDownloadInfo();
        },
        keyed: !0,
        children: (downloadInfo) => (() => {
          var _el$13 = _tmpl$310(), _el$14 = _el$13.firstChild, _el$15 = _el$14.firstChild, _el$16 = _el$15.firstChild, _el$17 = _el$16.nextSibling, _el$18 = _el$15.nextSibling, _el$19 = _el$18.firstChild, _el$20 = _el$19.firstChild, _el$21 = _el$20.nextSibling, _el$22 = _el$19.nextSibling, _el$23 = _el$22.firstChild, _el$24 = _el$23.nextSibling;
          return addEventListener(_el$13, "wheel", stopEvent), addEventListener(_el$13, "pointerdown", stopEvent, !0), _el$13.$$click = (event) => {
            event.stopPropagation(), event.target === event.currentTarget && setDialogDownloadInfo(null);
          }, insert(_el$16, () => `${texts_default.reader.download} · ${downloadInfo.pageNum}`), _el$17.$$click = () => setDialogDownloadInfo(null), className(_el$17, READER_BUTTON_CLASS), insert(_el$17, createComponent(Icon, {
            name: "close",
            size: READER_ICON_SIZE
          })), _el$19.$$click = () => {
            startImageDownload(downloadInfo.currentImageUrl, downloadInfo.currentFileName) && setDialogDownloadInfo(null);
          }, className(_el$19, DOWNLOAD_OPTION_CLASS), insert(_el$20, () => texts_default.reader.downloadDisplayedImage), insert(_el$21, () => downloadInfo.currentFileName), _el$22.$$click = () => {
            downloadInfo.originalImageUrl && startImageDownload(downloadInfo.originalImageUrl) && setDialogDownloadInfo(null);
          }, className(_el$22, DOWNLOAD_OPTION_CLASS), insert(_el$23, () => texts_default.reader.downloadOriginalImage), insert(_el$24, (() => {
            var _c$ = memo(() => !!downloadInfo.originalImageUrl);
            return () => _c$() ? texts_default.reader.originalImageSource : texts_default.reader.originalImageUnavailable;
          })()), createRenderEffect((_p$) => {
            var _v$5 = texts_default.reader.download, _v$6 = texts_default.button.close, _v$7 = texts_default.button.close, _v$8 = !downloadInfo.originalImageUrl;
            return _v$5 !== _p$.e && setAttribute(_el$13, "aria-label", _p$.e = _v$5), _v$6 !== _p$.t && setAttribute(_el$17, "title", _p$.t = _v$6), _v$7 !== _p$.a && setAttribute(_el$17, "aria-label", _p$.a = _v$7), _v$8 !== _p$.o && (_el$22.disabled = _p$.o = _v$8), _p$;
          }, {
            e: void 0,
            t: void 0,
            a: void 0,
            o: void 0
          }), _el$13;
        })()
      }), null), createRenderEffect((_p$) => {
        var _v$ = `ehpeek-reader-toolbar-buttons flex flex-row gap-md coarse:gap-lg pointer-events-auto${props.open ? "" : " !hidden"}`, _v$2 = !props.downloadInfo, _v$3 = props.controls.mode === "scroll" && !props.open && !props.fullscreenActive, _v$4 = String(props.open);
        return _v$ !== _p$.e && className(_el$3, _p$.e = _v$), _v$2 !== _p$.t && (_el$7.disabled = _p$.t = _v$2), _v$3 !== _p$.a && (_el$1.hidden = _p$.a = _v$3), _v$4 !== _p$.o && setAttribute(_el$12, "data-open", _p$.o = _v$4), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0
      }), _el$;
    })();
  }
  function startImageDownload(url, name) {
    try {
      return GM_download({
        url,
        ...name ? {
          name
        } : {},
        onerror: (error) => {
          console.error("[ehpeek]", error), window.alert(texts_default.errors.downloadFailed);
        }
      }), !0;
    } catch (error) {
      return console.error("[ehpeek]", error), window.alert(texts_default.errors.downloadFailed), !1;
    }
  }
  function createFullscreenTime(enabled) {
    let [time, setTime] = createSignal(TIME_FORMATTER.format(/* @__PURE__ */ new Date()));
    return createEffect(() => {
      if (!enabled())
        return;
      let updateTime = () => setTime(TIME_FORMATTER.format(/* @__PURE__ */ new Date()));
      updateTime();
      let interval = null, timeout = window.setTimeout(() => {
        updateTime(), interval = window.setInterval(updateTime, 6e4);
      }, 6e4 - Date.now() % 6e4);
      onCleanup(() => {
        window.clearTimeout(timeout), interval !== null && window.clearInterval(interval);
      });
    }), time;
  }
  function progressFillPercent(progress) {
    let max = Math.max(1, progress.maxProgressPageNum), value = Math.min(max, Math.max(1, progress.pageNum));
    return max > 1 ? (value - 1) / (max - 1) * 100 : 100;
  }
  function pageNumberText(pageNum, totalPages) {
    return totalPages && pageNum === totalPages + 1 ? texts_default.reader.endPage : totalPages ? `${pageNum} / ${totalPages}` : String(pageNum);
  }
  delegateEvents(["click", "pointerdown"]);

  // src/components/Reader/ZoomOverlay.tsx
  var _tmpl$41 = /* @__PURE__ */ template('<div class="fixed inset-0 z-4 flex items-center justify-center overflow-hidden ehp-color-reader pointer-events-none"><img class="block max-w-screen max-h-screen object-contain origin-center select-none will-change-transform [-webkit-user-drag:none]">'), MIN_SCALE = 1, MAX_SCALE = 5, CLOSE_SCALE = 1.02;
  function ZoomOverlay(props) {
    let [transform, setTransform] = createSignal("translate3d(0px, 0px, 0) scale(1)"), element, scale = 1, requestedScale = 1, offsetX = 0, offsetY = 0, pinchStartScale = 1, pinchStartOffsetX = 0, pinchStartOffsetY = 0, pinchStartCenterX = 0, pinchStartCenterY = 0, dragStartOffsetX = 0, dragStartOffsetY = 0, renderTransform = () => {
      setTransform(`translate3d(${offsetX}px, ${offsetY}px, 0) scale(${scale})`);
    }, startPinch = (pinch) => {
      pinchStartScale = scale, pinchStartOffsetX = offsetX, pinchStartOffsetY = offsetY, pinchStartCenterX = pinch.centerX, pinchStartCenterY = pinch.centerY;
    }, actions = {
      reset(pinch) {
        scale = 1, requestedScale = 1, offsetX = 0, offsetY = 0, startPinch(pinch), renderTransform();
      },
      startPinch,
      movePinch(pinch) {
        if (!props.image)
          return;
        requestedScale = pinchStartScale * pinch.scale, scale = clamp(requestedScale, MIN_SCALE, MAX_SCALE);
        let rect = element.getBoundingClientRect(), viewportCenterX = rect.left + rect.width / 2, viewportCenterY = rect.top + rect.height / 2, ratio = scale / pinchStartScale;
        offsetX = pinch.centerX - viewportCenterX - (pinchStartCenterX - viewportCenterX - pinchStartOffsetX) * ratio, offsetY = pinch.centerY - viewportCenterY - (pinchStartCenterY - viewportCenterY - pinchStartOffsetY) * ratio, renderTransform();
      },
      endPinch() {
        if (requestedScale <= CLOSE_SCALE) {
          props.onClose();
          return;
        }
        renderTransform();
      },
      startDrag() {
        dragStartOffsetX = offsetX, dragStartOffsetY = offsetY;
      },
      moveDrag(move) {
        props.image && (offsetX = dragStartOffsetX + move.dx, offsetY = dragStartOffsetY + move.dy, renderTransform());
      }
    };
    return untrack(() => props.actionsRef(actions)), (() => {
      var _el$ = _tmpl$41(), _el$2 = _el$.firstChild, _ref$ = element;
      return typeof _ref$ == "function" ? use(_ref$, _el$) : element = _el$, createRenderEffect((_p$) => {
        var _v$ = !props.image, _v$2 = props.image ? "" : "none", _v$3 = props.image?.imageUrl, _v$4 = props.image ? `Page ${props.image.pageNum}` : "", _v$5 = props.image?.width ?? void 0, _v$6 = props.image?.height ?? void 0, _v$7 = transform();
        return _v$ !== _p$.e && (_el$.hidden = _p$.e = _v$), _v$2 !== _p$.t && setStyleProperty(_el$, "display", _p$.t = _v$2), _v$3 !== _p$.a && setAttribute(_el$2, "src", _p$.a = _v$3), _v$4 !== _p$.o && setAttribute(_el$2, "alt", _p$.o = _v$4), _v$5 !== _p$.i && setAttribute(_el$2, "width", _p$.i = _v$5), _v$6 !== _p$.n && setAttribute(_el$2, "height", _p$.n = _v$6), _v$7 !== _p$.s && setStyleProperty(_el$2, "transform", _p$.s = _v$7), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0,
        n: void 0,
        s: void 0
      }), _el$;
    })();
  }

  // src/components/Reader/session.ts
  var DEFAULT_WINDOW_SIZE = 10, DEFAULT_FAR_CONCURRENT_LOADS = 6, ReaderSession = class {
    constructor(options) {
      this.animationFrames = /* @__PURE__ */ new Set();
      this.timers = /* @__PURE__ */ new Set();
      this.disposed = !1;
      this.imageQueue = new PriorityLoadQueue(
        options.concurrentLoads
      );
      let [controls, setControls] = createSignal({
        mode: state.reader.viewMode.value,
        readDirection: state.reader.readDirection.value,
        rightTapAction: state.reader.rightTapAction.value
      }), [toolbarOpen, setToolbarOpen] = createSignal(!1), [viewportWindow, setViewportWindow] = createSignal(initialViewportWindow(options)), [zoomImage, setZoomImage] = createSignal(null), [currentPageNum, setCurrentPageNum] = createSignal(initialPageNumber(options)), [direction, setDirection] = createSignal(1), [downloadInfo, setDownloadInfo] = createSignal(null), [maxProgressPageNum, setMaxProgressPageNum] = createSignal(1), [progressInputActive, setProgressInputActive] = createSignal(!1), [scrollBarVisible, setScrollBarVisible] = createSignal(!1), [scrollBarExpanded, setScrollBarExpanded] = createSignal(!1);
      this.state = {
        navi: {
          currentPageNum,
          direction,
          setCurrentPageNum,
          setDirection,
          setViewportWindow,
          viewportWindow,
          leftDragDelta: () => state.reader.readDirection.value === "rtl" ? -1 : 1,
          leftTapDelta: () => state.reader.rightTapAction.value === "previous" ? 1 : -1,
          rightDragDelta: () => state.reader.readDirection.value === "rtl" ? 1 : -1,
          rightTapDelta: () => state.reader.rightTapAction.value === "previous" ? -1 : 1,
          downloadInfo,
          maxProgressPageNum,
          progressInputActive,
          setDownloadInfo,
          setMaxProgressPageNum,
          setProgressInputActive
        },
        ctrls: { update: setControls, value: controls },
        toolbar: {
          open: toolbarOpen,
          toggle: () => setToolbarOpen((open) => !open)
        },
        scrollBar: {
          expanded: scrollBarExpanded,
          updateExpanded: setScrollBarExpanded,
          updateVisible: setScrollBarVisible,
          visible: scrollBarVisible
        },
        overlay: { image: zoomImage, update: setZoomImage }
      };
    }
    setTimeout(callback, delay) {
      let timer = window.setTimeout(() => {
        this.timers.delete(timer), callback();
      }, delay);
      return this.timers.add(timer), timer;
    }
    clearTimeout(timer) {
      timer !== null && (window.clearTimeout(timer), this.timers.delete(timer));
    }
    requestAnimationFrame(callback) {
      let frame = window.requestAnimationFrame((time) => {
        this.animationFrames.delete(frame), callback(time);
      });
      return this.animationFrames.add(frame), frame;
    }
    cancelAnimationFrame(frame) {
      frame !== null && (window.cancelAnimationFrame(frame), this.animationFrames.delete(frame));
    }
    dispose() {
      if (!this.disposed) {
        this.disposed = !0;
        for (let timer of this.timers)
          window.clearTimeout(timer);
        for (let frame of this.animationFrames)
          window.cancelAnimationFrame(frame);
        this.imageQueue.dispose(), this.timers.clear(), this.animationFrames.clear();
      }
    }
  };
  function initialViewportWindow(options) {
    return {
      currentPageNum: initialPageNumber(options),
      windowSize: options.renderWindowSize ?? DEFAULT_WINDOW_SIZE,
      totalPages: options.totalPages && options.totalPages > 0 ? options.totalPages : void 0,
      pages: /* @__PURE__ */ new Map()
    };
  }
  function initialPageNumber(options) {
    let totalPages = options.totalPages && options.totalPages > 0 ? options.totalPages : Number.MAX_SAFE_INTEGER;
    return clamp(Math.round(options.initialPageNum), 1, totalPages);
  }
  var PriorityLoadQueue = class {
    constructor(concurrentLoads = DEFAULT_FAR_CONCURRENT_LOADS) {
      this.concurrentLoads = concurrentLoads;
      this.pending = /* @__PURE__ */ new Map();
      this.active = /* @__PURE__ */ new Set();
      this.timer = null;
      this.disposed = !1;
      this.callbacks = {};
    }
    updateCallbacks(callbacks) {
      this.callbacks = callbacks, this.schedule();
    }
    dispose() {
      this.disposed = !0, this.pending.clear(), this.timer !== null && (window.clearTimeout(this.timer), this.timer = null);
    }
    sync(targets) {
      this.pending = new Map(targets.filter(({ key }) => !this.active.has(key)).map((target) => [target.key, target])), this.schedule();
    }
    schedule() {
      this.timer !== null || this.disposed || !this.callbacks.loadTarget || (this.timer = window.setTimeout(() => {
        this.timer = null, this.process();
      }, 0));
    }
    process() {
      if (!this.disposed)
        for (; this.active.size < this.concurrentLoads; ) {
          let next = Array.from(this.pending.values()).sort((left, right) => left.priority - right.priority)[0];
          if (!next)
            return;
          this.pending.delete(next.key), this.start(next);
        }
    }
    start({ key, target }) {
      let { loadTarget, markLoading, onLoaded, onError } = this.callbacks;
      if (!loadTarget || !markLoading || !onLoaded || !onError)
        return;
      let token = markLoading(target);
      token !== null && (this.active.add(key), loadTarget(target).then((loaded) => {
        this.disposed || onLoaded(target, loaded, token);
      }).catch((error) => {
        this.disposed || onError(target, error, token);
      }).finally(() => {
        this.active.delete(key), this.process();
      }));
    }
  };

  // src/components/Reader/ScrollBar.tsx
  var _tmpl$48 = /* @__PURE__ */ template('<div><div class="absolute inset-y-0 right-2px w-3px bg-[var(--color-reader-border)]"></div><div><span>');
  function ReaderScrollBar(props) {
    let track, thumb, [dragging, setDragging] = createSignal(!1), dragOffset = 0, expanded = () => props.expanded || dragging(), interactionWidth = () => expanded() ? "w-18px coarse:w-24px" : "w-10px coarse:w-12px", position = () => props.totalPages <= 1 ? 0 : (props.currentPage - 1) / (props.totalPages - 1) * 100, pageAt = (clientY) => {
      let trackRect = track.getBoundingClientRect(), travel = Math.max(1, trackRect.height - thumb.offsetHeight), ratio = clamp((clientY - trackRect.top - dragOffset) / travel, 0, 1);
      return Math.round(1 + ratio * (props.totalPages - 1));
    }, updatePage = (clientY) => {
      let page = pageAt(clientY);
      return props.callbacks.onProgressInput(page), page;
    };
    return (() => {
      var _el$ = _tmpl$48(), _el$2 = _el$.firstChild, _el$3 = _el$2.nextSibling, _el$4 = _el$3.firstChild;
      _el$.addEventListener("wheel", (event) => event.stopPropagation()), _el$.addEventListener("pointercancel", (event) => {
        dragging() && (setDragging(!1), track.releasePointerCapture(event.pointerId), props.callbacks.onProgressCommit(props.currentPage));
      }), _el$.$$pointerup = (event) => {
        if (!dragging())
          return;
        setDragging(!1);
        let page = updatePage(event.clientY);
        track.releasePointerCapture(event.pointerId), props.callbacks.onProgressCommit(page);
      }, _el$.$$pointermove = (event) => {
        dragging() && updatePage(event.clientY);
      }, _el$.$$pointerdown = (event) => {
        event.preventDefault(), event.stopPropagation(), setDragging(!0), track.setPointerCapture(event.pointerId);
        let thumbRect = thumb.getBoundingClientRect();
        dragOffset = event.target instanceof Node && thumb.contains(event.target) ? event.clientY - thumbRect.top : thumbRect.height / 2, props.callbacks.onProgressPointerDown(event), updatePage(event.clientY);
      }, _el$.$$contextmenu = (event) => {
        event.preventDefault(), event.stopPropagation();
      }, _el$.$$click = (event) => event.stopPropagation();
      var _ref$ = track;
      typeof _ref$ == "function" ? use(_ref$, _el$) : track = _el$;
      var _ref$2 = thumb;
      return typeof _ref$2 == "function" ? use(_ref$2, _el$3) : thumb = _el$3, createRenderEffect((_p$) => {
        var _v$ = `fixed inset-y-0 right-0 z-2 ${interactionWidth()} touch-none select-none transition-[width,opacity] duration-160 ease-in-out ` + (props.visible || dragging() ? "opacity-100" : "opacity-0 pointer-events-none"), _v$2 = `absolute right-0 flex ${interactionWidth()} h-[120px] coarse:h-[200px] items-center justify-end cursor-grab active:cursor-grabbing transition-[width] duration-160`, _v$3 = `${position()}%`, _v$4 = `translateY(-${position()}%)`, _v$5 = `block h-full rounded-l-md bg-[var(--color-reader-scrollbar)] shadow-[0_2px_10px_var(--color-shadow-control)] transition-[width] duration-160 ${expanded() ? "w-18px coarse:w-24px" : "w-10px coarse:w-12px"}`;
        return _v$ !== _p$.e && className(_el$, _p$.e = _v$), _v$2 !== _p$.t && className(_el$3, _p$.t = _v$2), _v$3 !== _p$.a && setStyleProperty(_el$3, "top", _p$.a = _v$3), _v$4 !== _p$.o && setStyleProperty(_el$3, "transform", _p$.o = _v$4), _v$5 !== _p$.i && className(_el$4, _p$.i = _v$5), _p$;
      }, {
        e: void 0,
        t: void 0,
        a: void 0,
        o: void 0,
        i: void 0
      }), _el$;
    })();
  }
  delegateEvents(["click", "contextmenu", "pointerdown", "pointermove", "pointerup"]);

  // src/components/Reader/index.css
  var Reader_default = `#ehpeek-reader,
[data-ehpeek-reader-container="true"]:fullscreen {
  --color-reader-background: #070707;
  --color-reader-surface: #151515;
  --color-reader-elevated: #232323;
  --color-reader-text: #f3f3f3;
  --color-reader-accent: #4da3ff;
  --color-reader-border: color-mix(in srgb, var(--color-reader-text) 18%, transparent);
  --color-reader-muted: color-mix(in srgb, var(--color-reader-text) 72%, transparent);
  --color-reader-scrollbar: color-mix(in srgb, var(--color-reader-text) 56%, transparent);
  --color-background: var(--color-reader-background);
  --color-surface: var(--color-reader-surface);
  --color-elevated: var(--color-reader-elevated);
  --color-text: var(--color-reader-text);
  --color-accent: var(--color-reader-accent);
  --color-border: color-mix(in srgb, var(--color-text) 18%, transparent);
  --color-muted: color-mix(in srgb, var(--color-text) 72%, transparent);
  --color-track: color-mix(in srgb, var(--color-text) 34%, var(--color-background));
  --color-control: color-mix(in srgb, var(--color-elevated) 88%, transparent);
  --color-badge: color-mix(in srgb, var(--color-background) 34%, transparent);
}

#ehpeek-reader,
#ehpeek-reader * {
  box-sizing: border-box;
}

[data-ehpeek-reader-container="true"]:fullscreen {
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: var(--color-reader-background);
}

[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-toolbar-buttons {
  transform: scale(var(--ehpeek-reader-fullscreen-ui-scale, 1));
  transform-origin: top right;
}

[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-download-dialog-panel,
[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-page-reload {
  transform: scale(var(--ehpeek-reader-fullscreen-ui-scale, 1));
  transform-origin: center;
}

[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-progress {
  font-size: var(--ehpeek-reader-fullscreen-progress-size, var(--font-size-lg));
}

[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-fullscreen-status {
  top: calc(10px + env(safe-area-inset-top, 0px));
  right: max(10px, env(safe-area-inset-right, 0px));
  left: auto;
}

[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-page-number {
  top: calc(10px + env(safe-area-inset-top, 0px));
  right: auto;
  left: max(10px, env(safe-area-inset-left, 0px));
  min-width: 0;
  transform: none;
  text-align: left;
}

[data-ehpeek-reader-container="true"]:fullscreen .ehpeek-reader-toolbar {
  top: calc(42px + env(safe-area-inset-top, 0px));
}

[data-ehpeek-reader-container="true"]:fullscreen
  #ehpeek-reader[data-view-mode="scroll"]
  .ehpeek-reader-page-strip {
  padding-top: 0;
}
`;

  // src/components/Reader/index.tsx
  var _tmpl$49 = /* @__PURE__ */ template('<div id=ehpeek-reader class="fixed inset-0 z-reader ehp-color-reader font-sans textsize-sm leading-[1.4]"><header class=contents>');
  registerGlobalStyle("ehpeek-reader-style", Reader_default);
  var DEFAULT_WINDOW_SIZE2 = 10, PAGED_SWIPE_THRESHOLD = 24, PAGED_WHEEL_THRESHOLD = 8, PROGRESS_IDLE_COMMIT_MS = 180, LOADED_IMAGE_INFO_CACHE_LIMIT = 160, SCROLL_GESTURE_IDLE_MS = 160, SCROLL_BAR_IDLE_MS = 900, SCROLL_BAR_SHOW_DISTANCE = 48, SCROLL_BAR_EXPAND_VIEWPORTS = 2, DOUBLE_TAP_MS = 340, DOUBLE_TAP_DISTANCE = 36, TAP_CANCEL_DISTANCE = 8, FALLBACK_ASPECT_RATIO2 = 1.42;
  function Reader(props) {
    let options = untrack(() => props.options), totalPages = options.totalPages ?? 0, previewCache = untrack(() => props.previewCache), callbacks = untrack(() => props.callbacks), session = new ReaderSession(options), readerState = session.state, readerCallbacks2 = wireReaderCallbacks(session, options, previewCache, callbacks), previousFullscreenActive = untrack(() => props.fullscreenActive);
    return createEffect(() => {
      let fullscreenActive = props.fullscreenActive;
      fullscreenActive !== previousFullscreenActive && (previousFullscreenActive = fullscreenActive, session.requestAnimationFrame(() => {
        session.requestAnimationFrame(readerCallbacks2.realignCurrentPage);
      }));
    }), onMount(() => {
      readerCallbacks2.init(), onCleanup(() => {
        readerCallbacks2.cleanup(), session.dispose();
      });
    }), (() => {
      var _el$ = _tmpl$49(), _el$2 = _el$.firstChild;
      return insert(_el$2, createComponent(Toolbar, {
        get callbacks() {
          return readerCallbacks2.toolbar;
        },
        get controls() {
          return readerState.ctrls.value();
        },
        get downloadInfo() {
          return readerState.navi.downloadInfo();
        },
        get fullscreenActive() {
          return props.fullscreenActive;
        },
        get open() {
          return readerState.toolbar.open();
        },
        get progress() {
          return {
            pageNum: readerState.navi.currentPageNum(),
            totalPages: options.totalPages,
            maxProgressPageNum: readerState.navi.maxProgressPageNum(),
            keepInputValue: readerState.navi.progressInputActive()
          };
        }
      })), insert(_el$, createComponent(PagesViewport, {
        get actionsRef() {
          return readerCallbacks2.viewportActionsRef;
        },
        get callbacks() {
          return readerCallbacks2.viewport;
        },
        get decodedImageCacheLimit() {
          return options.decodedImageCacheLimit;
        },
        get mode() {
          return readerState.ctrls.value().mode;
        },
        get readDirection() {
          return readerState.ctrls.value().readDirection;
        },
        get window() {
          return readerState.navi.viewportWindow();
        }
      }), null), insert(_el$, createComponent(Show, {
        get when() {
          return readerState.ctrls.value().mode === "scroll" && totalPages > 1;
        },
        get children() {
          return createComponent(ReaderScrollBar, {
            get callbacks() {
              return readerCallbacks2.toolbar;
            },
            get currentPage() {
              return readerState.navi.currentPageNum();
            },
            get expanded() {
              return readerState.scrollBar.expanded();
            },
            totalPages,
            get visible() {
              return readerState.scrollBar.visible();
            }
          });
        }
      }), null), insert(_el$, createComponent(ZoomOverlay, {
        get actionsRef() {
          return readerCallbacks2.zoomOverlayActionsRef;
        },
        get image() {
          return readerState.overlay.image();
        },
        onClose: () => readerState.overlay.update(null)
      }), null), createRenderEffect((_p$) => {
        var _v$ = readerState.ctrls.value().readDirection, _v$2 = readerState.ctrls.value().mode;
        return _v$ !== _p$.e && setAttribute(_el$, "data-read-direction", _p$.e = _v$), _v$2 !== _p$.t && setAttribute(_el$, "data-view-mode", _p$.t = _v$2), _p$;
      }, {
        e: void 0,
        t: void 0
      }), _el$;
    })();
  }
  function wireReaderCallbacks(session, options, previewCache, callbacks) {
    let state2 = session.state, viewportActions, zoomOverlay, totalPages = options.totalPages && options.totalPages > 0 ? options.totalPages : void 0, renderWindowSize = options.renderWindowSize ?? DEFAULT_WINDOW_SIZE2, preloadWindowSize = options.preloadWindowSize ?? DEFAULT_WINDOW_SIZE2, pages = /* @__PURE__ */ new Map(), loadedImages = /* @__PURE__ */ new Map(), pagedTargetPageNumber = null, syncToken = 0, closed = !1;
    function requestReaderClose() {
      closed || (closed = !0, callbacks.onClosed());
    }
    function setCurrentPageNumber(pageNumber, scrollIntoView, scrollMotion = "instant") {
      pagedTargetPageNumber = null;
      let target = clamp(Math.round(pageNumber), 1, maxProgressPageNum());
      target !== state2.navi.currentPageNum() && (state2.navi.setDirection(target > state2.navi.currentPageNum() ? 1 : -1), state2.navi.setCurrentPageNum(target)), syncAfterPageChange({
        scrollIntoView,
        scrollMotion
      });
    }
    function syncAfterPageChange(options2) {
      let token = ++syncToken, missing = pageWindowNumbers(state2.navi.currentPageNum(), renderWindowSize).filter((number) => isRealPageNum(number) && !pages.has(number));
      syncViewportWindow(), maintainLoadQueue(), notifyActivePageChange(), options2.scrollIntoView && scrollToCurrentPage(options2.scrollMotion), missing.length > 0 && loadMissingPages(missing, token);
    }
    async function loadMissingPages(pageNums, token) {
      let incoming;
      try {
        incoming = await previewCache.getPages(pageNums);
      } catch (error) {
        console.error("[ehpeek]", error);
        return;
      }
      closed || (addPages(incoming), token === syncToken && (syncViewportWindow(), maintainLoadQueue(), notifyActivePageChange()));
    }
    function addPages(incomingPages) {
      for (let [index, page] of incomingPages.entries()) {
        let pageNum = pageNumForPage(page, index);
        pageNum > 0 && pages.set(pageNum, {
          ...page,
          aspectRatio: normalizedAspectRatio(page.aspectRatio, FALLBACK_ASPECT_RATIO2),
          pageNum
        });
      }
    }
    function syncViewportWindow() {
      state2.navi.setViewportWindow({
        currentPageNum: state2.navi.currentPageNum(),
        windowSize: renderWindowSize,
        totalPages,
        pages: pageMetaForViewport()
      }), updatePageNumber();
    }
    function maintainLoadQueue() {
      let currentPageNum = state2.navi.currentPageNum(), pageNums = [currentPageNum];
      for (let offset = 1; offset <= preloadWindowSize; offset += 1)
        pageNums.push(currentPageNum + offset * state2.navi.direction());
      session.imageQueue.sync(pageNums.flatMap((pageNum, priority) => {
        let target = loadTargetFor(pageNum);
        return target ? [{
          key: pageNum,
          priority,
          target
        }] : [];
      }));
    }
    function pageMetaForViewport() {
      return new Map(Array.from(pages, ([pageNum, page]) => [pageNum, {
        aspectRatio: page.aspectRatio
      }]));
    }
    function loadTargetFor(pageNum) {
      let page = pages.get(pageNum);
      return page ? {
        pageNum,
        page
      } : null;
    }
    function maxProgressPageNum() {
      return totalPages ? totalPages + 1 : Number.MAX_SAFE_INTEGER;
    }
    function isRealPageNum(pageNum) {
      return pageNum >= 1 && (!totalPages || pageNum <= totalPages);
    }
    function turnPageBy(delta) {
      if (state.reader.viewMode.value === "paged") {
        animatePagedStep(delta);
        return;
      }
      setCurrentPageNumber(state2.navi.currentPageNum() + delta, !0);
    }
    function animatePagedStep(delta) {
      let base = pagedTargetPageNumber ?? state2.navi.currentPageNum(), target = clamp(Math.round(base + delta), 1, maxProgressPageNum());
      if (target === base) {
        scrollToCurrentPage("animated");
        return;
      }
      if (viewportActions.pageOffset(target) === null) {
        pagedTargetPageNumber = null, setCurrentPageNumber(target, !0, "animated");
        return;
      }
      state2.navi.setDirection(target > base ? 1 : -1), pagedTargetPageNumber = target, viewportActions.moveToPage(target, "animated", () => {
        pagedTargetPageNumber === target && (pagedTargetPageNumber = null, setCurrentPageNumber(target, !0));
      });
    }
    function scrollToCurrentPage(motion = "instant") {
      viewportActions.moveToPage(state2.navi.currentPageNum(), motion);
    }
    function updatePageNumber() {
      let pageNum = state2.navi.currentPageNum(), image = loadedImages.get(pageNum);
      image && (loadedImages.delete(pageNum), loadedImages.set(pageNum, image)), state2.navi.setDownloadInfo(image && isRealPageNum(pageNum) ? {
        currentFileName: displayedImageFileName(options.galleryId, pageNum, image.imageUrl),
        currentImageUrl: image.imageUrl,
        originalImageUrl: image.originalImageUrl,
        pageNum
      } : null), state2.navi.setMaxProgressPageNum(Math.max(1, maxProgressPageNum()));
    }
    function notifyActivePageChange() {
      let page = pages.get(state2.navi.currentPageNum());
      page && callbacks.onActivePageChange(page);
    }
    function updateCurrentFromScroll() {
      let next = viewportActions.centerPageNum();
      next !== null && next !== state2.navi.currentPageNum() && (state2.navi.setDirection(next > state2.navi.currentPageNum() ? 1 : -1), state2.navi.setCurrentPageNum(next), syncAfterPageChange({
        scrollIntoView: !1
      }));
    }
    let onKeydown = (event) => {
      shouldIgnoreKeyboardEvent(event) || (event.key === "Escape" ? (state2.overlay.image() !== null ? state2.overlay.update(null) : requestReaderClose(), event.preventDefault()) : (event.key === "ArrowLeft" || event.key === "ArrowRight") && (event.preventDefault(), state2.overlay.image() === null && turnPageBy(event.key === "ArrowLeft" ? state2.navi.leftTapDelta() : state2.navi.rightTapDelta())));
    }, gesture = wireGesture(), viewport = wireViewport();
    wireImageQueue();
    let toolbar = wireToolbar();
    return {
      viewportActionsRef: (actions) => {
        viewportActions = actions;
      },
      zoomOverlayActionsRef: (actions) => {
        zoomOverlay = actions;
      },
      init: () => {
        document.addEventListener("keydown", onKeydown, !0), viewportActions.focus(), updatePageNumber(), syncAfterPageChange({
          scrollIntoView: !0
        });
      },
      cleanup: () => {
        document.removeEventListener("keydown", onKeydown, !0);
      },
      realignCurrentPage: () => {
        scrollToCurrentPage();
      },
      toolbar,
      viewport
    };
    function wireViewport() {
      let scrollFrame = null, scrollBarTimer = null, scrollGestureTimer = null, previousScrollTop = null, scrollDistance = 0, updateScrollBarActivity = () => {
        let currentScrollTop = viewportActions.scrollTop();
        previousScrollTop !== null && (scrollDistance += Math.abs(currentScrollTop - previousScrollTop)), previousScrollTop = currentScrollTop, scrollDistance >= SCROLL_BAR_SHOW_DISTANCE && state2.scrollBar.updateVisible(!0), scrollDistance >= window.innerHeight * SCROLL_BAR_EXPAND_VIEWPORTS && state2.scrollBar.updateExpanded(!0), session.clearTimeout(scrollGestureTimer), scrollGestureTimer = session.setTimeout(() => {
          scrollGestureTimer = null, scrollDistance = 0, previousScrollTop = viewportActions.scrollTop();
        }, SCROLL_GESTURE_IDLE_MS), session.clearTimeout(scrollBarTimer), scrollBarTimer = session.setTimeout(() => {
          scrollBarTimer = null, scrollDistance = 0, previousScrollTop = viewportActions.scrollTop(), state2.scrollBar.updateExpanded(!1), state2.scrollBar.updateVisible(!1);
        }, SCROLL_BAR_IDLE_MS);
      };
      return onCleanup(() => {
        session.clearTimeout(scrollBarTimer), session.clearTimeout(scrollGestureTimer);
      }), {
        onNativeScroll: () => {
          if (state2.overlay.image() !== null || state.reader.viewMode.value === "paged" || (updateScrollBarActivity(), viewportActions.isDragging()))
            return;
          let previousScrollTop2 = viewportActions.scrollTop();
          viewportActions.moveToTop(previousScrollTop2), !(viewportActions.scrollTop() !== previousScrollTop2 || scrollFrame !== null) && (scrollFrame = session.requestAnimationFrame(() => {
            scrollFrame = null, updateCurrentFromScroll();
          }));
        },
        onReloadPage: (pageNum) => {
          viewportActions.resetPageError(pageNum) && maintainLoadQueue();
        },
        onWheel: (delta, event) => {
          if (state2.overlay.image() !== null) {
            event.preventDefault();
            return;
          }
          state.reader.viewMode.value === "paged" && (event.preventDefault(), !viewportActions.isDragging() && Math.abs(delta) >= PAGED_WHEEL_THRESHOLD && turnPageBy(delta > 0 ? 1 : -1));
        },
        pointer: gesture
      };
    }
    function wireImageQueue() {
      let rememberLoadedImage = (pageNum, loaded) => {
        let image = {
          pageNum,
          imageUrl: loaded.imageUrl,
          originalImageUrl: loaded.originalImageUrl ?? null,
          width: positiveNumber(loaded.width),
          height: positiveNumber(loaded.height)
        };
        for (loadedImages.delete(pageNum), loadedImages.set(pageNum, image); loadedImages.size > LOADED_IMAGE_INFO_CACHE_LIMIT; ) {
          let oldestPageNum = loadedImages.keys().next().value;
          if (oldestPageNum === void 0)
            break;
          loadedImages.delete(oldestPageNum);
        }
        return image;
      }, installImage = async (target, loaded, token) => {
        let imageUrl = loaded.imageUrl, width = positiveNumber(loaded.width), height = positiveNumber(loaded.height);
        try {
          await viewportActions.loadPageImage(target.pageNum, token, {
            imageUrl,
            highPriority: target.pageNum === state2.navi.currentPageNum(),
            width,
            height
          });
        } catch (error) {
          let message = error instanceof Error ? error.message : texts_default.errors.imageLoadFailed;
          viewportActions.setPageError(target.pageNum, token, message);
          return;
        }
        closed || target.pageNum === state2.navi.currentPageNum() && updatePageNumber();
      };
      session.imageQueue.updateCallbacks({
        loadTarget: (target) => Promise.resolve(loadedImages.get(target.pageNum) ?? previewCache.loadImage(target.page)),
        markLoading: (target) => viewportActions.markPageLoading(target.pageNum),
        onLoaded: async (target, loaded, token) => {
          let image = rememberLoadedImage(target.pageNum, loaded);
          pageWindowNumbers(state2.navi.currentPageNum(), renderWindowSize).includes(target.pageNum) && await installImage(target, image, token);
        },
        onError: (target, error, token) => {
          let message = error instanceof Error ? error.message : texts_default.errors.loadFailed;
          viewportActions.setPageError(target.pageNum, token, message);
        }
      });
    }
    function wireToolbar() {
      let toolbar2 = {}, progressNavigationTimer = null, pendingProgressPageNum = null, updateControls = (controls) => {
        let previous = state2.ctrls.value();
        state.reader.viewMode.set(controls.mode), state.reader.readDirection.set(controls.readDirection), state.reader.rightTapAction.set(controls.rightTapAction), state2.ctrls.update(controls), controls.mode !== previous.mode ? (viewportActions.stopMotion(), viewportActions.resetPosition(), syncAfterPageChange({
          scrollIntoView: !0
        })) : controls.readDirection !== previous.readDirection && (syncViewportWindow(), scrollToCurrentPage());
      }, cancelProgressNavigation = () => {
        progressNavigationTimer !== null && (session.clearTimeout(progressNavigationTimer), progressNavigationTimer = null);
      }, previewProgress = (pageNum) => {
        let target = clamp(Math.round(pageNum), 1, maxProgressPageNum());
        target !== state2.navi.currentPageNum() && (state2.navi.setDirection(target > state2.navi.currentPageNum() ? 1 : -1), state2.navi.setCurrentPageNum(target)), ++syncToken, syncViewportWindow(), scrollToCurrentPage(), updatePageNumber();
      };
      return onCleanup(cancelProgressNavigation), toolbar2.onCloseClick = requestReaderClose, toolbar2.onControlsChange = updateControls, toolbar2.onFullscreenClick = callbacks.onFullscreenToggle, toolbar2.onOpenOriginalPageClick = () => {
        let page = pages.get(state2.navi.currentPageNum());
        page && isRealPageNum(state2.navi.currentPageNum()) && callbacks.onOpenOriginalPage(page);
      }, toolbar2.onProgressPointerDown = (event) => {
        state2.navi.setProgressInputActive(!0), cancelProgressNavigation(), event.stopPropagation();
      }, toolbar2.onProgressInput = (pageNum) => {
        !Number.isFinite(pageNum) || pageNum <= 0 || (state2.navi.setProgressInputActive(!0), pendingProgressPageNum = clamp(Math.round(pageNum), 1, maxProgressPageNum()), previewProgress(pendingProgressPageNum), cancelProgressNavigation(), progressNavigationTimer = session.setTimeout(() => toolbar2.onProgressCommit(pendingProgressPageNum ?? state2.navi.currentPageNum()), PROGRESS_IDLE_COMMIT_MS));
      }, toolbar2.onProgressCommit = (value) => {
        if (!state2.navi.progressInputActive() && pendingProgressPageNum === null)
          return;
        let pageNum = pendingProgressPageNum ?? value;
        state2.navi.setProgressInputActive(!1), pendingProgressPageNum = null, cancelProgressNavigation(), Number.isFinite(pageNum) && pageNum > 0 && setCurrentPageNumber(pageNum, !0);
      }, toolbar2;
    }
    function wireGesture() {
      let gesture2 = {}, tapTimer = null, pendingTap = null, shouldStartDrag = (event) => state2.overlay.image() !== null || state.reader.viewMode.value === "paged" || event.pointerType === "mouse", imageAtPoint = (point) => {
        let pageNum = viewportActions.pageNumAtPoint(point);
        return pageNum === null ? null : loadedImages.get(pageNum) ?? null;
      }, cancelPendingTap = () => {
        tapTimer !== null && (session.clearTimeout(tapTimer), tapTimer = null), pendingTap = null;
      }, toggleZoomAtPoint = (point) => {
        if (state2.overlay.image() !== null)
          return state2.overlay.update(null), !0;
        let image = imageAtPoint(point);
        return image ? (viewportActions.stopMotion(), viewportActions.cancelDrag(), state2.overlay.update(image), zoomOverlay.reset({
          centerX: point.clientX,
          centerY: point.clientY
        }), zoomOverlay.movePinch({
          centerX: point.clientX,
          centerY: point.clientY,
          scale: 2
        }), zoomOverlay.endPinch(), !0) : !1;
      }, consumeDoubleTap = (info, event) => {
        let now = event.timeStamp || performance.now(), nativeDoubleClick = event instanceof MouseEvent && event.detail >= 2, nearPendingTap = pendingTap ? now - pendingTap.time <= DOUBLE_TAP_MS && Math.hypot(info.clientX - pendingTap.info.clientX, info.clientY - pendingTap.info.clientY) <= DOUBLE_TAP_DISTANCE : !1;
        return !nativeDoubleClick && !nearPendingTap || (cancelPendingTap(), !toggleZoomAtPoint(info)) ? !1 : (event.preventDefault(), !0);
      }, runSingleTap = (info, event) => {
        if (state2.overlay.image() !== null)
          event.preventDefault();
        else if (viewportActions.isHitEndPage(info))
          requestReaderClose();
        else if (state.reader.viewMode.value === "scroll")
          state2.toolbar.toggle();
        else {
          let zone = info.clientX / viewportActions.viewportWidth();
          zone >= 1 / 3 && zone <= 2 / 3 ? state2.toolbar.toggle() : turnPageBy(zone < 1 / 3 ? state2.navi.leftTapDelta() : state2.navi.rightTapDelta());
        }
      }, queueSingleTap = (info, event) => {
        cancelPendingTap(), pendingTap = {
          info,
          event,
          time: event.timeStamp || performance.now()
        }, tapTimer = session.setTimeout(() => {
          let pending = pendingTap;
          pendingTap = null, tapTimer = null, pending && runSingleTap(pending.info, pending.event);
        }, DOUBLE_TAP_MS);
      };
      return gesture2.onTap = (info, event) => {
        viewportActions.cancelDrag(), !consumeDoubleTap(info, event) && queueSingleTap(info, event);
      }, gesture2.onStart = () => {
        if (state2.overlay.image() !== null) {
          zoomOverlay.startDrag();
          return;
        }
        viewportActions.beginDrag();
      }, gesture2.onMove = (info) => {
        if (state2.overlay.image() !== null) {
          zoomOverlay.moveDrag(info);
          return;
        }
        viewportActions.moveDrag({
          dx: info.dx,
          dy: info.dy
        }) && (Math.abs(info.dx) >= TAP_CANCEL_DISTANCE || Math.abs(info.dy) >= TAP_CANCEL_DISTANCE) && cancelPendingTap();
      }, gesture2.onEnd = (info) => {
        if (state2.overlay.image() === null) {
          if (viewportActions.cancelDrag(), state.reader.viewMode.value !== "paged") {
            viewportActions.moveToTop(viewportActions.scrollTop()), viewportActions.startVerticalFlingFromDragVelocity(info.velocityY, () => updateCurrentFromScroll()), updateCurrentFromScroll();
            return;
          }
          info.dx >= PAGED_SWIPE_THRESHOLD ? turnPageBy(state2.navi.rightDragDelta()) : info.dx <= -PAGED_SWIPE_THRESHOLD ? turnPageBy(state2.navi.leftDragDelta()) : scrollToCurrentPage("animated");
        }
      }, gesture2.onPinchStart = (info) => {
        if (cancelPendingTap(), viewportActions.stopMotion(), viewportActions.cancelDrag(), state2.overlay.image() !== null)
          return zoomOverlay.startPinch({
            centerX: info.clientX,
            centerY: info.clientY
          }), !0;
        let image = imageAtPoint(info);
        return image ? (state2.overlay.update(image), zoomOverlay.reset({
          centerX: info.clientX,
          centerY: info.clientY
        }), !0) : !1;
      }, gesture2.onPinchMove = (info) => zoomOverlay.movePinch({
        centerX: info.clientX,
        centerY: info.clientY,
        scale: info.scale
      }), gesture2.onPinchEnd = () => zoomOverlay.endPinch(), gesture2.shouldCaptureDrag = (event) => !(event instanceof PointerEvent) || event.pointerType === "mouse" && event.button !== 0 ? !1 : shouldStartDrag(event), gesture2.shouldObserveTap = (event) => event instanceof PointerEvent && event.pointerType !== "mouse" && !shouldStartDrag(event), gesture2.dragStartThreshold = TAP_CANCEL_DISTANCE, gesture2.tapMoveThreshold = TAP_CANCEL_DISTANCE, gesture2;
    }
  }
  function displayedImageFileName(galleryId, pageNum, imageUrl) {
    return `${galleryId}-p${String(pageNum).padStart(2, "0")}.${imageFileExtension(imageUrl)}`;
  }
  function imageFileExtension(imageUrl) {
    try {
      let extension = decodeURIComponent(new URL(imageUrl).pathname.split("/").pop() ?? "").match(/\.([a-z0-9]{2,5})$/i)?.[1]?.toLowerCase();
      if (extension && ["avif", "bmp", "gif", "jpeg", "jpg", "png", "webp"].includes(extension))
        return extension;
    } catch {
      return "";
    }
    return "";
  }
  function pageNumForPage(page, index) {
    let pageNum = page?.pageNum;
    return typeof pageNum == "number" && Number.isFinite(pageNum) && pageNum > 0 ? pageNum : index + 1;
  }
  function shouldIgnoreKeyboardEvent(event) {
    if (event.isComposing)
      return !0;
    let eventTarget = event.target;
    return eventTarget instanceof Element ? !!eventTarget.closest("input, textarea, select, [contenteditable='true'], [contenteditable='']") : !1;
  }

  // src/App/Reader.tsx
  var activeReaderClose;
  function openReaderFromUserAction(startPageUrl, callbacks, previewCache, viewport, preferredPageNum) {
    let fullscreenLaunch = requestConfiguredFullscreen(viewport);
    openReader(startPageUrl, callbacks, previewCache, viewport, preferredPageNum, fullscreenLaunch).catch(async (error) => {
      if (fullscreenLaunch) {
        let fullscreenEntered = await fullscreenLaunch.result;
        document.fullscreenElement === fullscreenLaunch.host && await document.exitFullscreen().catch((fullscreenError) => {
          console.warn("[ehpeek] Failed to exit fullscreen", fullscreenError);
        }), fullscreenEntered && await fullscreenLaunch.fullscreen.exit(), fullscreenLaunch.host.remove(), await fullscreenLaunch.fullscreen.restore();
      }
      reportReaderOpenError(error);
    });
  }
  async function openReaderFromHash(callbacks, previewCache, viewport) {
    let peekPage = peekPageFromHash();
    if (peekPage === null)
      return;
    let pages = previewCache.current().data.pages, page = pages.find((item) => item.pageNum === peekPage) ?? pages[0];
    page && await openReader(page.url, callbacks, previewCache, viewport).catch(reportReaderOpenError);
  }
  async function openOriginalReader(pageNum, previewCache) {
    let page = (await previewCache.getPages([pageNum]))[0];
    if (!page || page.pageNum !== pageNum)
      throw new Error(texts_default.errors.imageNotFound);
    window.location.assign(page.url);
  }
  async function openReader(startPageUrl, callbacks, previewCache, viewport, preferredPageNum, fullscreenLaunch) {
    if (!state.reader.enabled.value)
      return;
    let preview = previewCache.current().data, gallery = galleryIdentityFromUrl(preview.currentUrl);
    if (!gallery)
      return;
    let currentPreviewIndex = preview.currentIndex, pageSize = preview.pageSize, maxPreviewIndex = preview.maxIndex, totalPages = preview.totalImages, startPageNum = preferredPageNum ?? peekPageFromHash() ?? galleryPageNumber(startPageUrl);
    if (!startPageNum)
      throw new Error(texts_default.errors.imageNotFound);
    let historySession = callbacks.readHistoryEnabled ? new ReadHistorySession({
      galleryId: gallery.galleryId,
      token: gallery.token,
      totalPages
    }) : null;
    if ((fullscreenLaunch ? await fullscreenLaunch.result : void 0) && document.fullscreenElement !== fullscreenLaunch?.host) {
      historySession?.dispose(), await fullscreenLaunch?.fullscreen.exit(), fullscreenLaunch?.host.remove(), await fullscreenLaunch?.fullscreen.restore();
      return;
    }
    let lastPageNum = startPageNum, onExit = () => {
      if (historySession?.dispose(), callbacks.onReaderClosed(lastPageNum, totalPages ?? null), lastPageNum === startPageNum)
        return;
      let exitIndex = previewCache.previewIndexForPage(lastPageNum), galleryUrl = previewUrlForIndex(exitIndex);
      if (callbacks.enhanceThumbsGridsEnabled) {
        callbacks.onGotoPreviewIndex(exitIndex), previewCache.select(exitIndex).catch(() => {
          window.location.replace(galleryUrl);
        });
        return;
      }
      exitIndex === currentPreviewIndex ? window.history.replaceState(window.history.state, "", galleryUrl) : window.location.replace(galleryUrl);
    }, host = fullscreenLaunch?.host ?? createReaderHost(), fullscreen = fullscreenLaunch?.fullscreen ?? viewport.createFullscreen(host);
    mountReader({
      galleryId: gallery.galleryId,
      initialPageNum: startPageNum,
      totalPages
    }, previewCache, {
      onActivePageChange: (page) => {
        page.pageNum && (lastPageNum = page.pageNum, callbacks.enhanceThumbsGridsEnabled && callbacks.onGotoPreviewIndex(previewCache.previewIndexForPage(page.pageNum))), historySession?.update(page.pageNum, totalPages), updatePeekLocation(page.pageNum, pageSize, maxPreviewIndex);
      },
      onOpenOriginalPage: (page) => {
        historySession?.dispose(), window.location.assign(page.url);
      }
    }, viewport.lockScroll, fullscreen, onExit, host);
  }
  function mountReader(options, previewCache, callbacks, lockPageScroll2, fullscreen, onExit, host) {
    activeReaderClose?.();
    let disposeRoot = () => {
    }, unlockPageScroll = lockPageScroll2(), setFullscreenActive = (_active) => {
    }, keepReaderOpen = !1, historyEntry = !0, closeRequested = !1, closing = !1, close = () => requestClose();
    function requestClose() {
      if (!(closing || closeRequested)) {
        if (historyEntry) {
          closeRequested = !0, window.history.back();
          return;
        }
        onClosed();
      }
    }
    let onPopState = () => {
      historyEntry = !1, onClosed();
    };
    async function onClosed() {
      closing || (closing = !0, await fullscreen.exit().catch((error) => {
        console.warn("[ehpeek] Failed to exit fullscreen", error);
      }), disposeRoot(), disposeRoot = () => {
      }, unlockPageScroll(), unlockPageScroll = () => {
      }, host.remove(), await fullscreen.restore().catch((error) => {
        console.warn("[ehpeek] Failed to restore page viewport", error);
      }), activeReaderClose === close && (activeReaderClose = void 0), onExit());
    }
    host.isConnected || document.body.append(host), window.addEventListener("popstate", onPopState), window.history.pushState({
      ehpeekReader: !0
    }, "", window.location.href), activeReaderClose = close;
    let unsubscribeFullscreen = fullscreen.subscribe((active) => {
      setFullscreenActive(active), !active && !keepReaderOpen && requestClose(), keepReaderOpen = !1;
    });
    disposeRoot = render(() => {
      let [fullscreenActive, updateFullscreenActive] = createSignal(fullscreen.active());
      return setFullscreenActive = updateFullscreenActive, createComponent(Reader, {
        get callbacks() {
          return {
            ...callbacks,
            onClosed: requestClose,
            onFullscreenToggle: () => {
              fullscreen.active() ? (keepReaderOpen = !0, fullscreen.exit().catch((error) => {
                keepReaderOpen = !1, console.warn("[ehpeek] Failed to exit fullscreen", error);
              })) : fullscreen.enter().catch((error) => {
                console.warn("[ehpeek] Fullscreen request failed", error);
              });
            }
          };
        },
        options,
        previewCache,
        get fullscreenActive() {
          return fullscreenActive();
        }
      });
    }, host);
    let previousDispose = disposeRoot;
    disposeRoot = () => {
      window.removeEventListener("popstate", onPopState), unsubscribeFullscreen(), previousDispose();
    };
  }
  function requestConfiguredFullscreen(viewportSource) {
    if (!state.reader.enabled.value || !state.reader.fullscreen.value || document.fullscreenElement)
      return;
    let host = createReaderHost();
    document.body.append(host);
    let fullscreen = viewportSource.createFullscreen(host);
    return !document.fullscreenEnabled || typeof host.requestFullscreen != "function" ? {
      fullscreen,
      host,
      result: Promise.resolve(!1)
    } : {
      fullscreen,
      host,
      result: fullscreen.enter().then(() => !0, (error) => (console.warn("[ehpeek] Fullscreen request failed", error), !1))
    };
  }
  function createReaderHost() {
    let host = document.createElement("div");
    return host.dataset.ehpeekReaderContainer = "true", host;
  }
  function reportReaderOpenError(error) {
    let message = error instanceof Error ? error.message : texts_default.errors.loadFailed;
    console.error("[ehpeek]", error), window.alert(message);
  }

  // src/App/GalleryPreviewCache.ts
  var PREVIEW_CACHE_LIMIT = 10;
  function createGalleryPreviewCache(initialPreview) {
    let [current, setCurrent] = createSignal(initialPreview), [loading, setLoading] = createSignal(!1), previews = /* @__PURE__ */ new Map(), pages = /* @__PURE__ */ new Map(), pending = /* @__PURE__ */ new Map(), pageSize = initialPreview.data.pageSize, maxPreviewIndex = initialPreview.data.maxIndex, currentPreviewIndex = initialPreview.data.currentIndex, selectionId = 0, remember = (preview) => {
      let index = preview.data.currentIndex;
      previews.delete(index), previews.set(index, preview);
      for (let page of preview.data.pages)
        page.pageNum && page.pageNum > 0 && pages.set(page.pageNum, page);
      for (; previews.size > PREVIEW_CACHE_LIMIT; ) {
        let removable;
        for (let candidate of previews.keys())
          if (candidate !== currentPreviewIndex && candidate !== index) {
            removable = candidate;
            break;
          }
        if (removable === void 0)
          break;
        previews.delete(removable);
      }
    }, previewIndexForPage = (pageNum) => previewPageIndexForGalleryPage(
      pageNum,
      pageSize,
      maxPreviewIndex
    ), load = (previewIndex) => {
      if (previewIndex < 0 || previewIndex > maxPreviewIndex)
        return Promise.reject(new RangeError(`Invalid Preview index: ${previewIndex}`));
      let cached = previews.get(previewIndex);
      if (cached)
        return previews.delete(previewIndex), previews.set(previewIndex, cached), Promise.resolve(cached);
      let existing = pending.get(previewIndex);
      if (existing)
        return existing;
      setLoading(!0);
      let request = loadGalleryPreviewPage(
        previewIndex,
        initialPreview.data.currentUrl
      ).then(
        (preview) => (pending.delete(previewIndex), setLoading(pending.size > 0), remember(preview), preview),
        (error) => {
          throw pending.delete(previewIndex), setLoading(pending.size > 0), error;
        }
      );
      return pending.set(previewIndex, request), request;
    }, getPages = async (pageNums) => {
      let requested = Array.from(new Set(pageNums.filter((pageNum) => pageNum > 0))), previewIndexes = Array.from(new Set(requested.filter((pageNum) => !pages.has(pageNum)).map(previewIndexForPage)));
      return await Promise.all(previewIndexes.map(load)), requested.flatMap((pageNum) => pages.get(pageNum) ?? []);
    }, select = async (previewIndex) => {
      if (previewIndex === current().data.currentIndex)
        return current();
      let activeSelection = ++selectionId, preview = await load(previewIndex);
      return activeSelection === selectionId && (currentPreviewIndex = preview.data.currentIndex, setCurrent(preview), window.history.replaceState(
        window.history.state,
        "",
        preview.data.currentUrl
      )), preview;
    };
    return remember(initialPreview), {
      current,
      getPages,
      load,
      loadImage: loadEhImagePage,
      loading,
      previewIndexForPage,
      select
    };
  }

  // src/App/host.ts
  function createAppMount(className2 = "", host = document.body ?? document.documentElement) {
    let mount = createManagedElement("div");
    return className2 && mount.replaceClasses(className2), host.append(mount.Component()), mount;
  }

  // src/App/viewport.ts
  var FULLSCREEN_UI_SCALE_PROPERTY = "--ehpeek-reader-fullscreen-ui-scale", FULLSCREEN_PROGRESS_SIZE_PROPERTY = "--ehpeek-reader-fullscreen-progress-size";
  function lockPageScroll() {
    let documentElement2 = document.documentElement, body = document.body, documentOverflow = documentElement2.style.overflow, bodyOverflow = body.style.overflow;
    return documentElement2.style.overflow = "hidden", body.style.overflow = "hidden", () => {
      documentElement2.style.overflow = documentOverflow, body.style.overflow = bodyOverflow;
    };
  }
  function pageViewportForFullscreen() {
    let existing = document.querySelector(
      'meta[name="viewport"]'
    ), meta = existing ?? document.createElement("meta"), scale = Math.max(0.1, window.visualViewport?.scale ?? 1), snapshot = {
      content: existing?.getAttribute("content") ?? null,
      created: !existing,
      meta,
      scale,
      scrollX: window.scrollX,
      scrollY: window.scrollY
    };
    return existing || (meta.name = "viewport", document.head.append(meta)), meta.content = lockedViewportContent(snapshot.content, scale), snapshot;
  }
  async function restorePageViewport(snapshot) {
    await nextAnimationFrame(), snapshot.created ? snapshot.meta.remove() : snapshot.content === null ? snapshot.meta.removeAttribute("content") : snapshot.meta.content = snapshot.content, await nextAnimationFrame(), await nextAnimationFrame(), window.scrollTo(snapshot.scrollX, snapshot.scrollY);
  }
  var readerViewport = {
    createFullscreen: createReaderFullscreen,
    lockScroll: lockPageScroll,
    prepareFullscreen: pageViewportForFullscreen,
    restore: restorePageViewport
  };
  function createReaderFullscreen(target, initialSnapshot = null) {
    let snapshot = initialSnapshot, restorePromise = null, restore = async () => {
      target.style.removeProperty(FULLSCREEN_UI_SCALE_PROPERTY), target.style.removeProperty(FULLSCREEN_PROGRESS_SIZE_PROPERTY), snapshot && (restorePromise ?? (restorePromise = restorePageViewport(snapshot).finally(() => {
        restorePromise = null;
      })), await restorePromise);
    };
    return {
      active: () => document.fullscreenElement === target,
      enter: async () => {
        if (document.fullscreenElement || !document.fullscreenEnabled)
          return;
        snapshot = pageViewportForFullscreen();
        let scaleBefore = window.visualViewport?.scale ?? 1;
        try {
          await target.requestFullscreen(), await nextAnimationFrame();
          let scaleAfter = window.visualViewport?.scale ?? 1, uiScale = Math.min(1, Math.max(0.25, scaleBefore / Math.max(scaleAfter, 0.01))), progressSize = Number.parseFloat(getComputedStyle(target).getPropertyValue("--font-size-lg")) || 24;
          target.style.setProperty(FULLSCREEN_UI_SCALE_PROPERTY, String(uiScale)), target.style.setProperty(FULLSCREEN_PROGRESS_SIZE_PROPERTY, `${progressSize * uiScale}px`);
        } catch (error) {
          throw await restore(), error;
        }
      },
      exit: async () => {
        document.fullscreenElement === target && await document.exitFullscreen(), snapshot && await waitForViewportSettled(), target.style.removeProperty(FULLSCREEN_UI_SCALE_PROPERTY), target.style.removeProperty(FULLSCREEN_PROGRESS_SIZE_PROPERTY);
      },
      restore,
      subscribe: (callback) => {
        let onChange = () => {
          let active = document.fullscreenElement === target;
          active || (target.style.removeProperty(FULLSCREEN_UI_SCALE_PROPERTY), target.style.removeProperty(FULLSCREEN_PROGRESS_SIZE_PROPERTY)), callback(active);
        };
        return document.addEventListener("fullscreenchange", onChange), () => document.removeEventListener("fullscreenchange", onChange);
      }
    };
  }
  function lockedViewportContent(content, scale) {
    let preserved = (content ?? "").split(",").map((item) => item.trim()).filter(
      (item) => item && !/^(?:initial-scale|minimum-scale|maximum-scale|user-scalable|viewport-fit)\s*=/i.test(item)
    ), value = String(Math.round(scale * 1e3) / 1e3);
    return [
      ...preserved,
      `initial-scale=${value}`,
      `minimum-scale=${value}`,
      `maximum-scale=${value}`,
      "user-scalable=no",
      "viewport-fit=cover"
    ].join(", ");
  }
  function nextAnimationFrame() {
    return new Promise((resolve) => {
      window.requestAnimationFrame(() => resolve());
    });
  }
  async function waitForViewportSettled() {
    await nextAnimationFrame(), await new Promise((resolve) => {
      let viewport = window.visualViewport, quietTimer = window.setTimeout(finish, 80), timeoutTimer = window.setTimeout(finish, 500), onResize = () => {
        window.clearTimeout(quietTimer), quietTimer = window.setTimeout(finish, 80);
      };
      function finish() {
        viewport?.removeEventListener("resize", onResize), window.clearTimeout(quietTimer), window.clearTimeout(timeoutTimer), resolve();
      }
      viewport?.addEventListener("resize", onResize);
    }), await nextAnimationFrame();
  }

  // src/App/index.tsx
  var _tmpl$50 = /* @__PURE__ */ template("<a href=#>");
  function settingsMenuState(defaults = !1) {
    let read = (setting) => defaults ? setting.defaultValue : setting.value;
    return {
      openGalleryInNewTab: read(state.app.openGalleryInNewTab),
      readerEnabled: read(state.reader.enabled),
      readerFullscreenEnabled: read(state.reader.fullscreen),
      enhanceThumbsGridsEnabled: read(state.gallery.enhanceThumbs),
      enhanceSearchGridsEnabled: read(state.search.enhance),
      myTagsEnabled: read(state.gallery.myTags),
      readHistoryEnabled: read(state.gallery.readHistory),
      searchHistoryEnabled: read(state.search.history),
      touchUiEnabled: read(state.touch.enabled)
    };
  }
  function applySettingsMenuState(next) {
    state.app.openGalleryInNewTab.set(next.openGalleryInNewTab), state.reader.enabled.set(next.readerEnabled), state.reader.fullscreen.set(next.readerFullscreenEnabled), state.gallery.enhanceThumbs.set(next.enhanceThumbsGridsEnabled), state.search.enhance.set(next.enhanceSearchGridsEnabled), state.gallery.myTags.set(next.myTagsEnabled), state.gallery.readHistory.set(next.readHistoryEnabled), state.search.history.set(next.searchHistoryEnabled), state.touch.enabled.set(next.touchUiEnabled), window.location.reload();
  }
  var gState = (() => {
    let [settingsMenuOpen, setSettingsMenuOpen] = createSignal(!1), [readProgress, setReadProgress] = createSignal({
      currentPage: 1,
      hasHistory: !1,
      totalPages: null
    });
    return {
      readProgress,
      setReadProgress,
      settings: settingsMenuState(),
      settingsMenuOpen,
      setSettingsMenuOpen,
      thumbsGridsActions: void 0
    };
  })();
  document.documentElement.setAttribute("data-ehpeek-site", ehSiteTheme());
  registerGlobalStyle("ehpeek-uno-style", ehpeek_uno_default);
  registerGlobalStyle("ehpeek-theme-style", theme_default);
  var readerCallbacks = {
    enhanceThumbsGridsEnabled: gState.settings.enhanceThumbsGridsEnabled,
    readHistoryEnabled: gState.settings.readHistoryEnabled,
    onGotoPreviewIndex: (previewIndex) => {
      gState.thumbsGridsActions?.gotoPreview(previewIndex);
    },
    onReaderClosed: (currentPage, totalPages) => {
      gState.setReadProgress({
        currentPage,
        hasHistory: !0,
        totalPages
      });
    }
  };
  function allowFeatureFailure(name, run) {
    try {
      return run();
    } catch (error) {
      return console.error(`[ehpeek] ${name} failed`, error), null;
    }
  }
  async function allowAsyncFeatureFailure(name, run) {
    try {
      return await run();
    } catch (error) {
      return console.error(`[ehpeek] ${name} failed`, error), null;
    }
  }
  function openGalleryPage(previewCache, startPageUrl, preferredPageNum) {
    state.reader.enabled.value ? openReaderFromUserAction(startPageUrl, readerCallbacks, previewCache, readerViewport, preferredPageNum) : preferredPageNum !== void 0 && openOriginalReader(preferredPageNum, previewCache).catch(reportReaderOpenError);
  }
  function openFromReadButton(previewCache) {
    let pageNum = gState.readProgress().currentPage, firstPage = previewCache.current().data.pages[0];
    firstPage && openGalleryPage(previewCache, firstPage.url, pageNum);
  }
  function GalleryReadButton(props) {
    return createComponent(ReadButton, {
      get currentPage() {
        return gState.readProgress().currentPage;
      },
      get hasHistory() {
        return gState.readProgress().hasHistory;
      },
      get totalPages() {
        return gState.readProgress().totalPages;
      },
      onClick: () => openFromReadButton(props.previewCache),
      get variant() {
        return props.variant;
      }
    });
  }
  function installSettingsMenu() {
    typeof GM_registerMenuCommand == "function" && GM_registerMenuCommand(texts_default.settings.openSettings, () => {
      gState.setSettingsMenuOpen(!0);
    }), createAppMount("fixed inset-0 z-[1150] pointer-events-none").mount(() => createComponent(SettingsMenu, {
      get open() {
        return gState.settingsMenuOpen();
      },
      get defaultState() {
        return settingsMenuState(!0);
      },
      get initState() {
        return gState.settings;
      },
      onApply: (next) => {
        applySettingsMenuState(next);
      },
      get onOpenChange() {
        return gState.setSettingsMenuOpen;
      }
    }));
  }
  function injectEnhanceUI(page, previewCache, searchTextInput, searchResultsDom, touchResultsDom) {
    let galleryPage = page.type === "gallery", resultsPage = page.type === "search" || page.type === "favorites", preview = previewCache?.current() ?? null, previewMount = preview?.elems.mount ?? null, updateSearchGridModeSelector = () => {
      mutateSearchGridModeSelect(state.search.grid.value, () => {
        state.search.grid.set(!0), window.location.assign(new URL("/?inline_set=dm_e", window.location.href).href);
      }, () => {
        state.search.grid.set(!1);
      });
    };
    galleryPage && preview && previewCache && gState.settings.readerEnabled && allowFeatureFailure("Reader thumbnail links", () => {
      preview.handle.interceptPreviewImageOpen((pageUrl) => {
        openGalleryPage(previewCache, pageUrl);
      });
    }), resultsPage && allowFeatureFailure("Search grid mode selector", () => {
      updateSearchGridModeSelector();
    });
    let searchGridEnabled = !!(resultsPage && state.search.grid.value);
    searchGridEnabled && allowFeatureFailure("Search grid", () => mutateSearchGrid()), gState.settings.openGalleryInNewTab && searchResultsDom && allowFeatureFailure("Gallery links in new tabs", () => {
      searchResultsDom.handle.ensureGalleryLinksOpenInNewTab();
    }), gState.settings.touchUiEnabled || allowFeatureFailure("Desktop settings entry", () => {
      let settingsMount = manageSettingsMenuMount();
      settingsMount && settingsMount.mount(() => (() => {
        var _el$ = _tmpl$50();
        return _el$.$$click = (event) => {
          event.preventDefault(), event.stopPropagation(), gState.setSettingsMenuOpen(!0);
        }, insert(_el$, () => texts_default.settings.menuLabel), _el$;
      })());
    }), !gState.settings.touchUiEnabled && gState.settings.readHistoryEnabled && galleryPage && preview && previewCache && allowFeatureFailure("Desktop Read button", () => {
      manageGalleryContinueReadingButtonMount().mount(() => createComponent(GalleryReadButton, {
        previewCache,
        variant: "gallery"
      }));
    }), galleryPage && gState.settings.enhanceThumbsGridsEnabled && previewCache && previewMount ? allowFeatureFailure("Enhanced thumbnail grid", () => {
      previewMount.mount(() => createComponent(ThumbsGrids, {
        actionsRef: (actions) => {
          gState.thumbsGridsActions = actions;
        },
        onLoadError: reportReaderOpenError,
        previewCache
      }));
    }) : galleryPage && preview && previewCache && allowFeatureFailure("Original thumbnail grid", () => {
      preview.elems.mount?.remove();
    }), gState.settings.enhanceSearchGridsEnabled && searchResultsDom && (searchResultsDom.data.previousUrl || searchResultsDom.data.nextUrl) && allowFeatureFailure("Enhanced Search pagination", () => {
      createAppMount().mount(() => createComponent(EnhanceSearchGrids, {
        source: searchResultsDom,
        onPageChange: (source) => {
          allowFeatureFailure("Changed Search page", () => {
            updateSearchGridModeSelector(), gState.settings.openGalleryInNewTab && source.handle.ensureGalleryLinksOpenInNewTab(), touchResultsDom?.handle.updateTouchResultsLayout(), searchGridEnabled && mutateSearchGrid();
          });
        }
      }));
    }), gState.settings.searchHistoryEnabled && searchTextInput && allowFeatureFailure("Search history", () => {
      createAppMount().mount(() => createComponent(SearchHistory, {
        source: searchTextInput
      }));
    });
  }
  function injectTouchUI(page, previewCache) {
    let galleryPage = page.type === "gallery", resultsPage = page.type === "search" || page.type === "favorites", preview = previewCache?.current() ?? null, resultsDom = resultsPage ? allowFeatureFailure("Touch results layout", () => manageTouchResultsPage(page)) : null;
    return allowFeatureFailure("Touch top bar", () => {
      let topBarDom = manageTopBar();
      topBarDom && (topBarDom.handle.updateNavItemVisual(TOUCH_TOP_BAR_NAV_ITEM_CLASS), topBarDom.elems.mount.mount(() => createComponent(TouchTopBar, {
        source: topBarDom,
        onSettingsMenuOpen: () => {
          gState.setSettingsMenuOpen(!0);
        }
      })));
    }), (galleryPage || resultsPage) && allowFeatureFailure("Back to top", () => {
      createAppMount("ehpeek-back-to-top-host").mount(() => createComponent(BackToTop, {}));
    }), galleryPage && (allowFeatureFailure("Touch GalleryInfo", () => {
      registerGlobalStyle("ehpeek-touch-gallery-page-rearrange-style", galleryRearrange_default);
      let galleryInfoDom = manageGalleryInfo(preview?.data ?? null);
      galleryInfoDom && (galleryInfoDom.handle.updateCoverVisual(TOUCH_GALLERY_INFO_CLASSES.cover), galleryInfoDom.handle.updateActionItemsVisual(TOUCH_GALLERY_INFO_CLASSES.actionItems), galleryInfoDom.handle.updateNewTagVisual(TOUCH_GALLERY_INFO_CLASSES.newTag), galleryInfoDom.handle.installGalleryInfoPanel(TOUCH_GALLERY_INFO_CLASSES.host), galleryInfoDom.elems.mount.mount(() => createComponent(GalleryInfoPanel, {
        source: galleryInfoDom,
        get primaryAction() {
          return memo(() => !!(gState.settings.readHistoryEnabled && preview && previewCache))() ? createComponent(GalleryReadButton, {
            previewCache,
            variant: "touchGallery"
          }) : void 0;
        }
      })));
    }), allowFeatureFailure("Touch Gallery comments", () => {
      mutateGalleryCommentsTouch();
    })), resultsPage && allowFeatureFailure("Touch Search panel", () => {
      let searchPanelDom = manageSearchPanel();
      searchPanelDom && (searchPanelDom.handle.updateSearchPanelVisual(touchSearchPanelClasses(searchPanelDom.data.hasClear)), searchPanelDom.elems.mount.mount(() => createComponent(TouchSearchPanel, {
        source: searchPanelDom,
        get after() {
          return memo(() => !!resultsDom?.data.favoritesCategory)() ? createComponent(FavoritesCategorySelect, {
            source: resultsDom
          }) : void 0;
        }
      })), searchPanelDom.elems.categoryToggleMount && searchPanelDom.elems.categoryToggleMount.mount(() => createComponent(TouchSearchCategoryToggle, {
        source: searchPanelDom
      })), searchPanelDom.elems.advancedToggleMount && searchPanelDom.elems.advancedToggleMount.mount(() => createComponent(TouchSearchOptionToggle, {
        option: "advancedOptions",
        source: searchPanelDom
      })), searchPanelDom.elems.fileSearchToggleMount && searchPanelDom.elems.fileSearchToggleMount.mount(() => createComponent(TouchSearchOptionToggle, {
        option: "fileSearch",
        source: searchPanelDom
      })), searchPanelDom.elems.searchActionMount.mount(() => createComponent(TouchSearchAction, {
        action: "search",
        source: searchPanelDom
      })), searchPanelDom.elems.clearActionMount && searchPanelDom.elems.clearActionMount.mount(() => createComponent(TouchSearchAction, {
        action: "clear",
        source: searchPanelDom
      })));
    }), resultsDom;
  }
  async function injectPage(page) {
    let galleryPage = page.type === "gallery", resultsPage = page.type === "search" || page.type === "favorites", galleryPreview = galleryPage ? allowFeatureFailure("Gallery Preview", () => manageGalleryPreview()) : null, galleryPreviewCache = galleryPreview ? allowFeatureFailure("Gallery Preview cache", () => createGalleryPreviewCache(galleryPreview)) : null;
    page.type === "gallery" && galleryPreview && allowFeatureFailure("Gallery Read History", () => {
      let record = loadReadHistory(page.galleryId, page.token);
      gState.setReadProgress({
        currentPage: record?.pageNum && record.pageNum > 0 ? record.pageNum : 1,
        hasHistory: record !== null,
        totalPages: record?.totalPages ?? galleryPreview.data.totalImages
      });
    });
    let searchTextInput = resultsPage ? allowFeatureFailure("Search text input", () => manageSearchTextInput()) : null, searchResultsSource = resultsPage ? allowFeatureFailure("Search results", () => manageSearchResults()) : null;
    if (gState.settings.myTagsEnabled) {
      if (page.type === "myTags")
        allowAsyncFeatureFailure("My Tags refresh", async () => {
          let currentMyTags = extractMyTagsPageData();
          await refreshMyTags(currentMyTags);
        });
      else if (galleryPage) {
        let myTagAppearances = loadMyTagAppearances();
        myTagAppearances ? allowFeatureFailure("Gallery My Tags appearance", () => {
          mutateGalleryMyTags(myTagAppearances);
        }) : allowAsyncFeatureFailure("My Tags appearance", async () => {
          let appearances = await refreshMyTags();
          appearances && mutateGalleryMyTags(appearances);
        });
      }
    }
    gState.settings.readHistoryEnabled && page.type === "image" && allowFeatureFailure("Image Read History", () => {
      let gallery = extractImageGalleryPage();
      if (gallery?.galleryId === page.galleryId) {
        let previous = loadReadHistory(gallery.galleryId, gallery.token);
        new ReadHistorySession({
          galleryId: gallery.galleryId,
          token: gallery.token,
          totalPages: previous?.totalPages
        }).update(page.pageNum, previous?.totalPages);
      }
    });
    let touchResultsDom = gState.settings.touchUiEnabled ? injectTouchUI(page, galleryPreviewCache) : null;
    injectEnhanceUI(page, galleryPreviewCache, searchTextInput, searchResultsSource, touchResultsDom), page.type === "gallery" && state.reader.enabled.value && page.peekPage !== null && galleryPreviewCache && allowAsyncFeatureFailure("Reader deep link", () => openReaderFromHash(readerCallbacks, galleryPreviewCache, readerViewport));
  }
  ehSyringe_exports.initialize();
  async function startApp() {
    document.readyState === "loading" && await new Promise((resolve) => {
      document.addEventListener("DOMContentLoaded", () => resolve(), {
        once: !0
      });
    }), installSettingsMenu(), await injectPage(extractPageType());
  }
  startApp().catch((error) => {
    console.error("[ehpeek] App startup failed", error);
  });
  delegateEvents(["click"]);
})();