91 Plus

自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         91 Plus
// @namespace    https://github.com/DonkeyBear
// @version      1.9.1
// @author       DonkeyBear
// @description  自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!
// @icon         https://www.91pu.com.tw/icons/favicon-32x32.png
// @match        *://www.91pu.com.tw/m/*
// @match        *://www.91pu.com.tw/song/*
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/zipson.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/vexchords.dev.min.js
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        unsafeWindow
// ==/UserScript==

(function (vue, vexchords, zipson, html2canvas) {
  'use strict';

  const d=new Set;const importCSS = async e=>{d.has(e)||(d.add(e),(t=>{typeof GM_addStyle=="function"?GM_addStyle(t):document.head.appendChild(document.createElement("style")).append(t);})(e));};

  importCSS(" .bi[data-v-a9f332df]{color:var(--c22eb872);font-size:var(--v70824f1d);-webkit-text-stroke:var(--v59c28e54) var(--c22eb872);line-height:0}.bi[data-v-a9f332df]:before{transition:text-shadow .2s}.bi[active=true][data-v-a9f332df]:before{text-shadow:0 0 .5rem color-mix(in srgb,var(--theme-color) 50%,white)}.chord-container .chord-name[data-v-128703be]{font-size:.5rem;font-weight:900;color:#666;text-align:center}.chord-container .chord-chart[data-v-128703be]{margin:-.6rem 0 -.25rem}html[data-v-ed81246a]{--toolbar-bg-color: color-mix(in srgb, var(--theme-color) 65%, transparent);--toolbar-border-color: color-mix(in srgb, var(--theme-color) 50%, rgba(255, 255, 255, .1))}.slide-and-fade-enter-active[data-v-ed81246a],.slide-and-fade-leave-active[data-v-ed81246a]{transition:all .2s}.slide-and-fade-enter-from[data-v-ed81246a],.slide-and-fade-leave-to[data-v-ed81246a]{transform:translateY(10%);opacity:0}.plus91-popup[data-v-ed81246a]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll}.plus91-popup[data-v-ed81246a]::-webkit-scrollbar{display:none}.plus91-popup[data-v-ed81246a]{padding-left:1rem;padding-right:1rem}#plus91-chord-popup .banner[data-v-858a1989]{display:flex;align-items:center;background:color-mix(in srgb,var(--toolbar-bg-color) 25%,transparent);color:color-mix(in srgb,var(--toolbar-bg-color) 50%,black);border-radius:.5rem;padding:.5rem .75rem;margin-bottom:.25rem}#plus91-chord-popup .banner section[data-v-858a1989]{flex-grow:1;margin-left:.5rem}#plus91-chord-popup .chord-popup-container[data-v-858a1989]{display:grid;grid-template-columns:repeat(6,1fr);column-gap:.5rem;padding-top:.4rem}#plus91-chord-popup.banner-only .banner[data-v-858a1989]{margin-bottom:0;background:#f6d26640;color:color-mix(in srgb,#f6d266 50%,black 35%)}#plus91-chord-popup.banner-only .chord-popup-container[data-v-858a1989]{padding-top:0}.toolbar-icon[data-v-cbf0cf0b]{cursor:pointer;padding:.25rem .75rem;display:flex;flex-direction:column;align-items:center;gap:.15rem}.toolbar-icon-text[data-v-cbf0cf0b]{color:color-mix(in srgb,var(--v08ff4922) 70%,var(--toolbar-bg-color));font-size:.5rem;letter-spacing:.15rem;margin-right:-.15rem}.adjust-widget[data-v-0178875e]{display:flex}.adjust-widget .adjust-button[data-v-0178875e]{border:0;border-radius:.25rem;background:transparent}.adjust-widget .adjust-button[data-v-0178875e]:hover{background:#00000006}.adjust-widget .adjust-button[data-v-0178875e]:not(:disabled){cursor:pointer}.adjust-widget .adjust-button[data-v-0178875e]:disabled{opacity:.25}.adjust-widget .adjust-button.adjust-button-middle[data-v-0178875e]{flex-grow:1;color:var(--v5e7bf01c);font-size:calc(var(--v1bd9a428) * .75);font-weight:700}.adjust-widget .adjust-button.adjust-button-left[data-v-0178875e]{padding-right:1rem}.adjust-widget .adjust-button.adjust-button-right[data-v-0178875e]{padding-left:1rem}.hotkey-item[data-v-851f225f]{display:flex;justify-content:space-between;align-items:center;padding:0 .25rem;border-radius:.25rem;height:1.4rem}.hotkey-item[data-v-851f225f]:nth-child(odd){background:#00000006}.desc.title[data-v-851f225f]{font-size:.55rem;color:#999}.hotkeys[data-v-851f225f]{display:flex}.hr[data-v-851f225f]{display:flex;flex-grow:1;border-top:1px solid lightgray;margin-left:.25rem}kbd[data-v-851f225f]{font-size:.6rem;border:solid lightgray;border-width:1px .1rem .15rem;border-radius:.2rem;padding:0 .2rem;letter-spacing:-.025rem;color:#666;margin-left:.15rem}#plus91-hotkey-popup .hotkey-popup-container[data-v-07402c98]{display:flex;color:#444}#plus91-hotkey-popup section[data-v-07402c98]{flex-grow:1;width:50%;margin:-.1rem 0}#plus91-hotkey-popup section.left-part[data-v-07402c98]{border-right:1px solid lightgray;margin-left:-.5rem;padding-right:.5rem}#plus91-hotkey-popup section.right-part[data-v-07402c98]{padding-left:.5rem;margin-right:-.5rem}#plus91-hotkey-popup kbd[data-v-07402c98]{font-size:.65rem;border:solid lightgray;border-width:1px .1rem .15rem;border-radius:.2rem;padding:0 .2rem;letter-spacing:-.025rem;color:#666}.icon-button[data-v-cb0cf859]{display:flex;flex-direction:column;align-items:center;cursor:pointer;padding:0 .6rem .4rem;border-radius:.25rem}.icon-button[data-v-cb0cf859]:hover{background:#00000006}.icon-button .button-text[data-v-cb0cf859]{font-size:.5rem;color:var(--v12c3e3a5)}#plus91-menu-popup .menu-popup-container[data-v-f8df8357]{display:flex;justify-content:space-around}.color-switcher-container[data-v-9499f72a]{display:flex}.color-switcher-container .color-switcher-option[data-v-9499f72a]{display:flex;align-items:center;justify-content:center;width:1.25em;height:1em;cursor:pointer;border-style:solid;border-width:0;border-top-width:1px;border-bottom-width:1px}.color-switcher-container .color-switcher-option[data-v-9499f72a]:first-child{border-radius:50rem 0 0 50rem;border-left-width:1px;width:1.3em}.color-switcher-container .color-switcher-option[data-v-9499f72a]:last-child{border-radius:0 50rem 50rem 0;border-right-width:1px;width:1.3em}.toggle-switch[data-v-1cf8e431]{display:inline-flex;cursor:pointer}.switch-track[data-v-1cf8e431]{position:relative;width:2.25em;height:1.25em;background:#ccc;border-radius:50rem;transition:background .3s}.switch-track.active[data-v-1cf8e431]{background:#1e90ff}.switch-track.active .switch-thumb[data-v-1cf8e431]{transform:translate(100%)}.switch-thumb[data-v-1cf8e431]{position:absolute;top:.125em;left:.125em;width:1em;height:1em;background:#fff;border-radius:50%;transition:transform .3s}#plus91-settings-popup .bi[data-v-3259211c]{color:#a9a9a9;margin-right:.25em;font-size:1em}#plus91-settings-popup .setting-item[data-v-3259211c]{display:flex;align-items:center;justify-content:space-between;padding:.5rem 1rem;border-radius:.5rem;color:#000c;cursor:pointer}#plus91-settings-popup .setting-item[data-v-3259211c]:hover{background:#0000000d}#plus91-sheet-popup .transpose-range-container[data-v-18539399]{margin-top:1rem}#plus91-sheet-popup .transpose-range-container input[type=range][data-v-18539399]{width:100%}#plus91-sheet-popup .instrument-select-container[data-v-18539399]{display:flex;border:1px solid lightgray;border-radius:.25rem;margin-top:1rem;background:#fff}#plus91-sheet-popup .instrument-select-container .instrument-select-button[data-v-18539399]{width:33.3333333333%;border:0;border-right:1px solid lightgray;background:transparent;color:#666;padding:.5rem;font-size:.65rem;font-weight:700;cursor:pointer!important}#plus91-sheet-popup .instrument-select-container .instrument-select-button[data-v-18539399]:last-child{border:0;border-radius:0 .25rem .25rem 0}#plus91-sheet-popup .instrument-select-container .instrument-select-button[data-v-18539399]:first-child{border-radius:.25rem 0 0 .25rem}#plus91-sheet-popup .instrument-select-container .instrument-select-button[data-v-18539399]:hover{background:#f5f5f5}html[data-v-c2303173]{--toolbar-bg-color: color-mix(in srgb, var(--theme-color) 65%, transparent);--toolbar-border-color: color-mix(in srgb, var(--theme-color) 50%, rgba(255, 255, 255, .1))}.slide-enter-active[data-v-c2303173],.slide-leave-active[data-v-c2303173]{transition:transform .2s}.slide-enter-from[data-v-c2303173],.slide-leave-to[data-v-c2303173]{transform:translateY(100%)}#plus91-footer[data-v-c2303173]{z-index:1000;display:flex;justify-content:center;position:fixed;left:0;right:0;bottom:0}.footer-container[data-v-c2303173]{width:min(100vw,768px);background-color:var(--toolbar-bg-color);background-image:linear-gradient(transparent,color-mix(in srgb,var(--toolbar-bg-color) 99%,white) 250%);background-blend-mode:multiply;transition:background-color .3s ease,border-color .3s ease;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.25rem .75rem;display:flex;justify-content:space-between;align-items:center}@media (min-width: 768px){.footer-container[data-v-c2303173]{border-radius:1rem 1rem 0 0}}.footer-container[data-v-c2303173]{padding-bottom:.75rem;border-top:1px solid var(--toolbar-border-color)}html[data-v-c3427f68]{--toolbar-bg-color: color-mix(in srgb, var(--theme-color) 65%, transparent);--toolbar-border-color: color-mix(in srgb, var(--theme-color) 50%, rgba(255, 255, 255, .1))}.slide-enter-active[data-v-c3427f68],.slide-leave-active[data-v-c3427f68]{transition:transform .2s}.slide-enter-from[data-v-c3427f68],.slide-leave-to[data-v-c3427f68]{transform:translateY(-100%)}#plus91-header[data-v-c3427f68]{z-index:1000;display:flex;justify-content:center;position:fixed;left:0;right:0;top:0}.header-container[data-v-c3427f68]{width:min(100vw,768px);background-color:var(--toolbar-bg-color);background-image:linear-gradient(transparent,color-mix(in srgb,var(--toolbar-bg-color) 99%,white) 250%);background-blend-mode:multiply;transition:background-color .3s ease,border-color .3s ease;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.25rem .75rem;display:flex;justify-content:space-between;align-items:center}@media (min-width: 768px){.header-container[data-v-c3427f68]{border-radius:0 0 1rem 1rem}}.header-container[data-v-c3427f68]{border-bottom:1px solid var(--toolbar-border-color)}.header-container form[data-v-c3427f68],.header-container .search-container[data-v-c3427f68]{display:flex;flex:1;height:100%}.header-container .search-container[data-v-c3427f68]{position:relative}.header-container .search-container .clear-input[data-v-c3427f68]{position:absolute;right:0;top:50%;transform:translateY(-50%)}.header-container input[data-v-c3427f68]{flex:1;border-radius:50rem;border:0;font-size:.8rem;font-weight:700;padding-left:1.25rem;padding-right:1.25rem;background:#fffa;color:#0009;opacity:.5;transition:all .2s}.header-container input[data-v-c3427f68]:focus-visible{outline:0;opacity:1}html[data-v-111379c3]{--toolbar-bg-color: color-mix(in srgb, var(--theme-color) 65%, transparent);--toolbar-border-color: color-mix(in srgb, var(--theme-color) 50%, rgba(255, 255, 255, .1))}.fade-enter-active[data-v-111379c3],.fade-leave-active[data-v-111379c3]{transition:opacity .2s}.fade-enter-from[data-v-111379c3],.fade-leave-to[data-v-111379c3]{opacity:0}#dark-mode-overlay[data-v-111379c3]{position:fixed;inset:0;z-index:800;-webkit-backdrop-filter:invert(1) hue-rotate(145deg) saturate(.75);backdrop-filter:invert(1) hue-rotate(145deg) saturate(.75);pointer-events:none} ");

  /*!
   * pinia v3.0.3
   * (c) 2025 Eduardo San Martin Morote
   * @license MIT
   */
  let activePinia;
  const setActivePinia = (pinia2) => activePinia = pinia2;
  const piniaSymbol = (
Symbol()
  );
  function isPlainObject(o) {
    return o && typeof o === "object" && Object.prototype.toString.call(o) === "[object Object]" && typeof o.toJSON !== "function";
  }
  var MutationType;
  (function(MutationType2) {
    MutationType2["direct"] = "direct";
    MutationType2["patchObject"] = "patch object";
    MutationType2["patchFunction"] = "patch function";
  })(MutationType || (MutationType = {}));
  function createPinia() {
    const scope = vue.effectScope(true);
    const state = scope.run(() => vue.ref({}));
    let _p = [];
    let toBeInstalled = [];
    const pinia2 = vue.markRaw({
      install(app) {
        setActivePinia(pinia2);
        pinia2._a = app;
        app.provide(piniaSymbol, pinia2);
        app.config.globalProperties.$pinia = pinia2;
        toBeInstalled.forEach((plugin) => _p.push(plugin));
        toBeInstalled = [];
      },
      use(plugin) {
        if (!this._a) {
          toBeInstalled.push(plugin);
        } else {
          _p.push(plugin);
        }
        return this;
      },
      _p,

_a: null,
      _e: scope,
      _s: new Map(),
      state
    });
    return pinia2;
  }
  const noop$1 = () => {
  };
  function addSubscription(subscriptions, callback, detached, onCleanup = noop$1) {
    subscriptions.push(callback);
    const removeSubscription = () => {
      const idx = subscriptions.indexOf(callback);
      if (idx > -1) {
        subscriptions.splice(idx, 1);
        onCleanup();
      }
    };
    if (!detached && vue.getCurrentScope()) {
      vue.onScopeDispose(removeSubscription);
    }
    return removeSubscription;
  }
  function triggerSubscriptions(subscriptions, ...args) {
    subscriptions.slice().forEach((callback) => {
      callback(...args);
    });
  }
  const fallbackRunWithContext = (fn) => fn();
  const ACTION_MARKER = Symbol();
  const ACTION_NAME = Symbol();
  function mergeReactiveObjects(target, patchToApply) {
    if (target instanceof Map && patchToApply instanceof Map) {
      patchToApply.forEach((value, key) => target.set(key, value));
    } else if (target instanceof Set && patchToApply instanceof Set) {
      patchToApply.forEach(target.add, target);
    }
    for (const key in patchToApply) {
      if (!patchToApply.hasOwnProperty(key))
        continue;
      const subPatch = patchToApply[key];
      const targetValue = target[key];
      if (isPlainObject(targetValue) && isPlainObject(subPatch) && target.hasOwnProperty(key) && !vue.isRef(subPatch) && !vue.isReactive(subPatch)) {
        target[key] = mergeReactiveObjects(targetValue, subPatch);
      } else {
        target[key] = subPatch;
      }
    }
    return target;
  }
  const skipHydrateSymbol = (
Symbol()
  );
  function shouldHydrate(obj) {
    return !isPlainObject(obj) || !Object.prototype.hasOwnProperty.call(obj, skipHydrateSymbol);
  }
  const { assign } = Object;
  function isComputed(o) {
    return !!(vue.isRef(o) && o.effect);
  }
  function createOptionsStore(id, options, pinia2, hot) {
    const { state, actions, getters } = options;
    const initialState = pinia2.state.value[id];
    let store;
    function setup() {
      if (!initialState && true) {
        pinia2.state.value[id] = state ? state() : {};
      }
      const localState = vue.toRefs(pinia2.state.value[id]);
      return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => {
        computedGetters[name] = vue.markRaw(vue.computed(() => {
          setActivePinia(pinia2);
          const store2 = pinia2._s.get(id);
          return getters[name].call(store2, store2);
        }));
        return computedGetters;
      }, {}));
    }
    store = createSetupStore(id, setup, options, pinia2, hot, true);
    return store;
  }
  function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) {
    let scope;
    const optionsForPlugin = assign({ actions: {} }, options);
    const $subscribeOptions = { deep: true };
    let isListening;
    let isSyncListening;
    let subscriptions = [];
    let actionSubscriptions = [];
    let debuggerEvents;
    const initialState = pinia2.state.value[$id];
    if (!isOptionsStore && !initialState && true) {
      pinia2.state.value[$id] = {};
    }
    vue.ref({});
    let activeListener;
    function $patch(partialStateOrMutator) {
      let subscriptionMutation;
      isListening = isSyncListening = false;
      if (typeof partialStateOrMutator === "function") {
        partialStateOrMutator(pinia2.state.value[$id]);
        subscriptionMutation = {
          type: MutationType.patchFunction,
          storeId: $id,
          events: debuggerEvents
        };
      } else {
        mergeReactiveObjects(pinia2.state.value[$id], partialStateOrMutator);
        subscriptionMutation = {
          type: MutationType.patchObject,
          payload: partialStateOrMutator,
          storeId: $id,
          events: debuggerEvents
        };
      }
      const myListenerId = activeListener = Symbol();
      vue.nextTick().then(() => {
        if (activeListener === myListenerId) {
          isListening = true;
        }
      });
      isSyncListening = true;
      triggerSubscriptions(subscriptions, subscriptionMutation, pinia2.state.value[$id]);
    }
    const $reset = isOptionsStore ? function $reset2() {
      const { state } = options;
      const newState = state ? state() : {};
      this.$patch(($state) => {
        assign($state, newState);
      });
    } : (
noop$1
    );
    function $dispose() {
      scope.stop();
      subscriptions = [];
      actionSubscriptions = [];
      pinia2._s.delete($id);
    }
    const action = (fn, name = "") => {
      if (ACTION_MARKER in fn) {
        fn[ACTION_NAME] = name;
        return fn;
      }
      const wrappedAction = function() {
        setActivePinia(pinia2);
        const args = Array.from(arguments);
        const afterCallbackList = [];
        const onErrorCallbackList = [];
        function after(callback) {
          afterCallbackList.push(callback);
        }
        function onError(callback) {
          onErrorCallbackList.push(callback);
        }
        triggerSubscriptions(actionSubscriptions, {
          args,
          name: wrappedAction[ACTION_NAME],
          store,
          after,
          onError
        });
        let ret;
        try {
          ret = fn.apply(this && this.$id === $id ? this : store, args);
        } catch (error) {
          triggerSubscriptions(onErrorCallbackList, error);
          throw error;
        }
        if (ret instanceof Promise) {
          return ret.then((value) => {
            triggerSubscriptions(afterCallbackList, value);
            return value;
          }).catch((error) => {
            triggerSubscriptions(onErrorCallbackList, error);
            return Promise.reject(error);
          });
        }
        triggerSubscriptions(afterCallbackList, ret);
        return ret;
      };
      wrappedAction[ACTION_MARKER] = true;
      wrappedAction[ACTION_NAME] = name;
      return wrappedAction;
    };
    const partialStore = {
      _p: pinia2,
$id,
      $onAction: addSubscription.bind(null, actionSubscriptions),
      $patch,
      $reset,
      $subscribe(callback, options2 = {}) {
        const removeSubscription = addSubscription(subscriptions, callback, options2.detached, () => stopWatcher());
        const stopWatcher = scope.run(() => vue.watch(() => pinia2.state.value[$id], (state) => {
          if (options2.flush === "sync" ? isSyncListening : isListening) {
            callback({
              storeId: $id,
              type: MutationType.direct,
              events: debuggerEvents
            }, state);
          }
        }, assign({}, $subscribeOptions, options2)));
        return removeSubscription;
      },
      $dispose
    };
    const store = vue.reactive(partialStore);
    pinia2._s.set($id, store);
    const runWithContext = pinia2._a && pinia2._a.runWithContext || fallbackRunWithContext;
    const setupStore = runWithContext(() => pinia2._e.run(() => (scope = vue.effectScope()).run(() => setup({ action }))));
    for (const key in setupStore) {
      const prop = setupStore[key];
      if (vue.isRef(prop) && !isComputed(prop) || vue.isReactive(prop)) {
        if (!isOptionsStore) {
          if (initialState && shouldHydrate(prop)) {
            if (vue.isRef(prop)) {
              prop.value = initialState[key];
            } else {
              mergeReactiveObjects(prop, initialState[key]);
            }
          }
          pinia2.state.value[$id][key] = prop;
        }
      } else if (typeof prop === "function") {
        const actionValue = action(prop, key);
        setupStore[key] = actionValue;
        optionsForPlugin.actions[key] = prop;
      } else ;
    }
    assign(store, setupStore);
    assign(vue.toRaw(store), setupStore);
    Object.defineProperty(store, "$state", {
      get: () => pinia2.state.value[$id],
      set: (state) => {
        $patch(($state) => {
          assign($state, state);
        });
      }
    });
    pinia2._p.forEach((extender) => {
      {
        assign(store, scope.run(() => extender({
          store,
          app: pinia2._a,
          pinia: pinia2,
          options: optionsForPlugin
        })));
      }
    });
    if (initialState && isOptionsStore && options.hydrate) {
      options.hydrate(store.$state, initialState);
    }
    isListening = true;
    isSyncListening = true;
    return store;
  }

function defineStore(id, setup, setupOptions) {
    let options;
    const isSetupStore = typeof setup === "function";
    options = isSetupStore ? setupOptions : setup;
    function useStore2(pinia2, hot) {
      const hasContext = vue.hasInjectionContext();
      pinia2 =

pinia2 || (hasContext ? vue.inject(piniaSymbol, null) : null);
      if (pinia2)
        setActivePinia(pinia2);
      pinia2 = activePinia;
      if (!pinia2._s.has(id)) {
        if (isSetupStore) {
          createSetupStore(id, setup, options, pinia2);
        } else {
          createOptionsStore(id, options, pinia2);
        }
      }
      const store = pinia2._s.get(id);
      return store;
    }
    useStore2.$id = id;
    return useStore2;
  }
  const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
  const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
  const JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
  function jsonParseTransform(key, value) {
    if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
      warnKeyDropped(key);
      return;
    }
    return value;
  }
  function warnKeyDropped(key) {
    console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
  }
  function destr(value, options = {}) {
    if (typeof value !== "string") {
      return value;
    }
    if (value[0] === '"' && value[value.length - 1] === '"' && value.indexOf("\\") === -1) {
      return value.slice(1, -1);
    }
    const _value = value.trim();
    if (_value.length <= 9) {
      switch (_value.toLowerCase()) {
        case "true": {
          return true;
        }
        case "false": {
          return false;
        }
        case "undefined": {
          return void 0;
        }
        case "null": {
          return null;
        }
        case "nan": {
          return Number.NaN;
        }
        case "infinity": {
          return Number.POSITIVE_INFINITY;
        }
        case "-infinity": {
          return Number.NEGATIVE_INFINITY;
        }
      }
    }
    if (!JsonSigRx.test(value)) {
      if (options.strict) {
        throw new SyntaxError("[destr] Invalid JSON");
      }
      return value;
    }
    try {
      if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
        if (options.strict) {
          throw new Error("[destr] Possible prototype pollution");
        }
        return JSON.parse(value, jsonParseTransform);
      }
      return JSON.parse(value);
    } catch (error) {
      if (options.strict) {
        throw error;
      }
      return value;
    }
  }
  function get(obj, path) {
    if (obj == null)
      return void 0;
    let value = obj;
    for (let i = 0; i < path.length; i++) {
      if (value == null || value[path[i]] == null)
        return void 0;
      value = value[path[i]];
    }
    return value;
  }
  function set(obj, value, path) {
    if (path.length === 0)
      return value;
    const idx = path[0];
    if (path.length > 1) {
      value = set(
        typeof obj !== "object" || obj === null || !Object.prototype.hasOwnProperty.call(obj, idx) ? Number.isInteger(Number(path[1])) ? [] : {} : obj[idx],
        value,
        Array.prototype.slice.call(path, 1)
      );
    }
    if (Number.isInteger(Number(idx)) && Array.isArray(obj))
      return obj.slice()[idx];
    return Object.assign({}, obj, { [idx]: value });
  }
  function unset(obj, path) {
    if (obj == null || path.length === 0)
      return obj;
    if (path.length === 1) {
      if (obj == null)
        return obj;
      if (Number.isInteger(path[0]) && Array.isArray(obj))
        return Array.prototype.slice.call(obj, 0).splice(path[0], 1);
      const result = {};
      for (const p in obj)
        result[p] = obj[p];
      delete result[path[0]];
      return result;
    }
    if (obj[path[0]] == null) {
      if (Number.isInteger(path[0]) && Array.isArray(obj))
        return Array.prototype.concat.call([], obj);
      const result = {};
      for (const p in obj)
        result[p] = obj[p];
      return result;
    }
    return set(
      obj,
      unset(
        obj[path[0]],
        Array.prototype.slice.call(path, 1)
      ),
      [path[0]]
    );
  }
  function deepPickUnsafe(obj, paths) {
    return paths.map((p) => p.split(".")).map((p) => [p, get(obj, p)]).filter((t) => t[1] !== void 0).reduce((acc, cur) => set(acc, cur[1], cur[0]), {});
  }
  function deepOmitUnsafe(obj, paths) {
    return paths.map((p) => p.split(".")).reduce((acc, cur) => unset(acc, cur), obj);
  }
  function hydrateStore(store, {
    storage,
    serializer,
    key,
    debug,
    pick,
    omit,
    beforeHydrate,
    afterHydrate
  }, context, runHooks = true) {
    try {
      if (runHooks)
        beforeHydrate?.(context);
      const fromStorage = storage.getItem(key);
      if (fromStorage) {
        const deserialized = serializer.deserialize(fromStorage);
        const picked = pick ? deepPickUnsafe(deserialized, pick) : deserialized;
        const omitted = omit ? deepOmitUnsafe(picked, omit) : picked;
        store.$patch(omitted);
      }
      if (runHooks)
        afterHydrate?.(context);
    } catch (error) {
      if (debug)
        console.error("[pinia-plugin-persistedstate]", error);
    }
  }
  function persistState(state, {
    storage,
    serializer,
    key,
    debug,
    pick,
    omit
  }) {
    try {
      const picked = pick ? deepPickUnsafe(state, pick) : state;
      const omitted = omit ? deepOmitUnsafe(picked, omit) : picked;
      const toStorage = serializer.serialize(omitted);
      storage.setItem(key, toStorage);
    } catch (error) {
      if (debug)
        console.error("[pinia-plugin-persistedstate]", error);
    }
  }
  function createPersistence(context, optionsParser, auto) {
    const { pinia: pinia2, store, options: { persist = auto } } = context;
    if (!persist)
      return;
    if (!(store.$id in pinia2.state.value)) {
      const originalStore = pinia2._s.get(store.$id.replace("__hot:", ""));
      if (originalStore)
        void Promise.resolve().then(() => originalStore.$persist());
      return;
    }
    const persistenceOptions = Array.isArray(persist) ? persist : persist === true ? [{}] : [persist];
    const persistences = persistenceOptions.map(optionsParser);
    store.$hydrate = ({ runHooks = true } = {}) => {
      persistences.forEach((p) => {
        hydrateStore(store, p, context, runHooks);
      });
    };
    store.$persist = () => {
      persistences.forEach((p) => {
        persistState(store.$state, p);
      });
    };
    persistences.forEach((p) => {
      hydrateStore(store, p, context);
      store.$subscribe(
        (_mutation, state) => persistState(state, p),
        { detached: true }
      );
    });
  }
  function createPersistedState(options = {}) {
    return function(context) {
      createPersistence(
        context,
        (p) => ({
          key: (options.key ? options.key : (x) => x)(p.key ?? context.store.$id),
          debug: p.debug ?? options.debug ?? false,
          serializer: p.serializer ?? options.serializer ?? {
            serialize: (data) => JSON.stringify(data),
            deserialize: (data) => destr(data)
          },
          storage: p.storage ?? options.storage ?? window.localStorage,
          beforeHydrate: p.beforeHydrate,
          afterHydrate: p.afterHydrate,
          pick: p.pick,
          omit: p.omit
        }),
        options.auto ?? false
      );
    };
  }
  var index_default = createPersistedState();
  function computedWithControl(source, fn, options = {}) {
    let v = void 0;
    let track;
    let trigger;
    let dirty = true;
    const update = () => {
      dirty = true;
      trigger();
    };
    vue.watch(source, update, {
      flush: "sync",
      ...options
    });
    const get$1 = typeof fn === "function" ? fn : fn.get;
    const set$1 = typeof fn === "function" ? void 0 : fn.set;
    const result = vue.customRef((_track, _trigger) => {
      track = _track;
      trigger = _trigger;
      return {
        get() {
          if (dirty) {
            v = get$1(v);
            dirty = false;
          }
          track();
          return v;
        },
        set(v$1) {
          set$1 === null || set$1 === void 0 || set$1(v$1);
        }
      };
    });
    result.trigger = update;
    return result;
  }
  function tryOnScopeDispose(fn, failSilently) {
    if (vue.getCurrentScope()) {
      vue.onScopeDispose(fn, failSilently);
      return true;
    }
    return false;
  }
  const isClient = typeof window !== "undefined" && typeof document !== "undefined";
  typeof WorkerGlobalScope !== "undefined" && globalThis instanceof WorkerGlobalScope;
  const notNullish = (val) => val != null;
  const toString = Object.prototype.toString;
  const isObject = (val) => toString.call(val) === "[object Object]";
  const noop = () => {
  };
  function toArray(value) {
    return Array.isArray(value) ? value : [value];
  }
  function getLifeCycleTarget(target) {
    return vue.getCurrentInstance();
  }
  function tryOnMounted(fn, sync = true, target) {
    if (getLifeCycleTarget()) vue.onMounted(fn, target);
    else if (sync) fn();
    else vue.nextTick(fn);
  }
  function tryOnUnmounted(fn, target) {
    if (getLifeCycleTarget()) vue.onUnmounted(fn, target);
  }
  function watchImmediate(source, cb, options) {
    return vue.watch(source, cb, {
      ...options,
      immediate: true
    });
  }
  const defaultWindow = isClient ? window : void 0;
  const defaultDocument = isClient ? window.document : void 0;
  function unrefElement(elRef) {
    var _$el;
    const plain = vue.toValue(elRef);
    return (_$el = plain === null || plain === void 0 ? void 0 : plain.$el) !== null && _$el !== void 0 ? _$el : plain;
  }
  function useEventListener(...args) {
    const cleanups = [];
    const cleanup = () => {
      cleanups.forEach((fn) => fn());
      cleanups.length = 0;
    };
    const register = (el, event, listener, options) => {
      el.addEventListener(event, listener, options);
      return () => el.removeEventListener(event, listener, options);
    };
    const firstParamTargets = vue.computed(() => {
      const test = toArray(vue.toValue(args[0])).filter((e) => e != null);
      return test.every((e) => typeof e !== "string") ? test : void 0;
    });
    const stopWatch = watchImmediate(() => {
      var _firstParamTargets$va, _firstParamTargets$va2;
      return [
        (_firstParamTargets$va = (_firstParamTargets$va2 = firstParamTargets.value) === null || _firstParamTargets$va2 === void 0 ? void 0 : _firstParamTargets$va2.map((e) => unrefElement(e))) !== null && _firstParamTargets$va !== void 0 ? _firstParamTargets$va : [defaultWindow].filter((e) => e != null),
        toArray(vue.toValue(firstParamTargets.value ? args[1] : args[0])),
        toArray(vue.unref(firstParamTargets.value ? args[2] : args[1])),
        vue.toValue(firstParamTargets.value ? args[3] : args[2])
      ];
    }, ([raw_targets, raw_events, raw_listeners, raw_options]) => {
      cleanup();
      if (!(raw_targets === null || raw_targets === void 0 ? void 0 : raw_targets.length) || !(raw_events === null || raw_events === void 0 ? void 0 : raw_events.length) || !(raw_listeners === null || raw_listeners === void 0 ? void 0 : raw_listeners.length)) return;
      const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;
      cleanups.push(...raw_targets.flatMap((el) => raw_events.flatMap((event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone)))));
    }, { flush: "post" });
    const stop = () => {
      stopWatch();
      cleanup();
    };
    tryOnScopeDispose(cleanup);
    return stop;
  }
  function onClickOutside(target, handler, options = {}) {
    const { window: window$1 = defaultWindow, ignore = [], capture = true, detectIframe = false, controls = false } = options;
    if (!window$1) return controls ? {
      stop: noop,
      cancel: noop,
      trigger: noop
    } : noop;
    let shouldListen = true;
    const shouldIgnore = (event) => {
      return vue.toValue(ignore).some((target$1) => {
        if (typeof target$1 === "string") return Array.from(window$1.document.querySelectorAll(target$1)).some((el) => el === event.target || event.composedPath().includes(el));
        else {
          const el = unrefElement(target$1);
          return el && (event.target === el || event.composedPath().includes(el));
        }
      });
    };
    function hasMultipleRoots(target$1) {
      const vm = vue.toValue(target$1);
      return vm && vm.$.subTree.shapeFlag === 16;
    }
    function checkMultipleRoots(target$1, event) {
      const vm = vue.toValue(target$1);
      const children = vm.$.subTree && vm.$.subTree.children;
      if (children == null || !Array.isArray(children)) return false;
      return children.some((child) => child.el === event.target || event.composedPath().includes(child.el));
    }
    const listener = (event) => {
      const el = unrefElement(target);
      if (event.target == null) return;
      if (!(el instanceof Element) && hasMultipleRoots(target) && checkMultipleRoots(target, event)) return;
      if (!el || el === event.target || event.composedPath().includes(el)) return;
      if ("detail" in event && event.detail === 0) shouldListen = !shouldIgnore(event);
      if (!shouldListen) {
        shouldListen = true;
        return;
      }
      handler(event);
    };
    let isProcessingClick = false;
    const cleanup = [
      useEventListener(window$1, "click", (event) => {
        if (!isProcessingClick) {
          isProcessingClick = true;
          setTimeout(() => {
            isProcessingClick = false;
          }, 0);
          listener(event);
        }
      }, {
        passive: true,
        capture
      }),
      useEventListener(window$1, "pointerdown", (e) => {
        const el = unrefElement(target);
        shouldListen = !shouldIgnore(e) && !!(el && !e.composedPath().includes(el));
      }, { passive: true }),
      detectIframe && useEventListener(window$1, "blur", (event) => {
        setTimeout(() => {
          var _window$document$acti;
          const el = unrefElement(target);
          if (((_window$document$acti = window$1.document.activeElement) === null || _window$document$acti === void 0 ? void 0 : _window$document$acti.tagName) === "IFRAME" && !(el === null || el === void 0 ? void 0 : el.contains(window$1.document.activeElement))) handler(event);
        }, 0);
      }, { passive: true })
    ].filter(Boolean);
    const stop = () => cleanup.forEach((fn) => fn());
    if (controls) return {
      stop,
      cancel: () => {
        shouldListen = false;
      },
      trigger: (event) => {
        shouldListen = true;
        listener(event);
        shouldListen = false;
      }
    };
    return stop;
  }
function useMounted() {
    const isMounted = vue.shallowRef(false);
    const instance = vue.getCurrentInstance();
    if (instance) vue.onMounted(() => {
      isMounted.value = true;
    }, instance);
    return isMounted;
  }
function useSupported(callback) {
    const isMounted = useMounted();
    return vue.computed(() => {
      isMounted.value;
      return Boolean(callback());
    });
  }
  function useMutationObserver(target, callback, options = {}) {
    const { window: window$1 = defaultWindow, ...mutationOptions } = options;
    let observer;
    const isSupported = useSupported(() => window$1 && "MutationObserver" in window$1);
    const cleanup = () => {
      if (observer) {
        observer.disconnect();
        observer = void 0;
      }
    };
    const stopWatch = vue.watch(vue.computed(() => {
      const items = toArray(vue.toValue(target)).map(unrefElement).filter(notNullish);
      return new Set(items);
    }), (newTargets) => {
      cleanup();
      if (isSupported.value && newTargets.size) {
        observer = new MutationObserver(callback);
        newTargets.forEach((el) => observer.observe(el, mutationOptions));
      }
    }, {
      immediate: true,
      flush: "post"
    });
    const takeRecords = () => {
      return observer === null || observer === void 0 ? void 0 : observer.takeRecords();
    };
    const stop = () => {
      stopWatch();
      cleanup();
    };
    tryOnScopeDispose(stop);
    return {
      isSupported,
      stop,
      takeRecords
    };
  }
  function onElementRemoval(target, callback, options = {}) {
    const { window: window$1 = defaultWindow, document: document$1 = window$1 === null || window$1 === void 0 ? void 0 : window$1.document, flush = "sync" } = options;
    if (!window$1 || !document$1) return noop;
    let stopFn;
    const cleanupAndUpdate = (fn) => {
      stopFn === null || stopFn === void 0 || stopFn();
      stopFn = fn;
    };
    const stopWatch = vue.watchEffect(() => {
      const el = unrefElement(target);
      if (el) {
        const { stop } = useMutationObserver(document$1, (mutationsList) => {
          if (mutationsList.map((mutation) => [...mutation.removedNodes]).flat().some((node) => node === el || node.contains(el))) callback(mutationsList);
        }, {
          window: window$1,
          childList: true,
          subtree: true
        });
        cleanupAndUpdate(stop);
      }
    }, { flush });
    const stopHandle = () => {
      stopWatch();
      cleanupAndUpdate();
    };
    tryOnScopeDispose(stopHandle);
    return stopHandle;
  }
  function createKeyPredicate(keyFilter) {
    if (typeof keyFilter === "function") return keyFilter;
    else if (typeof keyFilter === "string") return (event) => event.key === keyFilter;
    else if (Array.isArray(keyFilter)) return (event) => keyFilter.includes(event.key);
    return () => true;
  }
  function onKeyStroke(...args) {
    let key;
    let handler;
    let options = {};
    if (args.length === 3) {
      key = args[0];
      handler = args[1];
      options = args[2];
    } else if (args.length === 2) if (typeof args[1] === "object") {
      key = true;
      handler = args[0];
      options = args[1];
    } else {
      key = args[0];
      handler = args[1];
    }
    else {
      key = true;
      handler = args[0];
    }
    const { target = defaultWindow, eventName = "keydown", passive = false, dedupe = false } = options;
    const predicate = createKeyPredicate(key);
    const listener = (e) => {
      if (e.repeat && vue.toValue(dedupe)) return;
      if (predicate(e)) handler(e);
    };
    return useEventListener(target, eventName, listener, passive);
  }
function useActiveElement(options = {}) {
    var _options$document;
    const { window: window$1 = defaultWindow, deep = true, triggerOnRemoval = false } = options;
    const document$1 = (_options$document = options.document) !== null && _options$document !== void 0 ? _options$document : window$1 === null || window$1 === void 0 ? void 0 : window$1.document;
    const getDeepActiveElement = () => {
      let element = document$1 === null || document$1 === void 0 ? void 0 : document$1.activeElement;
      if (deep) {
        var _element$shadowRoot;
        while (element === null || element === void 0 ? void 0 : element.shadowRoot) element = element === null || element === void 0 || (_element$shadowRoot = element.shadowRoot) === null || _element$shadowRoot === void 0 ? void 0 : _element$shadowRoot.activeElement;
      }
      return element;
    };
    const activeElement = vue.shallowRef();
    const trigger = () => {
      activeElement.value = getDeepActiveElement();
    };
    if (window$1) {
      const listenerOptions = {
        capture: true,
        passive: true
      };
      useEventListener(window$1, "blur", (event) => {
        if (event.relatedTarget !== null) return;
        trigger();
      }, listenerOptions);
      useEventListener(window$1, "focus", trigger, listenerOptions);
    }
    if (triggerOnRemoval) onElementRemoval(activeElement, trigger, { document: document$1 });
    trigger();
    return activeElement;
  }
  function useCssVar(prop, target, options = {}) {
    const { window: window$1 = defaultWindow, initialValue, observe = false } = options;
    const variable = vue.shallowRef(initialValue);
    const elRef = vue.computed(() => {
      var _window$document;
      return unrefElement(target) || (window$1 === null || window$1 === void 0 || (_window$document = window$1.document) === null || _window$document === void 0 ? void 0 : _window$document.documentElement);
    });
    function updateCssVar() {
      const key = vue.toValue(prop);
      const el = vue.toValue(elRef);
      if (el && window$1 && key) {
        var _window$getComputedSt;
        variable.value = ((_window$getComputedSt = window$1.getComputedStyle(el).getPropertyValue(key)) === null || _window$getComputedSt === void 0 ? void 0 : _window$getComputedSt.trim()) || variable.value || initialValue;
      }
    }
    if (observe) useMutationObserver(elRef, updateCssVar, {
      attributeFilter: ["style", "class"],
      window: window$1
    });
    vue.watch([elRef, () => vue.toValue(prop)], (_, old) => {
      if (old[0] && old[1]) old[0].style.removeProperty(old[1]);
      updateCssVar();
    }, { immediate: true });
    vue.watch([variable, elRef], ([val, el]) => {
      const raw_prop = vue.toValue(prop);
      if ((el === null || el === void 0 ? void 0 : el.style) && raw_prop) if (val == null) el.style.removeProperty(raw_prop);
      else el.style.setProperty(raw_prop, val);
    }, { immediate: true });
    return variable;
  }
  function useCurrentElement(rootComponent) {
    const vm = vue.getCurrentInstance();
    const currentElement = computedWithControl(() => null, () => vm.proxy.$el);
    vue.onUpdated(currentElement.trigger);
    vue.onMounted(currentElement.trigger);
    return currentElement;
  }
  function useParentElement(element = useCurrentElement()) {
    const parentElement = vue.shallowRef();
    const update = () => {
      const el = unrefElement(element);
      if (el) parentElement.value = el.parentElement;
    };
    tryOnMounted(update);
    vue.watch(() => vue.toValue(element), update);
    return parentElement;
  }
  function useScriptTag(src, onLoaded = noop, options = {}) {
    const { immediate = true, manual = false, type = "text/javascript", async = true, crossOrigin, referrerPolicy, noModule, defer, document: document$1 = defaultDocument, attrs = {}, nonce = void 0 } = options;
    const scriptTag = vue.shallowRef(null);
    let _promise = null;
    const loadScript = (waitForScriptLoad) => new Promise((resolve, reject) => {
      const resolveWithElement = (el$1) => {
        scriptTag.value = el$1;
        resolve(el$1);
        return el$1;
      };
      if (!document$1) {
        resolve(false);
        return;
      }
      let shouldAppend = false;
      let el = document$1.querySelector(`script[src="${vue.toValue(src)}"]`);
      if (!el) {
        el = document$1.createElement("script");
        el.type = type;
        el.async = async;
        el.src = vue.toValue(src);
        if (defer) el.defer = defer;
        if (crossOrigin) el.crossOrigin = crossOrigin;
        if (noModule) el.noModule = noModule;
        if (referrerPolicy) el.referrerPolicy = referrerPolicy;
        if (nonce) el.nonce = nonce;
        Object.entries(attrs).forEach(([name, value]) => el === null || el === void 0 ? void 0 : el.setAttribute(name, value));
        shouldAppend = true;
      } else if (el.hasAttribute("data-loaded")) resolveWithElement(el);
      const listenerOptions = { passive: true };
      useEventListener(el, "error", (event) => reject(event), listenerOptions);
      useEventListener(el, "abort", (event) => reject(event), listenerOptions);
      useEventListener(el, "load", () => {
        el.setAttribute("data-loaded", "true");
        onLoaded(el);
        resolveWithElement(el);
      }, listenerOptions);
      if (shouldAppend) el = document$1.head.appendChild(el);
      if (!waitForScriptLoad) resolveWithElement(el);
    });
    const load = (waitForScriptLoad = true) => {
      if (!_promise) _promise = loadScript(waitForScriptLoad);
      return _promise;
    };
    const unload = () => {
      if (!document$1) return;
      _promise = null;
      if (scriptTag.value) scriptTag.value = null;
      const el = document$1.querySelector(`script[src="${vue.toValue(src)}"]`);
      if (el) document$1.head.removeChild(el);
    };
    if (immediate && !manual) tryOnMounted(load);
    if (!manual) tryOnUnmounted(unload);
    return {
      scriptTag,
      load,
      unload
    };
  }
  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  const _hoisted_1$i = ["active"];
  const _sfc_main$j = {
    __name: "BootstrapIcon",
    props: {
      icon: {
        type: String,
        required: true
      },
      color: {
        type: String,
        default: "whitesmoke"
      },
      size: {
        type: String,
        default: "1rem"
      },
      stroke: {
        type: String,
        default: "0"
      },
      active: {
        type: Boolean,
        default: false
      }
    },
    setup(__props) {
      vue.useCssVars((_ctx) => ({
        "c22eb872": __props.color,
        "v70824f1d": __props.size,
        "v59c28e54": __props.stroke
      }));
      const props = __props;
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("i", {
          class: vue.normalizeClass(`bi bi-${props.icon}`),
          active: props.active
        }, null, 10, _hoisted_1$i);
      };
    }
  };
  const BootstrapIcon = _export_sfc(_sfc_main$j, [["__scopeId", "data-v-a9f332df"]]);
  var _GM_getValue = (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
  var _GM_setValue = (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
  var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  class ChordSheetDocument {
    constructor() {
      this.el = {
        mtitle: document.getElementById("mtitle"),
        tkinfo: document.querySelector(".tkinfo"),
        capoSelect: document.querySelector(".capo .select"),
        tinfo: document.querySelector(".tinfo"),
        tone_z: document.getElementById("tone_z")
      };
    }
    getId() {
      const urlParams = new URLSearchParams(window.location.search);
      return Number(urlParams.get("id"));
    }
    getTitle() {
      return this.el.mtitle.textContent.trim();
    }
    getKey() {
      const match = this.el.tkinfo?.textContent.match(new RegExp("(?<=原調:)\\w*"));
      return match ? match[0].trim() : "";
    }
    getPlay() {
      const match = this.el.capoSelect?.textContent.split(/\s*\/\s*/);
      return match ? match[1].trim() : "";
    }
    getCapo() {
      const match = this.el.capoSelect?.textContent.split(/\s*\/\s*/);
      return match ? Number(match[0]) : 0;
    }
    getSinger() {
      const match = this.el.tinfo?.textContent.match(new RegExp("(?<=演唱:).*(?=\\n|$)"));
      return match ? match[0].trim() : "";
    }
    getComposer() {
      const match = this.el.tinfo?.textContent.match(new RegExp("(?<=曲:).*?(?=詞:|$)"));
      return match ? match[0].trim() : "";
    }
    getLyricist() {
      const match = this.el.tinfo?.textContent.match(new RegExp("(?<=詞:).*?(?=曲:|$)"));
      return match ? match[0].trim() : "";
    }
    getBpm() {
      const match = this.el.tkinfo?.textContent.match(/\d+/);
      return match ? Number(match[0]) : 0;
    }
    getSheetText() {
      const formattedChordSheet = this.el.tone_z.textContent.replaceAll(/\s+?\n/g, "\n").replaceAll("\n\n", "\n").trim().replaceAll(/\s+/g, (match) => {
        return `{%${match.length}%}`;
      });
      return formattedChordSheet;
    }
  }
  class Chord {
    static sharps = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
    static flats = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
constructor(chordString) {
      this.chordString = chordString;
    }
transpose(delta) {
      this.chordString = this.chordString.replaceAll(/[A-G][#b]?/g, (note) => {
        const isSharp = Chord.sharps.includes(note);
        const scale = isSharp ? Chord.sharps : Chord.flats;
        const noteIndex = scale.indexOf(note);
        const transposedIndex = (noteIndex + delta + 12) % 12;
        const transposedNote = scale[transposedIndex];
        return transposedNote;
      });
      return this;
    }
switchModifier() {
      this.chordString = this.chordString.replaceAll(/[A-G][#b]/g, (note) => {
        const scale = note.includes("#") ? Chord.sharps : Chord.flats;
        const newScale = note.includes("#") ? Chord.flats : Chord.sharps;
        const noteIndex = scale.indexOf(note);
        return newScale[noteIndex];
      });
      return this;
    }
useSharpModifier() {
      this.chordString = this.chordString.replaceAll(/[A-G]b/g, (note) => {
        const noteIndex = Chord.flats.indexOf(note);
        return Chord.sharps[noteIndex];
      });
      return this;
    }
useFlatModifier() {
      this.chordString = this.chordString.replaceAll(/[A-G]#/g, (note) => {
        const noteIndex = Chord.sharps.indexOf(note);
        return Chord.flats[noteIndex];
      });
      return this;
    }
toString() {
      return this.chordString;
    }
toFormattedString() {
      return this.chordString.replaceAll(
        /[#b]/g,
`<sup>$&</sup>`
      );
    }
  }
  class ChordSheetElement {
constructor(chordSheetElement) {
      this.chordSheetElement = chordSheetElement;
    }
static transposeSheet(delta) {
      $("#tone_z .tf").each(function() {
        const chord = new Chord($(this).text());
        const newChordHTML = chord.transpose(-delta).toFormattedString();
        $(this).html(newChordHTML);
      });
    }
formatUnderlines() {
      const underlineEl = this.chordSheetElement.querySelectorAll("u");
      const doubleUnderlineEl = this.chordSheetElement.querySelectorAll("abbr");
      underlineEl.forEach((el) => {
        el.textContent = `{_${el.textContent}_}`;
      });
      doubleUnderlineEl.forEach((el) => {
        el.textContent = `{=${el.textContent}=}`;
      });
      return this;
    }
#unformat(nodeList) {
      nodeList.forEach((el) => {
        el.innerHTML = el.textContent.replaceAll(/\{_|\{=|=\}|_\}/g, "").replaceAll(
          /[a-z0-9#/]+/gi,
`<span class="tf">$&</span>`
        );
      });
    }
unformatUnderlines() {
      const underlineEl = this.chordSheetElement.querySelectorAll("u");
      const doubleUnderlineEl = this.chordSheetElement.querySelectorAll("abbr");
      this.#unformat(underlineEl);
      this.#unformat(doubleUnderlineEl);
      return this;
    }
  }
  function redirect() {
    const currentUrl = window.location.href;
    if (/\/song\//.test(currentUrl)) {
      const sheetId = currentUrl.match(new RegExp("(?<=\\/)\\d+(?=\\.)"))[0];
      const newUrl = `https://www.91pu.com.tw/m/tone.shtml?id=${sheetId}`;
      window.location.replace(newUrl);
    }
  }
  function getQueryParams() {
    const url = new URL(window.location.href);
    const params = {
      transpose: +url.searchParams.get("transpose"),
      darkMode: !!url.searchParams.get("darkmode")
    };
    return params;
  }
  function changeTitle() {
    const newTitle = $("#mtitle").text().trim();
    document.title = `${newTitle} | 91+`;
  }
  function archiveChordSheet() {
    const sheet = document.getElementById("tone_z");
    const chordSheetDocument = new ChordSheetDocument();
    try {
      const chordSheetElement = new ChordSheetElement(sheet);
      chordSheetElement.formatUnderlines();
      const formBody = {
        id: chordSheetDocument.getId(),
        title: chordSheetDocument.getTitle(),
        key: chordSheetDocument.getKey(),
        play: chordSheetDocument.getPlay(),
        capo: chordSheetDocument.getCapo(),
        singer: chordSheetDocument.getSinger(),
        composer: chordSheetDocument.getComposer(),
        lyricist: chordSheetDocument.getLyricist(),
        bpm: chordSheetDocument.getBpm(),
        sheet_text: chordSheetDocument.getSheetText()
      };
      chordSheetElement.unformatUnderlines();
      fetch("https://91-plus-plus-api.fly.dev/archive", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(formBody)
      }).then((response) => {
        console.log("[91 Plus] 雲端樂譜備份成功:", response);
      }).catch((error) => {
        console.error("[91 Plus] 雲端樂譜備份失敗:", error);
      });
    } catch {
      console.warn("[91 Plus] 樂譜解析失敗,無法備份");
      fetch(
        `https://91-plus-plus-api.fly.dev/report?id=${chordSheetDocument.getId()}`
      );
    }
  }
  function onSheetDomReady(callback) {
    return new MutationObserver((_records, observer) => {
      const isMutationDone = !!document.querySelector("#tone_z").childElementCount;
      if (isMutationDone) {
        observer.disconnect();
        callback();
      }
    }).observe(document.body, { childList: true, subtree: true });
  }
  function switchInstrument(instrument) {
    switch (instrument) {
      case "guitar": {
        $(".schord").trigger("click");
        break;
      }
      case "ukulele": {
        $(".ukschord").trigger("click");
        break;
      }
      default: {
        $(".nsChord").trigger("click");
        break;
      }
    }
  }
  function getChordShapes() {
    const thisWindow = _unsafeWindow ?? window;
    const chordShapes = thisWindow?.chord_shapes ?? {};
    return chordShapes;
  }
  function getChordList() {
    const chordList = [];
    $("#tone_z .tf").each(function() {
      const chordName = $(this).text().trim();
      if (chordName) {
        chordList.push(chordName);
      }
    });
    return [...new Set(chordList)];
  }
  function convertChordName(chordName) {
    const root = chordName.match(/^[A-G]#?/)[0];
    const rest = chordName.replace(/^[A-G]#?/, "");
    return `${rest} ${root}`;
  }
  const _hoisted_1$h = {
    key: 0,
    class: "chord-container"
  };
  const _hoisted_2$b = { class: "chord-name" };
  const _hoisted_3$6 = ["chord-name"];
  const _sfc_main$i = {
    __name: "ChordChart",
    props: {
      chord: String
    },
    setup(__props) {
      const props = __props;
      const chordRef = vue.useTemplateRef("chord");
      const chordShapes = getChordShapes();
      const isChordExist = vue.ref(true);
      vue.onMounted(() => {
        const formattedChordKey = convertChordName(props.chord);
        const chordShape = chordShapes[formattedChordKey];
        if (!chordShape) {
          return isChordExist.value = false;
        }
        const chordObject = {
          ...chordShape,

barres: chordShape.bars?.map((barre) => {
            return {
              ...barre,
              fromString: barre.from_string,
              toString: barre.to_string
            };
          }),
          chord: chordShape.chord.map(([stringNum, fretNum]) => {
            const raw = [stringNum, fretNum];
            if (Number.isNaN(+fretNum)) {
              return raw;
            }
            let newFretNum = fretNum;
            newFretNum += chordShape.position || 0;
            newFretNum -= chordShape.position_text || 0;
            return [stringNum, newFretNum];
          })
        };
        vue.nextTick(() => {
          const width = chordRef.value.clientWidth;
          const chordBoxSelector = `.chord-chart[chord-name="${props.chord}"]`;
          const chordBox = new vexchords.ChordBox(chordBoxSelector, {
            width,
            height: width * 1.25,
            circleRadius: 5,
            numStrings: 6,
            numFrets: 5,
            showTuning: false,
            defaultColor: "#444",
            bgColor: "transparent"
          });
          chordBox.draw(chordObject);
        });
      });
      return (_ctx, _cache) => {
        return isChordExist.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$h, [
          vue.createElementVNode("div", _hoisted_2$b, vue.toDisplayString(props.chord), 1),
          vue.createElementVNode("div", {
            ref: "chord",
            class: "chord-chart",
            "chord-name": props.chord
          }, null, 8, _hoisted_3$6)
        ])) : vue.createCommentVNode("", true);
      };
    }
  };
  const ChordChart = _export_sfc(_sfc_main$i, [["__scopeId", "data-v-128703be"]]);
  const _hoisted_1$g = { class: "plus91-popup" };
  const _sfc_main$h = {
    __name: "PopupBase",
    props: {
      "modelValue": {},
      "modelModifiers": {}
    },
    emits: ["update:modelValue"],
    setup(__props) {
      const modelValue = vue.useModel(__props, "modelValue");
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
          default: vue.withCtx(() => [
            vue.withDirectives(vue.createElementVNode("div", _hoisted_1$g, [
              vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
            ], 512), [
              [vue.vShow, modelValue.value]
            ])
          ]),
          _: 3
        });
      };
    }
  };
  const PopupBase = _export_sfc(_sfc_main$h, [["__scopeId", "data-v-ed81246a"]]);
  class MonkeyStorage {
static getStorageType() {
      if (_GM_getValue && _GM_setValue) {
        return "Greasemonkey";
      } else {
        return "LocalStorage";
      }
    }
static getItem(key) {
      const storageType = this.getStorageType();
      switch (storageType) {
        case "Greasemonkey":
          return _GM_getValue(key, null);
        case "LocalStorage":
          return localStorage.getItem(key);
        default:
          return null;
      }
    }
static setItem(key, value) {
      const storageType = this.getStorageType();
      switch (storageType) {
        case "Greasemonkey":
          _GM_setValue(key, value);
          break;
        case "LocalStorage":
          localStorage.setItem(key, value);
          break;
      }
    }
  }
  const useStore = defineStore("store", {
    state() {
      return {


isDarkMode: false,
        isToolbarsShow: false,
        isPopupShow: {
          sheet: false,
          chord: false,
          font: false,
          settings: false,
          menu: false,
hotkey: false
        },


agreeToArchiveSheet: true,
        isDevMode: false,
        themeColor: "#4b96a9",


transpose: 0,
originalCapo: 0,
originalKey: "",
fontSizeDelta: 0,
originalFontSize: 0,
originalLineHeight: 0
      };
    },
    persist: {
      key: "plus91-preferences",
      storage: MonkeyStorage,
      deserialize: zipson.parse,
      serialize: zipson.stringify,
      pick: ["isDarkMode", "agreeToArchiveSheet", "isDevMode", "themeColor"],
      beforeHydrate() {
        console.log("[91Plus] 讀取偏好設置中");
      },
      afterHydrate() {
        console.log("[91Plus] 偏好設置讀取完畢");
      },
      debug: true
    },
    getters: {
      currentCapo() {
        return this.originalCapo + this.transpose;
      },
      currentKey() {
        return new Chord(this.originalKey).transpose(-this.transpose).toFormattedString();
      }
    },
    actions: {
      toggleToolbars() {
        if (this.isToolbarsShow) {
          this.closePopups();
        } else {
          this.isPopupShow.sheet = true;
        }
        this.isToolbarsShow = !this.isToolbarsShow;
      },
      closePopups() {
        for (const popup in this.isPopupShow) {
          this.isPopupShow[popup] = false;
        }
      },
togglePopup(name) {
        for (const popup in this.isPopupShow) {
          if (popup === name) {
            this.isPopupShow[popup] = !this.isPopupShow[popup];
          } else {
            this.isPopupShow[popup] = false;
          }
        }
      },
      plusTranspose(numberToPlus) {
        let newTranspose = this.transpose + numberToPlus;
        const newCapo = this.originalCapo + newTranspose;
        if (newCapo === 12 || newCapo === -12) {
          newTranspose = -this.originalCapo;
        }
        this.transpose = newTranspose;
      }
    }
  });
  const _hoisted_1$f = { class: "banner" };
  const _hoisted_2$a = { class: "chord-popup-container" };
  const _sfc_main$g = {
    __name: "ChordPopup",
    setup(__props) {
      const store = useStore();
      const bannerText = vue.ref("");
      const bannerTextList = [
        "此處的和弦圖示僅供參考!由於技術問題,目前尚無法準確繪製,尤其在把位較常出現錯誤,請注意。",
        "在 91 譜中沒有資料的和弦是畫不出來的呦!"
      ];
      function refreshBanner() {
        const randomIndex = Math.floor(Math.random() * bannerTextList.length);
        bannerText.value = bannerTextList[randomIndex];
      }
      const chordList = vue.ref([]);
      vue.watch(store.isPopupShow, () => {
        if (!store.isPopupShow.chord) {
          return;
        }
        refreshBanner();
        chordList.value = getChordList();
      });
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(PopupBase, {
          id: "plus91-chord-popup",
          modelValue: vue.unref(store).isPopupShow.chord,
          "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.unref(store).isPopupShow.chord = $event),
          class: vue.normalizeClass({ "banner-only": !chordList.value.length })
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", _hoisted_1$f, [
              vue.createVNode(BootstrapIcon, {
                icon: "info-circle-fill",
                color: "inherit",
                size: "inherit"
              }),
              vue.createElementVNode("section", null, vue.toDisplayString(bannerText.value), 1)
            ]),
            vue.createElementVNode("div", _hoisted_2$a, [
              (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(chordList.value, (chord) => {
                return vue.openBlock(), vue.createBlock(ChordChart, {
                  key: chord,
                  chord
                }, null, 8, ["chord"]);
              }), 128))
            ])
          ]),
          _: 1
        }, 8, ["modelValue", "class"]);
      };
    }
  };
  const ChordPopup = _export_sfc(_sfc_main$g, [["__scopeId", "data-v-858a1989"]]);
  const _hoisted_1$e = { class: "toolbar-icon" };
  const _hoisted_2$9 = {
    key: 0,
    class: "toolbar-icon-text"
  };
  const _sfc_main$f = {
    __name: "ToolbarIcon",
    props: {
      icon: {
        type: String,
        required: true
      },
      text: {
        type: String,
        default: ""
      },
      stroke: {
        type: String,
        default: "0"
      },
      active: {
        type: Boolean,
        default: false
      },
      color: {
        type: String,
        default: "whitesmoke"
      }
    },
    setup(__props) {
      vue.useCssVars((_ctx) => ({
        "v08ff4922": __props.color
      }));
      const props = __props;
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$e, [
          vue.createVNode(BootstrapIcon, {
            size: "1.3rem",
            icon: props.icon,
            color: props.color,
            stroke: props.stroke,
            active: props.active
          }, null, 8, ["icon", "color", "stroke", "active"]),
          props.text ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$9, vue.toDisplayString(props.text), 1)) : vue.createCommentVNode("", true)
        ]);
      };
    }
  };
  const ToolbarIcon = _export_sfc(_sfc_main$f, [["__scopeId", "data-v-cbf0cf0b"]]);
  const _hoisted_1$d = { class: "adjust-widget" };
  const _hoisted_2$8 = ["disabled"];
  const _hoisted_3$5 = ["disabled"];
  const _hoisted_4$2 = ["disabled"];
  const _sfc_main$e = {
    __name: "AdjustWidget",
    props: {
      iconLeft: {
        type: String,
        default: "caret-left-fill"
      },
      iconRight: {
        type: String,
        default: "caret-right-fill"
      },
      disabledLeft: {
        type: Boolean,
        default: false
      },
      disabledMiddle: {
        type: Boolean,
        default: false
      },
      disabledRight: {
        type: Boolean,
        default: false
      },
      color: {
        type: String,
        default: "#444"
      },
      size: {
        type: String,
        default: "1.25rem"
      },
      onclickLeft: Function,
      onclickMiddle: Function,
      onclickRight: Function
    },
    setup(__props) {
      vue.useCssVars((_ctx) => ({
        "v5e7bf01c": __props.color,
        "v1bd9a428": __props.size
      }));
      const props = __props;
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$d, [
          vue.createElementVNode("button", {
            class: "adjust-button adjust-button-left",
            disabled: props.disabledLeft,
            onClick: _cache[0] || (_cache[0] = (...args) => props.onclickLeft && props.onclickLeft(...args))
          }, [
            vue.createVNode(BootstrapIcon, {
              icon: props.iconLeft,
              color: props.color,
              size: props.size
            }, null, 8, ["icon", "color", "size"])
          ], 8, _hoisted_2$8),
          vue.createElementVNode("button", {
            class: "adjust-button adjust-button-middle",
            disabled: props.disabledMiddle,
            onClick: _cache[1] || (_cache[1] = (...args) => props.onclickMiddle && props.onclickMiddle(...args))
          }, [
            vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
          ], 8, _hoisted_3$5),
          vue.createElementVNode("button", {
            class: "adjust-button adjust-button-right",
            disabled: props.disabledRight,
            onClick: _cache[2] || (_cache[2] = (...args) => props.onclickRight && props.onclickRight(...args))
          }, [
            vue.createVNode(BootstrapIcon, {
              icon: props.iconRight,
              color: props.color,
              size: props.size
            }, null, 8, ["icon", "color", "size"])
          ], 8, _hoisted_4$2)
        ]);
      };
    }
  };
  const AdjustWidget = _export_sfc(_sfc_main$e, [["__scopeId", "data-v-0178875e"]]);
  const _hoisted_1$c = { class: "font-popup-container" };
  const _sfc_main$d = {
    __name: "FontSizePopup",
    setup(__props) {
      const store = useStore();
      const getFontSize = vue.computed(() => store.originalFontSize + store.fontSizeDelta);
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(PopupBase, {
          id: "plus91-font-popup",
          modelValue: vue.unref(store).isPopupShow.font,
          "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.unref(store).isPopupShow.font = $event)
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", _hoisted_1$c, [
              vue.createVNode(AdjustWidget, {
                "onclick-left": () => {
                  vue.unref(store).fontSizeDelta--;
                },
                "onclick-middle": () => {
                  vue.unref(store).fontSizeDelta = 0;
                },
                "onclick-right": () => {
                  vue.unref(store).fontSizeDelta++;
                },
                "disabled-left": getFontSize.value <= 8,
                "disabled-right": getFontSize.value >= 30
              }, {
                default: vue.withCtx(() => [
                  vue.createTextVNode(vue.toDisplayString(getFontSize.value) + "px ", 1)
                ]),
                _: 1
              }, 8, ["onclick-left", "onclick-middle", "onclick-right", "disabled-left", "disabled-right"])
            ])
          ]),
          _: 1
        }, 8, ["modelValue"]);
      };
    }
  };
  const hotkeysLeft = [{ "hotkey": "空白鍵", "desc": "開啟 / 關閉功能選單" }, { "hotkey": "ESC", "desc": "關閉功能選單" }, { "hotkey": "/", "desc": "切換至搜尋框" }];
  const hotkeysRight = [{ "hotkey": "", "desc": "移調選單開啟時" }, { "hotkey": "← →", "desc": "移調" }, { "hotkey": "↓", "desc": "移回初始調" }, { "hotkey": "", "desc": "在搜尋框內" }, { "hotkey": "Enter", "desc": "搜尋" }, { "hotkey": "ESC", "desc": "跳出搜尋框" }];
  const hotkeyData = {
    hotkeysLeft,
    hotkeysRight
  };
  const _hoisted_1$b = { class: "hotkey-item" };
  const _hoisted_2$7 = {
    key: 0,
    class: "hotkeys"
  };
  const _hoisted_3$4 = {
    key: 1,
    class: "hr"
  };
  const _sfc_main$c = {
    __name: "HotkeyItem",
    props: {
      hotkey: {
        type: String,
        required: false
      },
      desc: String
    },
    setup(__props) {
      const props = __props;
      const hotkeyList = props.hotkey.split(" ");
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$b, [
          vue.createElementVNode("div", {
            class: vue.normalizeClass(["desc", { title: !__props.hotkey }])
          }, vue.toDisplayString(__props.desc), 3),
          __props.hotkey ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$7, [
            (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyList), (key) => {
              return vue.openBlock(), vue.createElementBlock("kbd", {
                key: `${key}_${__props.hotkey}_${__props.desc}`
              }, vue.toDisplayString(key), 1);
            }), 128))
          ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$4))
        ]);
      };
    }
  };
  const HotkeyItem = _export_sfc(_sfc_main$c, [["__scopeId", "data-v-851f225f"]]);
  const _hoisted_1$a = { class: "hotkey-popup-container" };
  const _hoisted_2$6 = { class: "left-part" };
  const _hoisted_3$3 = { class: "right-part" };
  const _sfc_main$b = {
    __name: "HotkeyPopup",
    setup(__props) {
      const store = useStore();
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(PopupBase, {
          id: "plus91-hotkey-popup",
          modelValue: vue.unref(store).isPopupShow.hotkey,
          "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.unref(store).isPopupShow.hotkey = $event)
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", _hoisted_1$a, [
              vue.createElementVNode("section", _hoisted_2$6, [
                (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyData).hotkeysLeft, (item, index) => {
                  return vue.openBlock(), vue.createBlock(HotkeyItem, {
                    key: `${item.hotkey}_${item.desc}_${index}`,
                    hotkey: item.hotkey,
                    desc: item.desc
                  }, null, 8, ["hotkey", "desc"]);
                }), 128))
              ]),
              vue.createElementVNode("section", _hoisted_3$3, [
                (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyData).hotkeysRight, (item, index) => {
                  return vue.openBlock(), vue.createBlock(HotkeyItem, {
                    key: `${item.hotkey}_${item.desc}_${index}`,
                    hotkey: item.hotkey,
                    desc: item.desc
                  }, null, 8, ["hotkey", "desc"]);
                }), 128))
              ])
            ])
          ]),
          _: 1
        }, 8, ["modelValue"]);
      };
    }
  };
  const HotkeyPopup = _export_sfc(_sfc_main$b, [["__scopeId", "data-v-07402c98"]]);
  const _hoisted_1$9 = { class: "icon-button" };
  const _hoisted_2$5 = { class: "button-text" };
  const _sfc_main$a = {
    __name: "MenuButton",
    props: {
      icon: String,
      name: String,
      color: String
    },
    setup(__props) {
      vue.useCssVars((_ctx) => ({
        "v12c3e3a5": __props.color
      }));
      const props = __props;
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$9, [
          vue.createVNode(ToolbarIcon, {
            icon: props.icon,
            color: props.color
          }, null, 8, ["icon", "color"]),
          vue.createElementVNode("div", _hoisted_2$5, vue.toDisplayString(props.name), 1)
        ]);
      };
    }
  };
  const MenuButton = _export_sfc(_sfc_main$a, [["__scopeId", "data-v-cb0cf859"]]);
  const _hoisted_1$8 = { class: "menu-popup-container" };
  const BUTTON_COLOR = "#555";
  const _sfc_main$9 = {
    __name: "MenuPopup",
    setup(__props) {
      const store = useStore();
      async function captureAsImage() {
        const content = document.querySelector("section.content");
        const canvas = await html2canvas(content);
        const blob = await new Promise((resolve) => {
          canvas.toBlob(resolve, "image/png");
        });
        const url = URL.createObjectURL(blob);
        window.open(url, "_blank");
        setTimeout(() => URL.revokeObjectURL(url), 1e4);
      }
      function searchOnYoutube() {
        const chordSheetDocument = new ChordSheetDocument();
        const title = chordSheetDocument.getTitle();
        const artist = chordSheetDocument.getSinger();
        const url = `https://www.youtube.com/results?search_query=${title}+${artist}`;
        window.open(url, "_blank").focus();
      }
      function goToGithubPage() {
        const url = "https://github.com/DonkeyBear/91Plus/blob/main/README.md";
        window.open(url, "_blank").focus();
      }
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(PopupBase, {
          id: "plus91-menu-popup",
          modelValue: vue.unref(store).isPopupShow.menu,
          "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => vue.unref(store).isPopupShow.menu = $event)
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", _hoisted_1$8, [
              vue.createVNode(MenuButton, {
                icon: "keyboard",
                name: "快捷鍵",
                color: BUTTON_COLOR,
                onClick: _cache[0] || (_cache[0] = () => {
                  vue.unref(store).togglePopup("hotkey");
                })
              }),
              vue.createVNode(MenuButton, {
                icon: "file-earmark-image",
                name: "擷取為圖片",
                color: BUTTON_COLOR,
                onClick: captureAsImage
              }),
              vue.createVNode(MenuButton, {
                icon: "youtube",
                name: "搜尋 YouTube",
                color: BUTTON_COLOR,
                onClick: searchOnYoutube
              }),
              vue.createVNode(MenuButton, {
                icon: "github",
                name: "關於 91 Plus",
                color: BUTTON_COLOR,
                onClick: goToGithubPage
              })
            ])
          ]),
          _: 1
        }, 8, ["modelValue"]);
      };
    }
  };
  const MenuPopup = _export_sfc(_sfc_main$9, [["__scopeId", "data-v-f8df8357"]]);
  const colors = [
    "#4b96a9",
    "#a2b538",
    "#e181bf",
    "#6c59bb"
  ];
  const _hoisted_1$7 = { class: "color-switcher-container" };
  const _hoisted_2$4 = ["onClick"];
  const _sfc_main$8 = {
    __name: "ColorSwitcher",
    props: vue.mergeModels({
      options: {
        type: Array,
        required: true,
        validator: (options) => {
          return options.every((opt) => typeof opt === "string");
        }
      }
    }, {
      "modelValue": {},
      "modelModifiers": {}
    }),
    emits: ["update:modelValue"],
    setup(__props) {
      const props = __props;
      const modelValue = vue.useModel(__props, "modelValue");
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$7, [
          (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(props.options, (option) => {
            return vue.openBlock(), vue.createElementBlock("div", {
              key: option,
              class: "color-switcher-option",
              style: vue.normalizeStyle({
                background: `color-mix(in srgb, ${option} 75%, white)`,
                borderColor: `color-mix(in srgb, ${option} 80%, white)`
              }),
              onClick: () => modelValue.value = option
            }, [
              modelValue.value === option ? (vue.openBlock(), vue.createBlock(BootstrapIcon, {
                key: 0,
                icon: "check",
                color: `color-mix(in srgb, ${option} 25%, white)`
              }, null, 8, ["color"])) : vue.createCommentVNode("", true)
            ], 12, _hoisted_2$4);
          }), 128))
        ]);
      };
    }
  };
  const ColorSwitcher = _export_sfc(_sfc_main$8, [["__scopeId", "data-v-9499f72a"]]);
  const _hoisted_1$6 = { class: "toggle-switch" };
  const _sfc_main$7 = {
    __name: "ToggleSwitch",
    props: {
      "modelValue": {
        type: Boolean,
        default: false
      },
      "modelModifiers": {}
    },
    emits: ["update:modelValue"],
    setup(__props) {
      const modelValue = vue.useModel(__props, "modelValue");
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("label", _hoisted_1$6, [
          vue.withDirectives(vue.createElementVNode("input", {
            "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
            type: "checkbox",
            hidden: ""
          }, null, 512), [
            [vue.vModelCheckbox, modelValue.value]
          ]),
          vue.createElementVNode("div", {
            class: vue.normalizeClass(["switch-track", { active: modelValue.value }])
          }, [..._cache[1] || (_cache[1] = [
            vue.createElementVNode("div", { class: "switch-thumb" }, null, -1)
          ])], 2)
        ]);
      };
    }
  };
  const ToggleSwitch = _export_sfc(_sfc_main$7, [["__scopeId", "data-v-1cf8e431"]]);
  const _hoisted_1$5 = { class: "settings-popup-container" };
  const _hoisted_2$3 = { class: "setting-item" };
  const _hoisted_3$2 = { class: "setting-item" };
  const _hoisted_4$1 = { class: "setting-item" };
  const _hoisted_5$1 = { class: "setting-item" };
  const _sfc_main$6 = {
    __name: "SettingsPopup",
    setup(__props) {
      const store = useStore();
      const themeColor = useCssVar("--theme-color", document.documentElement);
      vue.watch(() => store.themeColor, (newColor) => {
        themeColor.value = newColor;
      }, { immediate: true });
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(PopupBase, {
          id: "plus91-settings-popup",
          modelValue: vue.unref(store).isPopupShow.settings,
          "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => vue.unref(store).isPopupShow.settings = $event)
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", _hoisted_1$5, [
              vue.createElementVNode("div", _hoisted_2$3, [
                vue.createElementVNode("div", null, [
                  vue.createVNode(BootstrapIcon, { icon: "palette" }),
                  _cache[5] || (_cache[5] = vue.createTextVNode(" 主題色 ", -1))
                ]),
                vue.createVNode(ColorSwitcher, {
                  modelValue: vue.unref(store).themeColor,
                  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.unref(store).themeColor = $event),
                  options: vue.unref(colors)
                }, null, 8, ["modelValue", "options"])
              ]),
              vue.createElementVNode("label", _hoisted_3$2, [
                vue.createElementVNode("div", null, [
                  vue.createVNode(BootstrapIcon, { icon: "moon" }),
                  _cache[6] || (_cache[6] = vue.createTextVNode(" 深色模式 ", -1))
                ]),
                vue.createVNode(ToggleSwitch, {
                  modelValue: vue.unref(store).isDarkMode,
                  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => vue.unref(store).isDarkMode = $event)
                }, null, 8, ["modelValue"])
              ]),
              vue.createElementVNode("label", _hoisted_4$1, [
                vue.createElementVNode("div", null, [
                  vue.createVNode(BootstrapIcon, { icon: "cloudy" }),
                  _cache[7] || (_cache[7] = vue.createTextVNode(" 協助測試雲端樂譜 ", -1))
                ]),
                vue.createVNode(ToggleSwitch, {
                  modelValue: vue.unref(store).agreeToArchiveSheet,
                  "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => vue.unref(store).agreeToArchiveSheet = $event)
                }, null, 8, ["modelValue"])
              ]),
              vue.createElementVNode("label", _hoisted_5$1, [
                vue.createElementVNode("div", null, [
                  vue.createVNode(BootstrapIcon, { icon: "code-slash" }),
                  _cache[8] || (_cache[8] = vue.createTextVNode(" 開發者模式 ", -1))
                ]),
                vue.createVNode(ToggleSwitch, {
                  modelValue: vue.unref(store).isDevMode,
                  "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => vue.unref(store).isDevMode = $event)
                }, null, 8, ["modelValue"])
              ])
            ])
          ]),
          _: 1
        }, 8, ["modelValue"]);
      };
    }
  };
  const SettingsPopup = _export_sfc(_sfc_main$6, [["__scopeId", "data-v-3259211c"]]);
  const _hoisted_1$4 = { class: "sheet-popup-container" };
  const _hoisted_2$2 = { class: "text-capo" };
  const _hoisted_3$1 = ["innerHTML"];
  const _hoisted_4 = { class: "transpose-range-container" };
  const _hoisted_5 = ["value"];
  const _hoisted_6 = { class: "instrument-select-container" };
  const _sfc_main$5 = {
    __name: "SheetPopup",
    setup(__props) {
      const store = useStore();
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(PopupBase, {
          id: "plus91-sheet-popup",
          modelValue: vue.unref(store).isPopupShow.sheet,
          "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => vue.unref(store).isPopupShow.sheet = $event)
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", _hoisted_1$4, [
              vue.createVNode(AdjustWidget, {
                "onclick-left": () => {
                  vue.unref(store).plusTranspose(-1);
                },
                "onclick-middle": () => {
                  vue.unref(store).transpose = 0;
                },
                "onclick-right": () => {
                  vue.unref(store).plusTranspose(1);
                }
              }, {
                default: vue.withCtx(() => [
                  _cache[5] || (_cache[5] = vue.createTextVNode(" CAPO:", -1)),
                  vue.createElementVNode("span", _hoisted_2$2, vue.toDisplayString(vue.unref(store).currentCapo), 1),
                  _cache[6] || (_cache[6] = vue.createTextVNode(" (", -1)),
                  vue.createElementVNode("span", {
                    class: "text-key",
                    innerHTML: vue.unref(store).currentKey
                  }, null, 8, _hoisted_3$1),
                  _cache[7] || (_cache[7] = vue.createTextVNode(") ", -1))
                ]),
                _: 1
              }, 8, ["onclick-left", "onclick-middle", "onclick-right"]),
              vue.createElementVNode("div", _hoisted_4, [
                vue.createElementVNode("input", {
                  type: "range",
                  min: "-11",
                  max: "11",
                  value: vue.unref(store).currentCapo,
                  onInput: _cache[0] || (_cache[0] = ($event) => {
                    vue.unref(store).transpose = $event.target.value - vue.unref(store).originalCapo;
                  })
                }, null, 40, _hoisted_5)
              ]),
              vue.createElementVNode("div", _hoisted_6, [
                vue.createElementVNode("button", {
                  class: "instrument-select-button",
                  onClick: _cache[1] || (_cache[1] = () => {
                    vue.unref(switchInstrument)("");
                  })
                }, " 無 "),
                vue.createElementVNode("button", {
                  class: "instrument-select-button",
                  onClick: _cache[2] || (_cache[2] = () => {
                    vue.unref(switchInstrument)("guitar");
                  })
                }, " 吉他 "),
                vue.createElementVNode("button", {
                  class: "instrument-select-button",
                  onClick: _cache[3] || (_cache[3] = () => {
                    vue.unref(switchInstrument)("ukulele");
                  })
                }, " 烏克莉莉 ")
              ])
            ])
          ]),
          _: 1
        }, 8, ["modelValue"]);
      };
    }
  };
  const SheetPopup = _export_sfc(_sfc_main$5, [["__scopeId", "data-v-18539399"]]);
  const _hoisted_1$3 = { id: "plus91-footer" };
  const _hoisted_2$1 = { class: "footer-container" };
  const _sfc_main$4 = {
    __name: "AppFooter",
    props: {
      active: Boolean
    },
    setup(__props) {
      const props = __props;
      const store = useStore();
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide" }, {
          default: vue.withCtx(() => [
            vue.withDirectives(vue.createElementVNode("div", _hoisted_1$3, [
              vue.createElementVNode("div", _hoisted_2$1, [
                vue.createVNode(ToolbarIcon, {
                  icon: "music-note-beamed",
                  text: "譜面",
                  stroke: ".05rem",
                  active: vue.unref(store).isPopupShow.sheet,
                  onClick: _cache[0] || (_cache[0] = ($event) => vue.unref(store).togglePopup("sheet"))
                }, null, 8, ["active"]),
                vue.createVNode(ToolbarIcon, {
                  icon: "table",
                  text: "和弦",
                  active: vue.unref(store).isPopupShow.chord,
                  onClick: _cache[1] || (_cache[1] = ($event) => vue.unref(store).togglePopup("chord"))
                }, null, 8, ["active"]),
                vue.createVNode(ToolbarIcon, {
                  icon: "type",
                  text: "字型",
                  stroke: ".05rem",
                  active: vue.unref(store).isPopupShow.font,
                  onClick: _cache[2] || (_cache[2] = ($event) => vue.unref(store).togglePopup("font"))
                }, null, 8, ["active"]),
                vue.createVNode(ToolbarIcon, {
                  icon: "gear-wide-connected",
                  text: "設定",
                  active: vue.unref(store).isPopupShow.settings,
                  onClick: _cache[3] || (_cache[3] = ($event) => vue.unref(store).togglePopup("settings"))
                }, null, 8, ["active"]),
                vue.createVNode(ToolbarIcon, {
                  icon: "list",
                  text: "其他",
                  stroke: ".05rem",
                  active: vue.unref(store).isPopupShow.menu,
                  onClick: _cache[4] || (_cache[4] = ($event) => vue.unref(store).togglePopup("menu"))
                }, null, 8, ["active"]),
                vue.createVNode(SheetPopup),
                vue.createVNode(ChordPopup),
                vue.createVNode(_sfc_main$d),
                vue.createVNode(SettingsPopup),
                vue.createVNode(MenuPopup),
                vue.createVNode(HotkeyPopup)
              ])
            ], 512), [
              [vue.vShow, props.active]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const AppFooter = _export_sfc(_sfc_main$4, [["__scopeId", "data-v-c2303173"]]);
  const _hoisted_1$2 = { id: "plus91-header" };
  const _hoisted_2 = { class: "header-container" };
  const _hoisted_3 = { class: "search-container" };
  const _sfc_main$3 = {
    __name: "AppHeader",
    props: {
      active: Boolean
    },
    setup(__props) {
      const props = __props;
      const isSearchInputFocused = vue.ref(false);
      const searchText = vue.ref("");
      function search() {
        if (!searchText.value) {
          return;
        }
        const url = `https://www.91pu.com.tw/plus/search.php?keyword=${searchText.value}`;
        window.open(url, "_blank").focus();
      }
      function backToPreviousPage() {
        history.back();
      }
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide" }, {
          default: vue.withCtx(() => [
            vue.withDirectives(vue.createElementVNode("div", _hoisted_1$2, [
              vue.createElementVNode("div", _hoisted_2, [
                vue.createVNode(ToolbarIcon, {
                  icon: "chevron-left",
                  stroke: ".04rem",
                  onClick: backToPreviousPage
                }),
                vue.createElementVNode("form", {
                  onSubmit: vue.withModifiers(search, ["prevent"])
                }, [
                  vue.createElementVNode("div", _hoisted_3, [
                    vue.withDirectives(vue.createElementVNode("input", {
                      "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchText.value = $event),
                      type: "text",
                      placeholder: "搜尋樂譜 —— 91 Plus",
                      onKeydown: _cache[1] || (_cache[1] = vue.withKeys(vue.withModifiers((event) => {
                        event.target.blur();
                      }, ["stop"]), ["esc"])),
                      onFocus: _cache[2] || (_cache[2] = ($event) => isSearchInputFocused.value = true),
                      onBlur: _cache[3] || (_cache[3] = ($event) => isSearchInputFocused.value = false)
                    }, null, 544), [
                      [
                        vue.vModelText,
                        searchText.value,
                        void 0,
                        { trim: true }
                      ]
                    ]),
                    searchText.value ? (vue.openBlock(), vue.createBlock(ToolbarIcon, {
                      key: 0,
                      class: "clear-input",
                      icon: "x",
                      color: isSearchInputFocused.value ? "#0007" : "#fffa",
                      onClick: _cache[4] || (_cache[4] = () => {
                        searchText.value = "";
                      })
                    }, null, 8, ["color"])) : vue.createCommentVNode("", true)
                  ])
                ], 32),
                vue.createVNode(ToolbarIcon, {
                  icon: "search",
                  stroke: ".03rem",
                  onClick: search
                })
              ])
            ], 512), [
              [vue.vShow, props.active]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const AppHeader = _export_sfc(_sfc_main$3, [["__scopeId", "data-v-c3427f68"]]);
  const _hoisted_1$1 = { id: "dark-mode-overlay" };
  const _sfc_main$2 = {
    __name: "DarkModeOverlay",
    props: {
      active: Boolean
    },
    setup(__props) {
      const props = __props;
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(vue.Transition, { name: "fade" }, {
          default: vue.withCtx(() => [
            vue.withDirectives(vue.createElementVNode("div", _hoisted_1$1, null, 512), [
              [vue.vShow, props.active]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const DarkModeOverlay = _export_sfc(_sfc_main$2, [["__scopeId", "data-v-111379c3"]]);
  const _hoisted_1 = { ref: "eruda-container" };
  const _sfc_main$1 = {
    __name: "ErudaContainer",
    setup(__props) {
      const store = useStore();
      const thisWindow = _unsafeWindow ?? window;
      const erudaContainer = vue.useTemplateRef("eruda-container");
      function initEruda() {
        const erudaEl = document.createElement("div");
        erudaContainer.value.appendChild(erudaEl);
        thisWindow.eruda.init({ container: erudaEl });
        thisWindow.eruda.get("snippets").clear();
        thisWindow.eruda.get("snippets").add("儲存模式", () => {
          console.log(`[91 Plus] 儲存模式:${MonkeyStorage.getStorageType()}`);
        }, "在控制台顯示目前的儲存模式");
      }
      function handleEruda(isDevMode) {
        if (isDevMode) {
          if (!thisWindow.eruda) {
            useScriptTag("https://cdn.jsdelivr.net/npm/eruda/eruda.min.js", initEruda);
          } else {
            initEruda();
          }
        } else {
          thisWindow.eruda?.destroy();
        }
      }
      watchImmediate(() => store.isDevMode, handleEruda);
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1, null, 512);
      };
    }
  };
  const _sfc_main = {
    __name: "App",
    setup(__props) {
      const store = useStore();
      const parent = useParentElement();
      onClickOutside(parent, store.toggleToolbars);
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.createVNode(AppHeader, {
            active: vue.unref(store).isToolbarsShow
          }, null, 8, ["active"]),
          vue.createVNode(AppFooter, {
            active: vue.unref(store).isToolbarsShow
          }, null, 8, ["active"]),
          vue.createVNode(DarkModeOverlay, {
            active: vue.unref(store).isDarkMode
          }, null, 8, ["active"]),
          vue.createVNode(_sfc_main$1)
        ], 64);
      };
    }
  };
  class StoreHandler {
#store;
    constructor() {
      this.#store = useStore();
    }
initStateFromDom() {
      const capoSelected = $(".capo .select").eq(0).text().trim();
      const originalCapo = +capoSelected.split(/\s*\/\s*/)[0];
      const originalKey = capoSelected.split(/\s*\/\s*/)[1];
      this.#store.originalCapo = originalCapo;
      this.#store.originalKey = originalKey;
      const fontSize = +$("#tone_z").css("font-size").match(/^\d+/)[0];
      const lineHeight = +$("#tone_z > p").css("line-height").match(/^\d+/)[0];
      this.#store.originalFontSize = fontSize;
      this.#store.originalLineHeight = lineHeight;
      const params = getQueryParams();
      if (params.transpose) {
        this.#store.transpose = params.transpose;
      }
    }
initWatchers() {
      this.#watchTranspose();
      this.#watchFontSize();
    }
#watchTranspose() {
      vue.watch(() => this.#store.transpose, (newValue, oldValue) => {
        ChordSheetElement.transposeSheet((newValue - oldValue) % 12);
      });
    }
    #watchFontSize() {
      vue.watch(() => this.#store.fontSizeDelta, (newValue) => {
        const oFontSize = this.#store.originalFontSize;
        const oLineHeight = this.#store.originalLineHeight;
        $("#tone_z").css("font-size", `${oFontSize + newValue}px`);
        $("#tone_z > p").css("line-height", `${oLineHeight + newValue}px`);
      });
    }
    initKeyBindings() {
      const activeElement = useActiveElement();
      function isInputFocused() {
        return activeElement.value?.tagName === "INPUT" || activeElement.value?.tagName === "TEXTAREA";
      }
      function whenInputNotFocused(func) {
        return () => {
          if (!isInputFocused()) {
            func();
          }
        };
      }
      onKeyStroke(" ", whenInputNotFocused(() => {
        this.#store.toggleToolbars();
      }));
      onKeyStroke("/", whenInputNotFocused(() => {
        if (!this.#store.isToolbarsShow) {
          this.#store.toggleToolbars();
          this.#store.closePopups();
        }
        setTimeout(() => {
          $("#plus91-header input")?.get(0)?.focus();
        });
      }));
      onKeyStroke("Escape", whenInputNotFocused(() => {
        if (this.#store.isToolbarsShow) {
          this.#store.toggleToolbars();
        }
      }));
      onKeyStroke("ArrowLeft", whenInputNotFocused(() => {
        if (this.#store.isPopupShow.sheet) {
          this.#store.plusTranspose(-1);
        }
      }));
      onKeyStroke("ArrowRight", whenInputNotFocused(() => {
        if (this.#store.isPopupShow.sheet) {
          this.#store.plusTranspose(1);
        }
      }));
      onKeyStroke("ArrowDown", whenInputNotFocused(() => {
        if (this.#store.isPopupShow.sheet) {
          this.#store.transpose = 0;
        }
      }));
    }
  }
  function init() {
    redirect();
    const storeHandler = new StoreHandler();
    storeHandler.initWatchers();
    storeHandler.initKeyBindings();
    onSheetDomReady(() => {
      changeTitle();
      storeHandler.initStateFromDom();
      const store = useStore();
      if (store.agreeToArchiveSheet) {
        archiveChordSheet();
      }
    });
  }
  const cdnsScss = '@import"https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css";';
  importCSS(cdnsScss);
  const variablesScss = "html{--toolbar-bg-color: color-mix(in srgb, var(--theme-color) 65%, transparent);--toolbar-border-color: color-mix(in srgb, var(--theme-color) 50%, rgba(255, 255, 255, .1))}";
  importCSS(variablesScss);
  const stylesScss = "html{--toolbar-bg-color: color-mix(in srgb, var(--theme-color) 65%, transparent);--toolbar-border-color: color-mix(in srgb, var(--theme-color) 50%, rgba(255, 255, 255, .1))}html{background:#fafafa url(/templets/pu/images/tone-bg.gif)}#vue-91plus{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif}.tfunc2{margin:10px}#mtitle{font-family:system-ui}input[type=range],input[type=range]::-webkit-slider-thumb,input[type=range]::-webkit-slider-runnable-track{-webkit-appearance:none;box-shadow:none}input[type=range]::-webkit-slider-thumb,input[type=range]::-webkit-slider-runnable-track{border:1px solid rgba(68,68,68,.25)}input[type=range]::-webkit-slider-thumb{background:#60748d}#viptoneWindow.window,#bottomad,.update_vip_bar,.wmask,header,footer,.autoscroll,.backplace,.set .keys,.set .plays,.set .clear,.setint .hr:nth-child(4),.setint .hr:nth-child(5),.setint .hr:nth-child(6),.adsbygoogle,[class^=AD2M],[id^=adGeek]{display:none!important}";
  importCSS(stylesScss);
  const pinia = createPinia();
  pinia.use(index_default);
  vue.createApp(_sfc_main).use(pinia).mount(
    (() => {
      const app = document.createElement("div");
      app.id = "vue-91plus";
      document.body.append(app);
      return app;
    })()
  );
  init();

})(Vue, vexchords, zipson, html2canvas);