91 Plus

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

// ==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);