[
  {
    "path": ".gitignore",
    "content": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n## User settings\nxcuserdata/\n\n## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)\n*.xcscmblueprint\n*.xccheckout\n\n## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)\nbuild/\nDerivedData/\n*.moved-aside\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\n\n## Obj-C/Swift specific\n*.hmap\n\n## App packaging\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n## Playgrounds\ntimeline.xctimeline\nplayground.xcworkspace\n\n# Swift Package Manager\n#\n# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.\n# Packages/\n# Package.pins\n# Package.resolved\n# *.xcodeproj\n#\n# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata\n# hence it is not needed unless you have added a package configuration file to your project\n# .swiftpm\n\n.build/\n\n# CocoaPods\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n#\n# Pods/\n#\n# Add this line if you want to avoid checking in source code from the Xcode workspace\n# *.xcworkspace\n\n# Carthage\n#\n# Add this line if you want to avoid checking in source code from Carthage dependencies.\n# Carthage/Checkouts\n\nCarthage/Build/\n\n# Accio dependency management\nDependencies/\n.accio/\n\n# fastlane\n#\n# It is recommended to not store the screenshots in the git repo.\n# Instead, use fastlane to re-generate the screenshots whenever they are needed.\n# For more information about the recommended setup visit:\n# https://docs.fastlane.tools/best-practices/source-control/#source-control\n\nfastlane/report.xml\nfastlane/Preview.html\nfastlane/screenshots/**/*.png\nfastlane/test_output\n\n# Code Injection\n#\n# After new code Injection tools there's a generated folder /iOSInjectionProject\n# https://github.com/johnno1962/injectionforxcode\n\niOSInjectionProject/\n"
  },
  {
    "path": "Docs/.eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": "Docs/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\n"
  },
  {
    "path": "Docs/README.md",
    "content": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.\n\n[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.\n\nThe `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "Docs/components/AvailableNow.tsx",
    "content": "import type { AppReleaseData } from '../pages/types'\nimport Image from \"next/image\"\n\nimport DownloadButton from './DownloadButton'\n\nimport logo from '../images/logo.png'\n\nconst AvailableNow = ({ latest }: { latest: AppReleaseData }) => (\n  <div className=\"relative py-8 px-4 md:px-8 border-y border-white/20 bg-[#232c33]\">\n    <div className=\"max-w-5xl mx-auto p-6 md:flex md:items-center md:justify-between\">\n      <div className=\"flex items-center\">\n        <Image className=\"-ml-4 mr-4\" src={logo} alt=\"logo\" height={120} width={120} />\n\n        <div>\n          <div className=\"font-bold font-mulish text-2xl md:text-4xl\">\n            Available on GitHub\n          </div>\n          <div className=\"font-medium opacity-60 mt-4 text-xs md:text-base\">\n            Requires macOS 11 Big Sur or later.\n          </div>\n        </div>\n      </div>\n\n      <DownloadButton\n        tagName={latest.tag_name}\n        downloadUrl={latest.assets[0].browser_download_url}\n        tailwindStyles={'block mt-6 md:mt-0 py-2.5 text-center'}\n      />\n    </div>\n  </div>\n)\nexport default AvailableNow\n"
  },
  {
    "path": "Docs/components/DownloadButton.tsx",
    "content": "import { RiDownloadCloud2Line } from 'react-icons/ri'\n\nconst DownloadButton = ({\n  tagName,\n  downloadUrl,\n  tailwindStyles,\n}: {\n  tagName: string\n  downloadUrl: string\n  tailwindStyles?: string\n}) => (\n  <a\n    href={downloadUrl}\n    target=\"_blank\"\n    rel=\"noopener noreferrer\"\n    className={`px-4 py-2 font-mulish rounded-lg lg:text-lg cursor-pointer bg-gradient-to-b from-slate-50 to-gray-300 text-slate-800 transition-all duration-150 hover:scale-105 hover:shadow-lg ${tailwindStyles}`}\n  >\n    Download <span className=\"font-bold\">{tagName}</span>\n    <RiDownloadCloud2Line className=\"inline ml-2\" size={20} />\n  </a>\n)\n\nexport default DownloadButton\n"
  },
  {
    "path": "Docs/components/Faq.tsx",
    "content": "const Faq = () => (\n  <article className=\"prose prose-invert max-w-none\">\n    <h2>FAQs</h2>\n\n    <ol>\n      <li>\n        <b>Will I get banned from the game?</b>\n        <br />\n        No, I seriously do not think simply reading from an API (outside the\n        game) constitutes as cheating.\n      </li>\n\n      <li>\n        <b>I don&apos;t see a window? Where is the APP?</b>\n        <br />\n        It is a <b>menu bar</b> APP, check for resin icons that should appear in\n        your menu bar.\n      </li>\n\n      <li>\n        <b>\n          Can I configure the refresh rate / data fetching frequency / polling\n          interval?\n        </b>\n        <br />\n        Yes - under <code>Preferences</code>.\n      </li>\n\n      <li>\n        <b>Can I revert back to the original colored resin icon?</b>\n        <br />\n        Yes - use either one, configured under <code>Preferences</code>.\n      </li>\n\n      <li>\n        <b>It is not working! Why?</b>\n        <br />\n        Most often it is your cookie problem. You need to make sure that you\n        have turned on <code>Real-Time Notes</code> under{' '}\n        <code>Battle Chronicle</code> inside{' '}\n        <a\n          href=\"https://www.hoyolab.com/article/1265396\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          HoYoLAB\n        </a>{' '}\n        or <code>实时便签</code> inside 米游社, depending on your server. You\n        would also have to set your profile to public (instructions for{' '}\n        <a\n          href=\"https://www.hoyolab.com/article/117720\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          HoYoLAB\n        </a>{' '}\n        (a bit futher down the page) /{' '}\n        <a\n          href=\"https://www.9game.cn/yuanshen/5606032.html\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          米游社\n        </a>\n        ).\n      </li>\n    </ol>\n  </article>\n)\nexport default Faq\n"
  },
  {
    "path": "Docs/components/Features.tsx",
    "content": "import Image, { StaticImageData } from \"next/image\"\n\nimport daily from '../images/daily.png'\nimport expedition from '../images/expedition.png'\nimport fragileResin from '../images/fragile-resin.png'\nimport weeklyBoss from '../images/weekly-boss.png'\nimport jarOfRiches from '../images/jar-of-riches.png'\nimport parametric from '../images/parametric-transformer.png'\n\nconst IconCard = ({\n  icon,\n  label,\n  style,\n}: {\n  icon: StaticImageData\n  label: string\n  style: string\n}) => (\n  <div\n    className={`rounded-lg lg:text-lg flex flex-col items-center justify-center py-6 ${style}`}\n  >\n    <Image\n      src={icon}\n      alt=\"icon\"\n      width={36}\n      height={36}\n      style={{\n        maxWidth: \"100%\",\n        height: \"auto\"\n      }} />\n    <span className=\"mt-2\">{label}</span>\n  </div>\n)\n\nconst Features = () => (\n  <div className=\"relative grid grid-cols-2 md:grid-cols-3 2xl:grid-cols-6 gap-4 md:gap-8 mt-12 py-16 px-4 md:px-8 border-y border-white/20 bg-[#232c33]\">\n    <IconCard\n      icon={fragileResin}\n      label=\"Resin\"\n      style=\"bg-blue-900/30 text-blue-200\"\n    />\n    <IconCard\n      icon={daily}\n      label=\"Daily Commissions\"\n      style=\"bg-purple-900/30 text-purple-200\"\n    />\n    <IconCard\n      icon={expedition}\n      label=\"Expeditions\"\n      style=\"bg-orange-900/30 text-orange-200\"\n    />\n    <IconCard\n      icon={weeklyBoss}\n      label=\"Weekly Bosses\"\n      style=\"bg-yellow-900/30 text-amber-200\"\n    />\n    <IconCard\n      icon={jarOfRiches}\n      label=\"Realm Currency\"\n      style=\"bg-gray-900/30 text-gray-200\"\n    />\n    <IconCard\n      icon={parametric}\n      label=\"Parametric Transformer\"\n      style=\"bg-teal-900/30 text-teal-200\"\n    />\n  </div>\n)\n\nexport default Features\n"
  },
  {
    "path": "Docs/components/Footer.tsx",
    "content": "import { RiHeartPulseLine, RiHeartsLine } from 'react-icons/ri'\n\nconst Footer = () => (\n  <div className=\"relative px-4 py-8 text-sm text-center border-t border-white/20 bg-[#232c33] w-full\">\n    <div>\n      Created with love by{' '}\n      <a\n        href=\"https://spencerwoo.com\"\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        className=\"text-[#9CA6A0] underline hover:opacity-90\"\n      >\n        Spencer Woo\n      </a>{' '}\n    </div>\n    <div>\n      <RiHeartPulseLine className=\"inline\" /> Love and Kisses from Hu Tao{' '}\n      <RiHeartsLine className=\"inline\" />\n    </div>\n  </div>\n)\n\nexport default Footer\n"
  },
  {
    "path": "Docs/components/GitHubButton.tsx",
    "content": "import { RiGithubLine } from 'react-icons/ri'\n\nconst GitHubButton = () => {\n  return (\n    <a\n      className=\"px-4 py-2 rounded-lg font-mulish lg:text-lg cursor-pointer border border-gradient-to-b from-slate-50 to-gray-300 transition-all duration-150 hover:scale-105 hover:shadow-lg\"\n      href=\"https://github.com/spencerwooo/PaimonMenuBar\"\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n    >\n      GitHub\n      <RiGithubLine className=\"inline ml-2\" size={20} />\n    </a>\n  )\n}\n\nexport default GitHubButton\n"
  },
  {
    "path": "Docs/components/Head.tsx",
    "content": "import Head from 'next/head'\n\nconst Meta = () => (\n  <Head>\n    <title>PaimonMenuBar</title>\n    <meta name=\"description\" content=\"Paimon is now in your macOS menubar!\" />\n    <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/apple-touch-icon.png\" />\n    <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\" />\n    <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\" />\n    <link rel=\"manifest\" href=\"/site.webmanifest\" />\n  </Head>\n)\n\nexport default Meta\n"
  },
  {
    "path": "Docs/components/Hero.tsx",
    "content": "import type { AppReleaseData } from '../pages/types'\n\nimport Image from \"next/image\"\n\nimport Screenshot3D from '../components/Screenshot3D'\nimport DownloadButton from '../components/DownloadButton'\nimport ReleaseInfo from '../components/ReleaseInfo'\nimport GitHubButton from '../components/GitHubButton'\n\nimport logo from '../images/logo.png'\n\nconst Hero = ({ latest }: { latest: AppReleaseData }) => (\n  <div className=\"relative max-w-5xl p-6 mx-auto pt-28\">\n    <div className=\"hidden md:block float-right\">\n      <Screenshot3D />\n    </div>\n\n    <div className=\"flex items-center\">\n      <Image className=\"-ml-4\" src={logo} alt=\"logo\" height={72} width={72} priority />\n\n      <span className=\"text-3xl lg:text-5xl font-bold tracking-wide ml-4 font-mulish inline\">\n        PaimonMenuBar\n      </span>\n    </div>\n\n    <div className=\"mt-16 text-xl lg:text-3xl max-w-lg tracking-wide\">\n      Track your Genshin Impact daily resin, expeditions, and more — straight in\n      your macOS menu bar.\n    </div>\n\n    <div className=\"text-lg mt-16\">\n      <div className=\"opacity-60 text-xs lg:text-base mb-4\">\n        Made with SwiftUI, designed for macOS. Works with —\n      </div>\n\n      <div className=\"flex items-center space-x-4\">\n        <div className=\"border rounded-lg p-2\">\n          <div className=\"text-xs\">天空岛 | 世界树</div>\n          <div className=\"font-bold font-mulish tracking-wider\">🇨🇳 CN</div>\n        </div>\n        <div className=\"opacity-60\">&</div>\n        <div className=\"border rounded-lg p-2\">\n          <div className=\"text-xs\">NA | EU | Asia | SAR</div>\n          <div className=\"font-bold font-mulish tracking-wider\">🌍 Global</div>\n        </div>\n      </div>\n    </div>\n\n    <div className=\"inline-flex items-center space-x-4 mt-16\">\n      <DownloadButton\n        tagName={latest.tag_name}\n        downloadUrl={latest.assets[0].browser_download_url}\n      />\n      <GitHubButton />\n    </div>\n\n    <div className=\"font-medium opacity-80 mt-4\">\n      Requires macOS 11 Big Sur or later.\n    </div>\n\n    <ReleaseInfo\n      htmlUrl={latest.html_url}\n      publishedAt={latest.published_at}\n      downloadCount={latest.assets[0].download_count}\n      reactions={latest.reactions}\n    />\n  </div>\n)\nexport default Hero\n"
  },
  {
    "path": "Docs/components/HowToGetMyCookie.tsx",
    "content": "import Image from \"next/image\"\n\nimport hutaoSleepy from '../images/hutao-sleepy.png'\nimport cookieScreenshot from '../images/cookie.jpg'\nimport configScreenshot from '../images/config-screenshot.jpg'\n\nconst HowToGetMyCookie = () => (\n  <div>\n    <Image\n      className=\"float-right sticky top-4 hidden md:block\"\n      src={hutaoSleepy}\n      alt=\"emoji hutao\"\n      width={128}\n      height={128} />\n\n    <section\n      id=\"how-to-get-my-cookie\"\n      className=\"prose prose-invert prose-img:rounded prose-figcaption:text-center\"\n    >\n      <h2>How to get my cookie?</h2>\n\n      <p>\n        Open{' '}\n        <a\n          href=\"https://bbs.mihoyo.com/ys\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          https://bbs.mihoyo.com/ys\n        </a>{' '}\n        (if you are on 天空岛 or 世界树) or{' '}\n        <a\n          href=\"https://www.hoyolab.com/home\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          https://hoyolab.com/home\n        </a>{' '}\n        (if you are on NA | EU | Asia | SAR) in <b>Chrome</b>, login, and press{' '}\n        <kbd>F12</kbd> to open Chrome devtools.\n      </p>\n\n      <p>\n        Navigate to <code>Console</code>, type in <code>document.cookie</code>{' '}\n        and press <kbd>Enter</kbd>:\n      </p>\n\n      <figure>\n        <Image\n          src={cookieScreenshot}\n          alt=\"Cookie screenshot\"\n          style={{\n            maxWidth: \"100%\",\n            height: \"auto\"\n          }} />\n        <figcaption>Getting your cookie from 米游社 or HoYoLAB</figcaption>\n      </figure>\n\n      <p>\n        Copy the string <i>without the quotes</i> and paste it inside{' '}\n        <code>PaimonMenuBar</code> under{' '}\n        <code>Preferences {'>'} Configuration</code>, and test your config:\n      </p>\n\n      <figure>\n        <Image\n          src={configScreenshot}\n          alt=\"Config screenshot\"\n          style={{\n            maxWidth: \"100%\",\n            height: \"auto\"\n          }} />\n        <figcaption>Putting your cookie in PaimonMenuBar</figcaption>\n      </figure>\n\n      <p>\n        You should be able to see the updated data inside the app - which will\n        periodically update if your config stays valid, enjoy!\n      </p>\n    </section>\n  </div>\n)\nexport default HowToGetMyCookie\n"
  },
  {
    "path": "Docs/components/PaimonCan.tsx",
    "content": "import Image from \"next/image\"\nimport paimonMighty from '../images/paimon-mighty.png'\n\nconst PaimonCan = () => (\n  <div>\n    <Image\n      className=\"float-right sticky top-4 hidden md:block\"\n      src={paimonMighty}\n      alt=\"paimon emoji\"\n      width={128}\n      height={128} />\n\n    <section className=\"prose prose-invert\">\n      <h2>Mighty Paimon!</h2>\n\n      <p>Paimon can help you —</p>\n\n      <ul>\n        <li>Keep track of your daily resin.</li>\n        <li>Monitor your daily expeditions and real-time realm currency.</li>\n        <li>Remind you about your daily commissions and weekly boss fights.</li>\n        <li>\n          And notify you when your parametric transformer is ready to use.\n        </li>\n      </ul>\n\n      <p>\n        Basically, Paimon lives in your macOS menu bar quietly, and offers you a\n        nice way of monitoring your in-game real-time stats when you need to\n        check them.\n      </p>\n    </section>\n  </div>\n)\n\nexport default PaimonCan\n"
  },
  {
    "path": "Docs/components/PaimonCookie.tsx",
    "content": "import Image from \"next/image\"\nimport luminePlease from '../images/lumine-please.png'\n\nconst PaimonCookie = () => (\n  <div>\n    <Image\n      className=\"float-right sticky top-4 hidden md:block\"\n      src={luminePlease}\n      alt=\"lumine emoji\"\n      width={128}\n      height={128} />\n\n    <section className=\"prose prose-invert\">\n      <h2>Why does Paimon need your cookie?</h2>\n      <p>\n        Cookies are sensitive information, and in some scenarios they function\n        as your login credentials. Paimon requires your cookie so that Paimon\n        can request said API on your behalf, and fetch those in-game stats\n        periodically.\n      </p>\n      <p>\n        Paimon will <b>never-ever-ever-ever</b> ask for your credentials to\n        Genshin Impact nor any account! The cookie is{' '}\n        <b>only stored locally.</b>\n      </p>\n    </section>\n  </div>\n)\nexport default PaimonCookie\n"
  },
  {
    "path": "Docs/components/PaimonUses.tsx",
    "content": "import Image from \"next/image\"\nimport zhongliThink from '../images/zhongli-think.png'\n\nconst PaimonUses = () => (\n  <div>\n    <Image\n      className=\"float-right sticky top-4 hidden md:block\"\n      src={zhongliThink}\n      alt=\"zhongli emoji\"\n      width={128}\n      height={128} />\n\n    <section className=\"prose prose-invert\">\n      <h2>How does Paimon work?</h2>\n      <p>\n        Paimon uses the official Mihoyo / Hoyoverse API found in either{' '}\n        <a\n          href=\"https://bbs.mihoyo.com/ys/\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          米游社 (for CN players)\n        </a>{' '}\n        or{' '}\n        <a\n          href=\"https://www.hoyolab.com/home\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n        >\n          HoYoLAB (for Global players)\n        </a>\n        .\n      </p>\n    </section>\n  </div>\n)\nexport default PaimonUses\n"
  },
  {
    "path": "Docs/components/ReleaseInfo.tsx",
    "content": "import type { AppReleaseData } from '../pages/types'\n\nconst reactionToEmoji = {\n  '+1': '👍',\n  '-1': '👎',\n  laugh: '😂',\n  confused: '😕',\n  heart: '❤️',\n  hooray: '🎉',\n  rocket: '🚀',\n  eyes: '👀',\n} as const\ntype reactionKeys = keyof typeof reactionToEmoji\n\nconst formatRelativeDate = (publishedAt: string) => {\n  const publishedDate = new Date(publishedAt)\n  const deltaTime = (publishedDate.getTime() - Date.now()) / 1000\n\n  const formatter = new Intl.RelativeTimeFormat()\n  if (deltaTime > -60 * 60) {\n    return formatter.format(Math.floor(deltaTime / 60), 'minute')\n  }\n  if (deltaTime > -24 * 60 * 60) {\n    return formatter.format(Math.floor(deltaTime / 60 / 60), 'hour')\n  }\n  if (deltaTime > -7 * 24 * 60 * 60) {\n    return formatter.format(Math.floor(deltaTime / 60 / 60 / 24), 'day')\n  }\n  return formatter.format(Math.floor(deltaTime / 60 / 60 / 24 / 7), 'week')\n}\n\nconst ReleaseInfo = ({\n  htmlUrl,\n  publishedAt,\n  downloadCount,\n  reactions,\n}: {\n  htmlUrl: string\n  publishedAt: string\n  downloadCount: number\n  reactions: AppReleaseData['reactions']\n}) => {\n  const reactionsNonZero = Object.entries(reactions ?? {}).filter(\n    ([key, count]) => key !== 'total_count' && count > 0\n  ) as Array<[reactionKeys, number]>\n\n  return (\n    <>\n      <a\n        href={htmlUrl}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        className=\"text-sm mt-4 opacity-50 hover:opacity-60\"\n      >\n        Last updated {formatRelativeDate(publishedAt)}. Downloads:{' '}\n        {downloadCount}.\n        <div className=\"flex md:inline-flex mt-2 md:ml-2 items-center gap-2 text-xs\">\n          {reactionsNonZero.map(\n            ([key, val]: [key: reactionKeys, val: number]) => (\n              <span\n                key={key}\n                className=\"rounded-lg border border-slate-50/40 px-2 py-0.5\"\n              >\n                {reactionToEmoji[key]} {val}\n              </span>\n            )\n          )}\n        </div>\n      </a>\n    </>\n  )\n}\n\nexport default ReleaseInfo\n"
  },
  {
    "path": "Docs/components/Screenshot3D.tsx",
    "content": "import Image from \"next/image\"\nimport Atropos from 'atropos/react'\nimport screenshot from '../images/screenshot-transparent-light.png'\n\nconst Screenshot3D = () => (\n  <Atropos className=\"w-[360px] h-[592.8]\" shadow={false} highlight={false}>\n    <Image\n      src={screenshot}\n      alt=\"PaimonMenuBar screenshot\"\n      width={360}\n      height={592.8}\n      style={{\n        maxWidth: \"100%\",\n        height: \"auto\"\n      }} />\n  </Atropos>\n)\n\nexport default Screenshot3D\n"
  },
  {
    "path": "Docs/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": "Docs/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  reactStrictMode: true,\n\n}\n\nmodule.exports = nextConfig\n"
  },
  {
    "path": "Docs/package.json",
    "content": "{\n  \"name\": \"docs\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@fontsource/mulish\": \"^4.5.14\",\n    \"atropos\": \"^1.0.2\",\n    \"next\": \"13.2.4\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-icons\": \"^4.8.0\"\n  },\n  \"devDependencies\": {\n    \"@tailwindcss/typography\": \"^0.5.9\",\n    \"@types/node\": \"18.15.0\",\n    \"@types/react\": \"18.0.28\",\n    \"@types/react-dom\": \"18.0.11\",\n    \"autoprefixer\": \"^10.4.14\",\n    \"eslint\": \"8.36.0\",\n    \"eslint-config-next\": \"13.2.4\",\n    \"postcss\": \"^8.4.21\",\n    \"tailwindcss\": \"^3.2.7\",\n    \"typescript\": \"4.9.5\"\n  }\n}\n"
  },
  {
    "path": "Docs/pages/_app.tsx",
    "content": "import '../styles/globals.css'\nimport 'atropos/css'\n\nimport '@fontsource/mulish/400.css'\nimport '@fontsource/mulish/700.css'\n\nimport type { AppProps } from 'next/app'\n\nfunction MyApp({ Component, pageProps }: AppProps) {\n  return <Component {...pageProps} />\n}\n\nexport default MyApp\n"
  },
  {
    "path": "Docs/pages/api/hello.ts",
    "content": "// Next.js API route support: https://nextjs.org/docs/api-routes/introduction\nimport type { NextApiRequest, NextApiResponse } from 'next'\n\ntype Data = {\n  name: string\n}\n\nexport default function handler(\n  req: NextApiRequest,\n  res: NextApiResponse<Data>\n) {\n  res.status(200).json({ name: 'Paimon says hi!' })\n}\n"
  },
  {
    "path": "Docs/pages/index.tsx",
    "content": "import type { GetStaticProps } from 'next'\nimport type { AppReleaseData } from './types'\nimport Image from \"next/image\"\n\nimport Features from '../components/Features'\nimport PaimonCan from '../components/PaimonCan'\nimport PaimonUses from '../components/PaimonUses'\nimport PaimonCookie from '../components/PaimonCookie'\nimport HowToGetMyCookie from '../components/HowToGetMyCookie'\nimport Footer from '../components/Footer'\nimport Hero from '../components/Hero'\nimport Meta from '../components/Head'\n\nimport hutaoBackground from '../images/hutao-bg.jpg'\nimport AvailableNow from '../components/AvailableNow'\nimport Faq from '../components/Faq'\n\nconst Home = ({ latest }: { latest: AppReleaseData }) => {\n  return <>\n    <Meta />\n\n    <main className=\"text-white relative\">\n      <div className=\"absolute w-full\">\n        <Image\n          src={hutaoBackground}\n          alt=\"background\"\n          placeholder=\"blur\"\n          priority\n          sizes=\"100vw\"\n          style={{\n            width: \"100%\",\n            height: \"auto\"\n          }} />\n        <div className=\"absolute top-0 bottom-0 left-0 right-0 bg-gradient-to-b from-transparent to-[#2C3740]\" />\n      </div>\n\n      <Hero latest={latest} />\n\n      <Features />\n\n      <div className=\"bg-[#2c3740] relative\">\n        <div className=\"max-w-5xl px-6 py-20 mx-auto\">\n          <div className=\"space-y-16\">\n            <PaimonCan />\n            <PaimonUses />\n            <PaimonCookie />\n            <HowToGetMyCookie />\n          </div>\n        </div>\n\n        <AvailableNow latest={latest} />\n\n        <div className=\"max-w-5xl px-6 py-20 mx-auto\">\n          <Faq />\n        </div>\n\n        <Footer />\n      </div>\n    </main>\n  </>;\n}\n\nexport const getStaticProps: GetStaticProps = async () => {\n  const resp = await fetch(\n    'https://api.github.com/repos/spencerwooo/PaimonMenuBar/releases/latest'\n  )\n  const latest = (await resp.json()) as AppReleaseData\n  return { props: { latest }, revalidate: 5 * 60 }\n}\n\nexport default Home\n"
  },
  {
    "path": "Docs/pages/types.d.ts",
    "content": "export type AppReleaseData = {\n  html_url: string\n  tag_name: string\n  name: string\n  published_at: string\n  assets: Array<{\n    size: number\n    download_count: number\n    browser_download_url: string\n  }>\n  reactions?: {\n    total_count: number\n    '+1': number\n    '-1': number\n    laugh: number\n    confused: number\n    heart: number\n    hooray: number\n    rocket: number\n    eyes: number\n  }\n}\n"
  },
  {
    "path": "Docs/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "Docs/public/site.webmanifest",
    "content": "{\"name\":\"\",\"short_name\":\"\",\"icons\":[{\"src\":\"/android-chrome-192x192.png\",\"sizes\":\"192x192\",\"type\":\"image/png\"},{\"src\":\"/android-chrome-512x512.png\",\"sizes\":\"512x512\",\"type\":\"image/png\"}],\"theme_color\":\"#ffffff\",\"background_color\":\"#ffffff\",\"display\":\"standalone\"}"
  },
  {
    "path": "Docs/styles/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n  background-color: #2c3740;\n}\n"
  },
  {
    "path": "Docs/tailwind.config.js",
    "content": "const defaultTheme = require('tailwindcss/defaultTheme')\n\nmodule.exports = {\n  content: [\n    \"./pages/**/*.{js,ts,jsx,tsx}\",\n    \"./components/**/*.{js,ts,jsx,tsx}\",\n  ],\n  theme: {\n    extend: {\n      fontFamily: {\n        \"mulish\": [\"Mulish\", ...defaultTheme.fontFamily.sans],\n      }\n    },\n  },\n  plugins: [require('@tailwindcss/typography')],\n}\n"
  },
  {
    "path": "Docs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Spencer Woo\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "PaimonMenuBar/AppDelegate.swift",
    "content": "//\n//  AppDelegate.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/23.\n//\n\nimport AppKit\nimport Defaults\nimport Foundation\nimport SwiftUI\nimport UserNotifications\n\nfinal class AppDelegate: NSObject, NSApplicationDelegate {\n    private(set) static var shared: AppDelegate!\n\n    private var statusItem: NSStatusItem!\n    private var statusButton: NSStatusBarButton!\n    private var menuItemMain: NSHostingView<MenuExtrasView>!\n\n    /** updateStatusBar and updateStatusIcon must be called in the main thread to avoid race condition. */\n    func updateStatusBar() {\n        assert(Thread.isMainThread)\n\n        updateStatusIcon()\n        updateStatusButtonTitle()\n\n        let gameRecord = Defaults[.lastGameRecord]\n        let currentExpeditionNum = gameRecord.data.current_expedition_num\n        menuItemMain.frame = NSRect(x: 0, y: 0, width: 290, height: 292 + currentExpeditionNum * 36)\n    }\n\n    func updateStatusIcon() {\n        assert(Thread.isMainThread)\n\n        statusButton.imagePosition = NSControl.ImagePosition.imageLeading\n        statusButton.image = NSImage(named: NSImage.Name(\"FragileResin\"))\n\n        // This sets the resin icon in the statusbar as monochrome if isTemplate == true\n        statusButton.image?.isTemplate = Defaults[.isStatusIconTemplate]\n\n        statusButton.image?.size.width = 19\n        statusButton.image?.size.height = 19\n    }\n\n    func updateStatusButtonTitle() {\n        if !Defaults[.isShowResinText] {\n            statusButton.title = \"\"\n            return\n        }\n\n        let gameRecord = Defaults[.lastGameRecord]\n        if gameRecord.fetchAt == nil {\n            statusButton.title = \"-/160\" // Cookie Not configured\n        } else {\n            statusButton.title = \"\\(gameRecord.data.current_resin)/\\(gameRecord.data.max_resin)\"\n        }\n    }\n\n    @objc private func openSettingsView() {\n        if #available(macOS 13, *) {\n            NSApp.sendAction(Selector((\"showSettingsWindow:\")), to: nil, from: nil)\n        } else {\n            NSApp.sendAction(Selector((\"showPreferencesWindow:\")), to: nil, from: nil)\n        }\n\n        NSApp.setActivationPolicy(.regular)\n        NSApp.activate(ignoringOtherApps: true)\n        NSApp.windows.first?.makeKeyAndOrderFront(self)\n    }\n\n    func applicationDidFinishLaunching(_: Notification) {\n        AppDelegate.shared = self\n\n        // Request notification permissions\n        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { _, error in\n            if error != nil {\n                print(\"Notification permission not granted.\")\n            } else {\n                print(\"Notification permission granted.\")\n            }\n        }\n\n        // Update game record on initial launch\n        print(\"App is started\")\n        GameRecordUpdater.shared.tryFetchGameRecordAndRender()\n\n        // Close main APP window on initial launch\n        NSApp.setActivationPolicy(.accessory)\n        if let window = NSApplication.shared.windows.first {\n            window.close()\n        }\n\n        setupStatusBar()\n    }\n\n    func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {\n        // Hide app icon in dock after all windows are closed\n        NSApp.setActivationPolicy(.accessory)\n        return false\n    }\n\n    private func setupStatusBar() {\n        let menu = NSMenu()\n\n        // Main menu area, render view as NSHostingView\n        menuItemMain = NSHostingView(rootView: MenuExtrasView())\n        let menuItem = NSMenuItem()\n        menuItem.view = menuItemMain\n        menu.addItem(menuItem)\n\n        // Submenu, preferences, and quit APP\n        menu.addItem(NSMenuItem.separator())\n        menu\n            .addItem(NSMenuItem(title: String.localized(\"Preferences\"), action: #selector(openSettingsView),\n                                keyEquivalent: \",\"))\n        menu\n            .addItem(NSMenuItem(title: String.localized(\"Quit\"), action: #selector(NSApplication.terminate(_:)),\n                                keyEquivalent: \"q\"))\n\n        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)\n        statusItem.menu = menu\n        statusButton = statusItem.button\n\n        updateStatusBar()\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"hutao16@1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"hutao16@2.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"hutao16@2-1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"hutao@64.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"hutao@128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"hutao@256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"hutao@256-1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"hutao@512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"hutao@512-1.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"hutao.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/Commision.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"commision.svg\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/Domain.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Domain.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"Domain@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"Domain@3x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/Expedition.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Expedition.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"Expedition@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"Expedition@3x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/FragileResin.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"fragile_resin@1.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"fragile_resin@2.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"fragile_resin@3.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/JarOfRiches.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"JarOfRiches.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"JarOfRiches@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"JarOfRiches@3x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/KokomiSad.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"kokomi_sad.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"kokomi_sad@2.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"kokomi_sad@3.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Assets.xcassets/ParametricTransformer.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"ParametricTransformer@1.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"ParametricTransformer@2.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"filename\" : \"ParametricTransformer@3.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Bundle.swift",
    "content": "//\n//  Bundle.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/26.\n//\n\nimport Foundation\n\nextension Bundle {\n    var appName: String? {\n        return object(forInfoDictionaryKey: \"CFBundleName\") as? String\n    }\n\n    var appVersion: String? {\n        return object(forInfoDictionaryKey: \"CFBundleShortVersionString\") as? String\n    }\n\n    var buildNumber: String? {\n        return object(forInfoDictionaryKey: \"CFBundleVersion\") as? String\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Compatibility.swift",
    "content": "//\n//  Compatibility.swift\n//  PaimonMenuBar\n//\n//  Created by DreamPiggy on 2022/5/6.\n//\n\nimport Foundation\nimport SwiftUI\n\nextension URLSession {\n    @available(macOS, deprecated: 12.0, message: \"This extension is no longer necessary. Use API built into SDK\")\n    func data(for request: URLRequest) async throws -> (Data, URLResponse) {\n        try await withCheckedThrowingContinuation { continuation in\n            let task = self.dataTask(with: request) { data, response, error in\n                guard let data = data, let response = response else {\n                    let error = error ?? URLError(.badServerResponse)\n                    return continuation.resume(throwing: error)\n                }\n\n                continuation.resume(returning: (data, response))\n            }\n\n            task.resume()\n        }\n    }\n}\n\nextension String {\n    @available(macOS, deprecated: 12.0, message: \"This extension is no longer necessary. Use API built into SDK\")\n    static func localized(\n        _ keyAndValue: LocalizedStringKey,\n        table: String? = nil,\n        bundle: Bundle? = nil,\n        locale: Locale = .current,\n        comment: StaticString? = nil\n    ) -> String {\n        var language = \"en\"\n        // Region: CN\n        // ID: zh-Hans-CN\n        // Need: zh-Hans\n        if let localRegion = locale.regionCode, let localID = locale.collatorIdentifier,\n           let range = localID.range(of: localRegion)\n        {\n            language = String(localID[..<range.lowerBound])\n            language.removeLast()\n        }\n\n        var localBundle = Bundle.main\n        if let path = (bundle ?? Bundle.main).path(forResource: language, ofType: \"lproj\") {\n            localBundle = Bundle(path: path) ?? .main\n        }\n\n        let mirror = Mirror(reflecting: keyAndValue)\n        let attributeLabelAndValue = mirror.children.first { arg0 -> Bool in\n            let (label, _) = arg0\n            if label == \"key\" {\n                return true\n            }\n            return false\n        }\n        guard let key = attributeLabelAndValue?.value as? String else {\n            fatalError()\n        }\n        return NSLocalizedString(key, tableName: table, bundle: localBundle, value: \"\", comment: \"\\(comment ?? \"\")\")\n    }\n}\n\nextension Date {\n    private static let shortenedFormatter: DateFormatter = {\n        let formatter = DateFormatter()\n        formatter.dateStyle = .none\n        formatter.timeStyle = .short\n        return formatter\n    }()\n\n    private static let defaultFormatter: DateFormatter = {\n        let formatter = DateFormatter()\n        formatter.dateStyle = .short\n        formatter.timeStyle = .short\n        return formatter\n    }()\n\n    var shortenedFormatted: String {\n        return Date.shortenedFormatter.string(from: self)\n    }\n\n    var defaultFormatted: String {\n        return Date.defaultFormatter.string(from: self)\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/DataModels.swift",
    "content": "//\n//  DataModels.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/24.\n//\n\nimport Defaults\nimport Foundation\n\nstruct GameRecord: Codable, Defaults.Serializable {\n    /**\n     This field is not returned by server. Instead, it is set by us after fetching.\n     When this field is not present, it means the record is not a real record (e.g. empty record).\n     */\n    var fetchAt: Date?\n\n    var retcode: Int\n    var message: String\n\n    var data: GameData\n\n    static let empty = GameRecord(\n        fetchAt: nil, // Indicate an empty record\n        retcode: 0,\n        message: \"OK\",\n        data: GameData(\n            current_resin: 0, max_resin: 160, resin_recovery_time: \"0\", finished_task_num: 0,\n            total_task_num: 4, is_extra_task_reward_received: false, remain_resin_discount_num: 0,\n            resin_discount_num_limit: 3, current_expedition_num: 1, max_expedition_num: 5,\n            expeditions: [Expeditions(status: \"Finished\", avatar_side_icon: \"\", remained_time: \"0\")],\n            current_home_coin: 0, max_home_coin: 2400, home_coin_recovery_time: \"0\", calendar_url: \"\",\n            transformer: Transformer(\n                obtained: true,\n                recovery_time: RecoveryTime(Day: 0, Hour: 0, Minute: 0, Second: 0, reached: false), wiki: \"\"\n            )\n        )\n    )\n}\n\nstruct GameData: Codable, Defaults.Serializable {\n    var current_resin: Int\n    var max_resin: Int\n    var resin_recovery_time: String\n    var finished_task_num: Int\n    var total_task_num: Int\n    var is_extra_task_reward_received: Bool\n    var remain_resin_discount_num: Int\n    var resin_discount_num_limit: Int\n    var current_expedition_num: Int\n    var max_expedition_num: Int\n\n    var expeditions: [Expeditions]\n\n    var current_home_coin: Int\n    var max_home_coin: Int\n    var home_coin_recovery_time: String\n    var calendar_url: String\n\n    var transformer: Transformer\n}\n\nstruct Expeditions: Codable, Hashable, Defaults.Serializable {\n    var status: String\n    var avatar_side_icon: String\n    var remained_time: String\n}\n\nstruct Transformer: Codable, Defaults.Serializable {\n    var obtained: Bool\n    var recovery_time: RecoveryTime\n    var wiki: String\n}\n\nstruct RecoveryTime: Codable, Defaults.Serializable {\n    var Day: Int\n    var Hour: Int\n    var Minute: Int\n    var Second: Int\n    var reached: Bool\n}\n\nenum GenshinServer: String, CaseIterable, Identifiable, Defaults.Serializable {\n    case cn_gf01 // 天空岛\n    case cn_qd01 // 世界树\n\n    case os_usa // Global (NA)\n    case os_euro // Global (EU)\n    case os_asia // Global (Asia)\n    case os_cht // Global (SAR)\n\n    var id: String { rawValue }\n\n    var serverName: String {\n        switch self {\n        case .cn_gf01:\n            return \"天空岛\"\n        case .cn_qd01:\n            return \"世界树\"\n        case .os_usa:\n            return \"NA\"\n        case .os_euro:\n            return \"EU\"\n        case .os_asia:\n            return \"Asia\"\n        case .os_cht:\n            return \"SAR\"\n        }\n    }\n\n    var isCNServer: Bool {\n        switch self {\n        case .cn_gf01, .cn_qd01:\n            return true\n        default:\n            return false\n        }\n    }\n\n    var cookieSiteUrl: String {\n        switch self {\n        case .cn_gf01, .cn_qd01:\n            return \"https://bbs.mihoyo.com/ys\"\n        case .os_usa, .os_euro, .os_asia, .os_cht:\n            return \"https://www.hoyolab.com/home\"\n        }\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Defaults+Workaround.swift",
    "content": "// See https://github.com/sindresorhus/Defaults/blob/main/workaround.md\n\nimport Defaults\nimport Foundation\n\npublic extension Defaults.Serializable where Self: Codable {\n    static var bridge: Defaults.TopLevelCodableBridge<Self> { Defaults.TopLevelCodableBridge() }\n}\n\npublic extension Defaults.Serializable where Self: Codable & NSSecureCoding {\n    static var bridge: Defaults.CodableNSSecureCodingBridge<Self> { Defaults.CodableNSSecureCodingBridge() }\n}\n\npublic extension Defaults.Serializable where Self: Codable & NSSecureCoding & Defaults.PreferNSSecureCoding {\n    static var bridge: Defaults.NSSecureCodingBridge<Self> { Defaults.NSSecureCodingBridge() }\n}\n\npublic extension Defaults.Serializable where Self: Codable & RawRepresentable {\n    static var bridge: Defaults.RawRepresentableCodableBridge<Self> { Defaults.RawRepresentableCodableBridge() }\n}\n\npublic extension Defaults.Serializable where Self: Codable & RawRepresentable & Defaults.PreferRawRepresentable {\n    static var bridge: Defaults.RawRepresentableBridge<Self> { Defaults.RawRepresentableBridge() }\n}\n\npublic extension Defaults.Serializable where Self: RawRepresentable {\n    static var bridge: Defaults.RawRepresentableBridge<Self> { Defaults.RawRepresentableBridge() }\n}\n\npublic extension Defaults.Serializable where Self: NSSecureCoding {\n    static var bridge: Defaults.NSSecureCodingBridge<Self> { Defaults.NSSecureCodingBridge() }\n}\n\npublic extension Defaults.CollectionSerializable where Element: Defaults.Serializable {\n    static var bridge: Defaults.CollectionBridge<Self> { Defaults.CollectionBridge() }\n}\n\npublic extension Defaults.SetAlgebraSerializable where Element: Defaults.Serializable & Hashable {\n    static var bridge: Defaults.SetAlgebraBridge<Self> { Defaults.SetAlgebraBridge() }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Defaults.swift",
    "content": "//\n//  Defaults.swift\n//  PaimonMenuBar\n//\n//  Created by Breezewish on 2022/4/30.\n//\n\nimport Defaults\nimport Foundation\n\nextension Defaults.Keys {\n    static let uid = Key<String>(\"uid\", default: \"\")\n\n    static let server = Key<GenshinServer>(\"server\", default: .cn_gf01)\n\n    static let cookie = Key<String>(\"cookie\", default: \"\")\n\n    // render the icon in the status menu view as template (white icon) or original (colored icon)\n    static let isStatusIconTemplate = Key<Bool>(\"is_status_icon_template\", default: true)\n\n    // whether or not to render the text next to the resin icon\n    static let isShowResinText = Key<Bool>(\"is_show_resin_text\", default: true)\n\n    // notify on parametric transformer ready\n    static let isNotifyParametricReady = Key<Bool>(\"is_notify_parametric_ready\", default: true)\n\n    // store a state of whether the notification has been sent, to avoid duplicated notifications\n    static let hasNotifiedParametricReady = Key<Bool>(\"has_notified_parametric_ready\", default: false)\n\n    // update every 2 hours to prevent captchas\n    static let recordUpdateInterval = Key<Double>(\"update_interval\", default: 2)\n\n    // if the API encounters a failure (fetch failed, mostly because of the new captcha) ...\n    static let fetchFailed = Key<Bool>(\"fetch_failed\", default: false)\n\n    static let lastGameRecord = Key<GameRecord>(\"game_record\", default: GameRecord.empty)\n}\n"
  },
  {
    "path": "PaimonMenuBar/GameRecordUpdater.swift",
    "content": "//\n//  GameRecordUpdater.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/25.\n//\n\nimport Combine\nimport Defaults\nimport Foundation\nimport Network\nimport SwiftUI\nimport UserNotifications\n\nclass GameRecordUpdater {\n    static let shared = GameRecordUpdater()\n\n    /**\n     Fetch latest game record. After finished, UI will be updated accordingly.\n     */\n    func fetchGameRecordAndRenderNow() async -> GameRecord? {\n        if let data = await getGameRecord() {\n            DispatchQueue.main.async {\n                Defaults[.lastGameRecord] = data\n                Defaults[.fetchFailed] = false\n            }\n            return data\n        } else {\n            // sendLocalNotification(context: \"⚠️ Data fetch failed, check your configuration\") {}\n\n            /**\n             The new API is more likely to fail due to captcha. Instead of sending notification\n             each time fetch fails, we update the UI when we encounter the captcha instead.\n             */\n            DispatchQueue.main.async {\n                Defaults[.fetchFailed] = true\n            }\n            return nil\n        }\n    }\n\n    private var lastUpdateAt: DispatchTime = .init(uptimeNanoseconds: 0)\n    private var updateTask: Task<Void, Never>?\n\n    /**\n     Unlike fetchGameRecordAndRenderNow, this is throttle-protected so that not each call will cause an update.\n     Also it will return immediately, schedules an update in the background.\n\n     Must be called in the main thread to avoid race condition.\n     **/\n    func tryFetchGameRecordAndRender() {\n        assert(Thread.isMainThread)\n\n        guard updateTask == nil else {\n            // If there is an on-flying request, skip.\n            print(\"Fetch skipped, there is on-flying request\")\n            return\n        }\n        let now = DispatchTime.now()\n        if now.uptimeNanoseconds - lastUpdateAt.uptimeNanoseconds < 8 * 60 * UInt64(1e9) {\n            // If last request is started within 8 minutes, skip.\n            print(\"Fetch skipped, a fetch was performed recently\")\n            return\n        }\n        print(\"Try to update game record now\")\n        lastUpdateAt = now\n        updateTask = Task {\n            _ = await fetchGameRecordAndRenderNow()\n            updateTask = nil\n        }\n    }\n\n    /** Must be called in the main thread to avoid race condition. */\n    func clearGameRecord() {\n        assert(Thread.isMainThread)\n        Defaults[.lastGameRecord] = GameRecord.empty\n    }\n\n    // MARK: - Self-Update the record when network is actve\n\n    private let networkActivityMon = NWPathMonitor()\n\n    private func startNetworkActivityUpdater() {\n        assert(Thread.isMainThread)\n\n        networkActivityMon.pathUpdateHandler = { [weak self] path in\n            if path.status != .satisfied {\n                return\n            }\n            print(\"Network is active\")\n            self?.tryFetchGameRecordAndRender()\n        }\n        networkActivityMon.start(queue: DispatchQueue.main)\n    }\n\n    // MARK: - Make a request to the remote API and update the record according to the interval\n\n    private var apiUpdateTimer: Timer?\n\n    private func resetApiUpdateTimer() {\n        assert(Thread.isMainThread)\n\n        if apiUpdateTimer != nil {\n            apiUpdateTimer?.invalidate()\n        }\n        apiUpdateTimer = Timer\n            .scheduledTimer(withTimeInterval: Defaults[.recordUpdateInterval] * 3600, repeats: true) { _ in\n                print(\"Scheduled update is triggered\")\n                self.tryFetchGameRecordAndRender()\n            }\n\n        resetLocalUpdateTimer()\n    }\n\n    // MARK: - Self-Update the record according to the interval (which is 8 mins)\n\n    private var localUpdateTimer: Timer?\n\n    private func resetLocalUpdateTimer() {\n        assert(Thread.isMainThread)\n\n        if localUpdateTimer != nil {\n            localUpdateTimer?.invalidate()\n        }\n        localUpdateTimer = Timer.scheduledTimer(withTimeInterval: 8 * 60, repeats: true, block: { _ in\n            print(\"Local updater triggered.\")\n\n            guard self.updateTask == nil else {\n                // If there is an on-flying network request, then skip.\n                print(\"Local updater skipped, there is on-flying request\")\n                return\n            }\n\n            var gameRecord = Defaults[.lastGameRecord]\n            guard gameRecord.fetchAt != nil else {\n                print(\"Local updater skipped as there has never been an update from the API\")\n                return\n            }\n\n            // Update resin and recovery time\n            if gameRecord.data.current_resin < gameRecord.data.max_resin {\n                gameRecord.data.current_resin += 1\n            }\n            let resinRecoveryTime = Int(gameRecord.data.resin_recovery_time) ?? 0\n            if resinRecoveryTime > 0 {\n                let updatedTime = resinRecoveryTime - 8 * 60\n                gameRecord.data\n                    .resin_recovery_time =\n                    String(updatedTime > 0 ? updatedTime : 0)\n            }\n\n            // Update expedition and their status\n            for (index, expedition) in gameRecord.data.expeditions.enumerated() {\n                let expeditionRemainedTime = Int(expedition.remained_time) ?? 0\n                if expeditionRemainedTime > 0 {\n                    let updatedRemainedTime = expeditionRemainedTime - 8 * 60\n                    gameRecord.data.expeditions[index]\n                        .remained_time = String(updatedRemainedTime > 0 ? updatedRemainedTime : 0)\n                    if updatedRemainedTime <= 0 {\n                        gameRecord.data.expeditions[index].status = \"Finished\"\n                    }\n                }\n            }\n\n            Defaults[.lastGameRecord] = gameRecord\n        })\n    }\n\n    // MARK: - Record update at midnight to avoid today or tomorrow conflicts\n\n    private func setupDayChangeUpdater() {\n        assert(Thread.isMainThread)\n\n        NotificationCenter.default.addObserver(forName: .NSCalendarDayChanged, object: nil, queue: .main) { _ in\n            print(\"Day change (midnight) update is triggered\")\n            self.tryFetchGameRecordAndRender()\n        }\n    }\n\n    // MARK: - Notification handler\n\n    private func sendLocalNotification(context: LocalizedStringKey, completion: @escaping () -> Void) {\n        let center = UNUserNotificationCenter.current()\n\n        center.getNotificationSettings { settings in\n            guard settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional\n            else { return }\n\n            let content = UNMutableNotificationContent()\n            content.title = String.localized(context)\n            content.sound = UNNotificationSound.default\n\n            let request = UNNotificationRequest(\n                identifier: UUID().uuidString,\n                content: content,\n                trigger: UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)\n            )\n\n            center.add(request) { _ in\n                completion()\n            }\n        }\n    }\n\n    // MARK: -\n\n    private var initialized = false\n\n    init() {\n        startNetworkActivityUpdater()\n        resetApiUpdateTimer()\n//        resetLocalUpdateTimer()\n        setupDayChangeUpdater()\n\n        Defaults.observe(.recordUpdateInterval) { _ in\n            self.onRecordUpdateIntervalChanged()\n        }.tieToLifetime(of: self)\n\n        Defaults.observe(.lastGameRecord) { _ in\n            self.onGameRecordChanged()\n        }.tieToLifetime(of: self)\n\n        initialized = true\n    }\n\n    private func onGameRecordChanged() {\n        assert(Thread.isMainThread)\n\n        guard initialized else { return }\n\n        print(\"GameRecord is updated:\", Defaults[.lastGameRecord])\n        AppDelegate.shared.updateStatusBar()\n\n        // Early return if user chooses not to push notifications\n        guard Defaults[.isNotifyParametricReady] else { return }\n\n        let parametricTransformerReady = Defaults[.lastGameRecord].data.transformer.recovery_time.reached\n\n        /**\n         Check for parametric transformer ready state - push notification only on:\n         1. Parametric transformer ready\n         2. User selected to notify when parametric transformer is ready\n         3. Notification for parametric transformer ready state has not been sent\n         */\n        if parametricTransformerReady,\n           Defaults[.hasNotifiedParametricReady] == false\n        {\n            sendLocalNotification(context: \"Parametric transformer is ready\") {\n                // set .hasNotifiedParametricReady to true on notification delivery\n                Defaults[.hasNotifiedParametricReady] = true\n            }\n        }\n\n        // If parametric transformer is not ready but .hasNotifiedParametricReady is true, then reset the trigger\n        if parametricTransformerReady == false,\n           Defaults[.hasNotifiedParametricReady]\n        {\n            Defaults[.hasNotifiedParametricReady] = false\n        }\n    }\n\n    private func onRecordUpdateIntervalChanged() {\n        assert(Thread.isMainThread)\n\n        guard initialized else { return }\n\n        print(\"RecordUpdateInterval is changed: \", Defaults[.recordUpdateInterval])\n        resetApiUpdateTimer()\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>SUEnableInstallerLauncherService</key>\n\t<true/>\n\t<key>SUFeedURL</key>\n\t<string>https://raw.githubusercontent.com/spencerwooo/PaimonMenuBar/main/appcast.xml</string>\n\t<key>SUPublicEDKey</key>\n\t<string>AhJieofGr0gFEypC7KPkg3f37YZ2Pj9lo2+reSx1a20=</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "PaimonMenuBar/MenuExtrasView.swift",
    "content": "//\n//  MenuView.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/25.\n//\n\nimport Defaults\nimport Foundation\nimport Kingfisher\nimport SwiftUI\n\nclass RelativeFormatter {\n    private let df = DateFormatter()\n\n    init() {\n        df.dateStyle = DateFormatter.Style.long\n        df.timeStyle = DateFormatter.Style.short\n        df.doesRelativeDateFormatting = true\n    }\n\n    func string(time: Date) -> String {\n        return df.string(from: time)\n    }\n}\n\n/// Return the formatted time interval in a human-readable string\n/// - Parameter timeInterval: A time interval represented in seconds\n/// - Returns: A human-readable string representing the time interval\nprivate func formatTimeInterval(timeInterval: String) -> String {\n    let formatter = DateComponentsFormatter()\n    formatter.allowedUnits = [.hour, .minute]\n    formatter.unitsStyle = .abbreviated\n    return formatter.string(from: (TimeInterval(timeInterval) ?? TimeInterval(\"0\"))!) ?? \"\"\n}\n\n/// Format a date that is of 'timeInterval' seconds away from now\n/// - Parameter timeInterval: The number of seconds away from current time\n/// - Returns: A human-readable string describing the future date\nprivate func formatFutureDate(timeInterval: String) -> String {\n    let currentTime = Date()\n    let futureTime = currentTime.addingTimeInterval((TimeInterval(timeInterval) ?? TimeInterval(\"0\"))!)\n\n    if Calendar.current.isDateInToday(futureTime) {\n        return \"\\(String.localized(\"Today\")) \\(futureTime.shortenedFormatted)\"\n    }\n    if Calendar.current.isDateInTomorrow(futureTime) {\n        return \"\\(String.localized(\"Tomorrow\")) \\(futureTime.shortenedFormatted)\"\n    }\n    // This should not happen, but just in case.\n    return futureTime.defaultFormatted\n}\n\nstruct MenuExtrasView: View {\n    @Default(.lastGameRecord) private var lastGameRecord\n\n    var body: some View {\n        ZStack {\n            VStack(spacing: 8) {\n                ResinView(\n                    currentResin: lastGameRecord.data.current_resin,\n                    maxResin: lastGameRecord.data.max_resin,\n                    resinRecoveryTime: lastGameRecord.data.resin_recovery_time,\n                    fetchAt: lastGameRecord.fetchAt\n                )\n\n                ExpeditionView(\n                    expeditions: lastGameRecord.data.expeditions,\n                    maxExpeditionNum: lastGameRecord.data.max_expedition_num,\n                    currentExpeditionNum: lastGameRecord.data.current_expedition_num\n                )\n\n                DailyCommissionView(\n                    finishedTaskNum: lastGameRecord.data.finished_task_num,\n                    totalTaskNum: lastGameRecord.data.total_task_num\n                )\n\n                ExtraTaskRewardView(\n                    remainResinDiscountNum: lastGameRecord.data.remain_resin_discount_num,\n                    resinDiscountNumLimit: lastGameRecord.data.resin_discount_num_limit,\n                    isExtraTaskRewardReceived: lastGameRecord.data.is_extra_task_reward_received\n                )\n\n                HomeCoinView(\n                    currentHomeCoin: lastGameRecord.data.current_home_coin,\n                    maxHomeCoin: lastGameRecord.data.max_home_coin,\n                    homeCoinRecoveryTime: lastGameRecord.data.home_coin_recovery_time\n                )\n\n                ParametricTransformerView(transformer: lastGameRecord.data.transformer)\n            }\n            .padding([.horizontal])\n            .padding([.vertical], 8)\n            .opacity(lastGameRecord.fetchAt == nil ? 0.4 : 1.0)\n            .blur(radius: lastGameRecord.fetchAt == nil ? 4 : 0)\n\n            if lastGameRecord.fetchAt == nil {\n                VStack(spacing: 16) {\n                    Spacer()\n\n                    Image(\"KokomiSad\").resizable().frame(width: 72, height: 72)\n                    Text(\"NOT INITIALIZED\")\n                        .font(.custom(\"Avenir Next Bold\", size: 24, relativeTo: .largeTitle).italic())\n                    Text(\n                        \"Please go to _Preferences > Configuration_ and setup your in-game **_UID_**, **_server_**, and **_cookie_**.\"\n                    )\n\n                    Spacer()\n                    HStack {\n                        Label(\"Open preferences here\", systemImage: \"arrow.down\")\n                        Spacer()\n                        Link(destination: URL(string: \"https://paimon.swo.moe/#how-to-get-my-cookie\")!) {\n                            Button(\"?\") {\n                                print(\"Navigating to help page.\")\n                            }.clipShape(Circle()).shadow(radius: 1)\n                        }\n                    }\n                }\n                .padding()\n            }\n        }.onAppear {\n            GameRecordUpdater.shared.tryFetchGameRecordAndRender()\n        }\n    }\n}\n\nstruct ResinView: View {\n    @Default(.fetchFailed) private var fetchFailed\n\n    let currentResin: Int\n    let maxResin: Int\n    let resinRecoveryTime: String\n    let fetchAt: Date?\n\n    private let formatter = RelativeFormatter()\n\n    var body: some View {\n        VStack(spacing: 8) {\n            HStack {\n                Label {\n                    Text((fetchAt == nil) ? \"Not updated\" :\n                        (fetchFailed ? \"Update failed, check the official APP for captchas\" :\n                            \"Update: \\(formatter.string(time: fetchAt!))\"))\n                        .font(.caption)\n                } icon: {\n                    Image(systemName: \"circle.fill\").scaleEffect(0.4).frame(width: 5)\n                        .foregroundColor(fetchFailed ? Color.red : Color.green)\n                }.opacity(0.6)\n            }\n\n            HStack(spacing: 4) {\n                Image(\"FragileResin\")\n                    .resizable()\n                    .frame(width: 16, height: 16)\n                Text(\"Current Resin\")\n                    .font(.subheadline)\n                    .opacity(0.6)\n                Spacer()\n            }\n\n            Text(\"\\(currentResin)/\\(maxResin)\")\n                .frame(maxWidth: .infinity, alignment: .leading)\n                .font(.custom(\"Avenir Next Bold\", size: 25, relativeTo: .largeTitle).italic())\n\n            HStack {\n                Label(\"Fully replenished\", systemImage: \"moon.circle\")\n                Spacer()\n                Text(formatTimeInterval(timeInterval: resinRecoveryTime))\n                    .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n            }\n            HStack {\n                Label(\"ETA\", systemImage: \"clock\")\n                Spacer()\n                Text(formatFutureDate(timeInterval: resinRecoveryTime))\n                    .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n            }\n            Divider()\n        }\n    }\n}\n\nstruct ExpeditionView: View {\n    let expeditions: [Expeditions]\n    let maxExpeditionNum: Int\n    let currentExpeditionNum: Int\n\n    var body: some View {\n        VStack(spacing: 10) {\n            HStack {\n                Text(\"Expeditions \\(currentExpeditionNum)/\\(maxExpeditionNum)\")\n                    .font(.subheadline)\n                    .opacity(0.6)\n                Spacer()\n                Image(\"Expedition\").resizable().frame(width: 18, height: 18)\n            }\n\n            ForEach(expeditions, id: \\.self) { expedition in\n                ExpeditionItemView(\n                    status: expedition.status, avatar: expedition.avatar_side_icon,\n                    remainedTime: expedition.remained_time\n                )\n            }\n\n            Divider()\n        }\n    }\n}\n\nstruct ExpeditionItemView: View {\n    let status: String\n    let avatar: String\n    let remainedTime: String\n\n    // 20 hours in seconds\n    let totalExpeditionTime: Float = 20 * 60 * 60\n\n    var body: some View {\n        HStack {\n            KFImage.url(URL(string: avatar))\n                .resizable()\n                .placeholder { Color.gray.opacity(0.3) }\n                .clipShape(Circle())\n                .overlay(Circle().stroke(status == \"Finished\" ? Color.green : Color.gray))\n                .frame(width: 22, height: 22)\n\n            VStack(spacing: 6) {\n                HStack {\n                    Text(status == \"Finished\" ? String.localized(\"Complete\") : String.localized(\"Exploring\"))\n                    Spacer()\n                    Text(formatTimeInterval(timeInterval: remainedTime))\n                        .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n                }\n\n                ProgressView(value: totalExpeditionTime - (Float(remainedTime) ?? 0), total: totalExpeditionTime)\n                    .progressViewStyle(LinearProgressViewStyle(tint: status == \"Finished\" ?\n                            Color.green : Color(red: 0.89, green: 0.90, blue: 0.92)))\n                    .frame(height: 1).scaleEffect(x: 1, y: 0.4, anchor: .center)\n            }\n        }\n    }\n}\n\nstruct DailyCommissionView: View {\n    let finishedTaskNum: Int\n    let totalTaskNum: Int\n\n    var body: some View {\n        HStack {\n            Image(\"Commision\")\n                .resizable()\n                .frame(width: 20, height: 20, alignment: .leading)\n            Text(\"Daily commissions\")\n            Spacer()\n            Text(\"\\(totalTaskNum - finishedTaskNum) left\")\n                .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n        }\n    }\n}\n\nstruct HomeCoinView: View {\n    let currentHomeCoin: Int\n    let maxHomeCoin: Int\n    let homeCoinRecoveryTime: String\n\n    var body: some View {\n        HStack {\n            Image(\"JarOfRiches\")\n                .resizable()\n                .scaledToFit()\n                .frame(width: 20, height: 20, alignment: .center)\n            Text(\"Realm currency\")\n            Spacer()\n            Text(\"\\(currentHomeCoin)/\\(maxHomeCoin)\")\n                .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n        }\n    }\n}\n\nstruct ExtraTaskRewardView: View {\n    let remainResinDiscountNum: Int\n    let resinDiscountNumLimit: Int\n    let isExtraTaskRewardReceived: Bool\n\n    var body: some View {\n        HStack {\n            Image(\"Domain\")\n                .resizable()\n                .scaledToFit()\n                .frame(width: 20, height: 20, alignment: .center)\n            Text(\"Weekly bosses\")\n            Spacer()\n            Text(\"\\(remainResinDiscountNum) left\")\n                .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n        }\n    }\n}\n\nstruct ParametricTransformerView: View {\n    let transformer: Transformer\n\n    func formatRecoveryTime(recoveryTime: RecoveryTime) -> String {\n        if recoveryTime.reached {\n            return \"Ready\"\n        } else {\n            return recoveryTime\n                .Day != 0 ? \"\\(recoveryTime.Day) \\(String.localized(recoveryTime.Day == 1 ? \"day\" : \"days\"))\" :\n                recoveryTime\n                .Hour != 0 ? \"\\(recoveryTime.Hour) \\(String.localized(recoveryTime.Hour == 1 ? \"hour\" : \"hours\"))\"\n                : \"\\(recoveryTime.Minute) \\(String.localized(recoveryTime.Minute == 1 ? \"min\" : \"mins\"))\"\n        }\n    }\n\n    var body: some View {\n        HStack {\n            Image(\"ParametricTransformer\")\n                .resizable()\n                .scaledToFit()\n                .frame(width: 20, height: 20, alignment: .center)\n            Text(\"Parametric transformer\")\n            Spacer()\n            Text(transformer.obtained ? formatRecoveryTime(recoveryTime: transformer.recovery_time) : \"Unavailable\")\n                .font(.custom(\"Avenir Next Demi Bold\", size: 13, relativeTo: .body).italic())\n        }\n    }\n}\n\nstruct MenuView_Previews: PreviewProvider {\n    static var previews: some View {\n        MenuExtrasView()\n            .frame(width: 290.0)\n            .frame(height: 472.0)\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Networking.swift",
    "content": "//\n//  Networking.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/24.\n//\n\nimport CryptoKit\nimport Defaults\nimport Foundation\n\nextension String {\n    // MD5 hash from: https://powermanuscript.medium.com/swift-5-2-macos-md5-hash-for-some-simple-use-cases-66be9e274182\n    var MD5: String {\n        let computed = Insecure.MD5.hash(data: data(using: .utf8)!)\n        return computed.map { String(format: \"%02hhx\", $0) }.joined()\n    }\n}\n\nfunc getDS(uid: String, server: GenshinServer) -> String {\n    // Part 1: current unix timestamp\n    let timestamp = Int(Date().timeIntervalSince1970)\n\n    // Part 2: a random integer from 100,000 to 200,000\n    let randomString = Int.random(in: 100_000 ..< 200_000)\n\n    // Part 3: MD5 hash of salt\n    let salt = server.isCNServer ? \"xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs\" : \"okr4obncj8bw5a65hbnn5oo6ixjc3l9w\"\n    let sign = \"salt=\\(salt)&t=\\(timestamp)&r=\\(randomString)&b=&q=role_id=\\(uid)&server=\\(server)\".MD5\n\n    return \"\\(timestamp),\\(randomString),\\(sign)\"\n}\n\nfunc getGameRecord() async -> GameRecord? {\n    // API to query Genshin game record\n    let apiCn = \"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/dailyNote\"\n    let apiGlobal = \"https://bbs-api-os.hoyolab.com/game_record/app/genshin/api/dailyNote\"\n\n    let uid = Defaults[.uid]\n    let server = Defaults[.server]\n    let cookie = Defaults[.cookie]\n    guard !uid.isEmpty, !cookie.isEmpty else {\n        print(\"Fetch skipped, because cookie is not set\")\n        return nil\n    }\n\n    print(\"Fetching game record data...\", uid, server)\n\n    let api = server.isCNServer ? apiCn : apiGlobal\n    guard let url = URL(string: \"\\(api)?role_id=\\(uid)&server=\\(server)\") else { return nil }\n\n    // Reverse engineering Mihoyo API ;)\n    let DS = getDS(uid: uid, server: server)\n    let appVersion = server.isCNServer ? \"2.26.1\" : \"2.9.1\"\n    let clientType = server.isCNServer ? \"5\" : \"2\"\n\n    // Construct request with query parameters and relevant headers\n    var req = URLRequest(url: url)\n    req.httpMethod = \"GET\"\n\n    // Add required headers\n    req.setValue(cookie, forHTTPHeaderField: \"Cookie\")\n    req.setValue(DS, forHTTPHeaderField: \"DS\")\n    req.setValue(appVersion, forHTTPHeaderField: \"x-rpc-app_version\")\n    req.setValue(clientType, forHTTPHeaderField: \"x-rpc-client_type\")\n\n    // Perform HTTP request\n    do {\n        let (data, _) = try await URLSession.shared.data(for: req)\n//        if let string = String(bytes: data, encoding: .utf8) {\n//            print(string)\n//        }\n        var gameRecord = try? JSONDecoder().decode(GameRecord.self, from: data)\n        gameRecord?.fetchAt = Date()\n        return gameRecord\n    } catch {\n        print(\"Invalid data\")\n        return nil\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/PaimonMenuBar.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.files.user-selected.read-only</key>\n\t<true/>\n\t<key>com.apple.security.network.client</key>\n\t<true/>\n\t<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>\n\t<array>\n\t\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string>\n\t\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "PaimonMenuBar/PaimonMenuBarApp.swift",
    "content": "//\n//  PaimonMenuBarApp.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/23.\n//\n\nimport SwiftUI\n\n@main\nstruct PaimonMenuBarApp: App {\n    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate\n\n    var body: some Scene {\n        WindowGroup {\n            if false {}\n        }\n        .commands {\n            CommandGroup(after: .appInfo) {\n                CheckForUpdatesView(updaterViewModel: UpdaterViewModel.shared)\n            }\n        }\n\n        Settings {\n            SettingsView()\n        }\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/Preview Content/Preview Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "PaimonMenuBar/SettingsView.swift",
    "content": "//\n//  ContentView.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/23.\n//\n\nimport Defaults\nimport LaunchAtLogin\nimport SwiftUI\nimport UserNotifications\n\n// This additional view is needed for the disabled state on the menu item to work properly before Monterey.\n// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more information\nstruct CheckForUpdatesView: View {\n    @ObservedObject var updaterViewModel: UpdaterViewModel\n\n    var body: some View {\n        Button(\"Check for Updates\", action: updaterViewModel.checkForUpdates)\n            .disabled(!updaterViewModel.canCheckForUpdates)\n    }\n}\n\nfunc getNotificationPermission(completion: @escaping (Bool) -> Void) {\n    UNUserNotificationCenter.current().getNotificationSettings { settings in\n        completion(settings.authorizationStatus == .authorized || settings.authorizationStatus == .provisional)\n    }\n}\n\nstruct PreferenceSettingsView: View {\n    @Default(.recordUpdateInterval) private var recordUpdateInterval\n    @Default(.isStatusIconTemplate) private var isStatusIconTemplate\n    @Default(.isShowResinText) private var isShowResinText\n    @Default(.isNotifyParametricReady) private var isNotifyParametricReady\n    @Default(.lastGameRecord) private var lastGameRecord\n\n    @StateObject var updaterViewModel = UpdaterViewModel.shared\n\n    @State private var isEditing = false\n    @State private var isNotNotificationAuthorized = false\n\n    var body: some View {\n        VStack(spacing: 20) {\n            Form {\n                LaunchAtLogin.Toggle {\n                    Text(\"Launch at Login\")\n                }\n                .formLabel(Text(\"Start:\"))\n\n                CheckForUpdatesView(updaterViewModel: updaterViewModel)\n                    .formLabel(Text(\"Updates:\"))\n                Text(\"Current version: \\(Bundle.main.appVersion ?? \"\") (\\(Bundle.main.buildNumber ?? \"\"))\")\n                    .font(.caption).opacity(0.6)\n\n                HStack {\n                    Defaults.Toggle(key: .isStatusIconTemplate) {\n                        Image(\"FragileResin\")\n                            .renderingMode(isStatusIconTemplate ? .template : .original)\n                            .frame(width: 19, height: 19)\n                    }\n                    .onChange { _ in\n                        AppDelegate.shared.updateStatusIcon()\n                    }\n\n                    Defaults.Toggle(key: .isShowResinText) {\n                        Text(\"\\(lastGameRecord.data.current_resin)/\\(lastGameRecord.data.max_resin)\")\n                            .opacity(isShowResinText ? 1 : 0.4)\n                    }\n                    .onChange { _ in\n                        AppDelegate.shared.updateStatusButtonTitle()\n                    }\n                }\n                .formLabel(Text(\"Menubar icon:\"))\n\n                HStack(spacing: 2) {\n                    Text(isStatusIconTemplate ? \"Native macOS adaptive icon.\" : \"Colored icon.\").font(.caption)\n                        .opacity(0.6)\n                    Text(isShowResinText ? \"With resin counter.\" : \"Resin counter hidden.\").font(.caption)\n                        .opacity(0.6)\n                }\n\n                Defaults.Toggle(key: .isNotifyParametricReady) {\n                    Image(\n                        systemName: isNotifyParametricReady ? \"bell.badge\" : \"bell.slash\"\n                    )\n                    if isNotNotificationAuthorized {\n                        Label(\"⚠️ Notification unauthorized.\", systemImage: \"arrow.left\")\n                            .font(.caption2).opacity(0.8)\n                    }\n                }\n                .formLabel(Text(\"Notify:\"))\n                .onAppear {\n                    getNotificationPermission { authorized in\n                        isNotNotificationAuthorized = !authorized\n                    }\n                }\n                .onChange(of: isNotifyParametricReady) { _ in\n                    getNotificationPermission { authorized in\n                        isNotNotificationAuthorized = !authorized\n                    }\n                }\n                Text(\"... when parametric transformer is ready.\").font(.caption).opacity(0.6)\n\n                Slider(value: $recordUpdateInterval, in: 1 ... 6, step: 1, label: {\n                    Text(\"Update interval:\")\n                }) { editing in\n                    isEditing = editing\n                }\n                .frame(width: 360)\n\n                Text(\"Paimon fetches data every \\(recordUpdateInterval, specifier: \"%.0f\") hour(s)*\")\n                    .font(.caption).opacity(0.6)\n            }\n\n            Divider()\n\n            Label(\n                \"*Updating every 1-3 hours is sufficient, to prevent from being captcha-ed. Don't worry, as Paimon will auto-update the data offline as time passes.\",\n                image: \"FragileResin\"\n            )\n            .font(.caption).opacity(0.6).frame(width: 400)\n        }\n    }\n}\n\nstruct ConfigurationSettingsView: View {\n    @Default(.uid) private var uid\n    @Default(.server) private var server\n    @Default(.cookie) private var cookie\n\n    @State private var alertText = \"\"\n    @State private var alertMessage = \"\"\n    @State private var showConfigValidAlert = false\n\n    @State private var isLoading = false\n\n    var body: some View {\n        VStack {\n            Text(\"User\")\n                .font(.headline)\n                .frame(maxWidth: .infinity, alignment: .leading)\n            Form {\n                TextField(\"UID:\", text: $uid)\n                    .textFieldStyle(.roundedBorder)\n                Picker(\"Server:\", selection: $server) {\n                    ForEach(GenshinServer.allCases, id: \\.id) { value in\n                        Text(value.serverName).tag(value)\n                    }\n                }.pickerStyle(SegmentedPickerStyle())\n            }.padding([.bottom])\n\n            Text(\"Cookie\")\n                .font(.headline)\n                .frame(maxWidth: .infinity, alignment: .leading)\n            HStack {\n                Text(\"Paste your cookie from:\")\n                    .font(.subheadline)\n                Link(destination: URL(string: server.cookieSiteUrl)!) {\n                    Text(server.cookieSiteUrl)\n                        .font(.subheadline)\n                }\n            }\n            .frame(maxWidth: .infinity, alignment: .leading)\n            TextEditor(text: $cookie)\n                .font(.system(.body, design: .monospaced))\n                .frame(height: 120).cornerRadius(6)\n                .background(Color.black.cornerRadius(6).shadow(radius: 0.5, y: 0.8).opacity(0.6))\n\n            Spacer()\n\n            HStack {\n                Label(\"This cookie is only stored locally.\", systemImage: \"exclamationmark.circle\")\n                    .font(.caption).opacity(0.6)\n                Spacer()\n                Button {\n                    GameRecordUpdater.shared.clearGameRecord()\n                    Task {\n                        isLoading = true\n                        if let _ = await GameRecordUpdater.shared.fetchGameRecordAndRenderNow() {\n                            self.alertText = String.localized(\"👌 It's working!\")\n                            self.alertMessage = String.localized(\"Your config is valid.\")\n                        } else {\n                            self.alertText = String.localized(\"🚫 Whoooops...\")\n                            self.alertMessage = String.localized(\"Failed to fetch, check your config.\")\n                        }\n                        self.showConfigValidAlert.toggle()\n                        isLoading = false\n                    }\n                } label: {\n                    Label {\n                        Text(\"Test config\")\n                    } icon: {\n                        Image(systemName: \"bolt\")\n                            .opacity(isLoading ? 0 : 1)\n                            .overlay(isLoading ? ProgressView().scaleEffect(0.4) : nil)\n                    }\n                }\n                .alert(isPresented: self.$showConfigValidAlert, content: {\n                    Alert(title: Text(alertText), message: Text(alertMessage))\n                })\n                .disabled(isLoading)\n\n                Link(destination: URL(string: \"https://paimon.swo.moe/#how-to-get-my-cookie\")!) {\n                    Button(\"?\") {\n                        print(\"Navigating to help page.\")\n                    }.clipShape(Circle()).shadow(radius: 1)\n                }\n            }\n\n        }.padding()\n    }\n}\n\nstruct AboutSettingsView: View {\n    var body: some View {\n        VStack(spacing: 8) {\n            Image(nsImage: NSImage(named: \"AppIcon\") ?? NSImage())\n            Text(Bundle.main.appName ?? \"\").font(.headline.bold())\n            Text(\"Build \\(Bundle.main.appVersion ?? \"\") (\\(Bundle.main.buildNumber ?? \"\"))\")\n                .font(.system(.subheadline, design: .monospaced))\n\n            Divider()\n\n            Text(\n                \"Made with love @ [SpencerWoo](https://spencerwoo.com) | Check [Paimon's website](https://paimon.swo.moe)\"\n            )\n            .font(.system(.caption, design: .monospaced))\n            Text(\n                \"Icon by [Chawong](https://www.pixiv.net/en/artworks/92415888) | GitHub: [spencerwooo/PaimonMenuBar](https://github.com/spencerwooo/PaimonMenuBar)\"\n            )\n            .font(.system(.caption, design: .monospaced))\n        }\n    }\n}\n\nstruct SettingsView: View {\n    var body: some View {\n        TabView {\n            PreferenceSettingsView()\n                .tabItem {\n                    Label(\"Preferences\", systemImage: \"gear\")\n                }\n            ConfigurationSettingsView()\n                .tabItem {\n                    Label(\"Configuration\", systemImage: \"square.and.pencil\")\n                }\n            AboutSettingsView()\n                .tabItem {\n                    Label(\"About\", systemImage: \"person\")\n                }\n        }\n        .frame(width: 560, height: 360)\n    }\n}\n\n/// Alignment guide for aligning a text field in a `Form`.\n/// Thanks for Jim Dovey  https://developer.apple.com/forums/thread/126268\nextension HorizontalAlignment {\n    private enum ControlAlignment: AlignmentID {\n        static func defaultValue(in context: ViewDimensions) -> CGFloat {\n            return context[HorizontalAlignment.center]\n        }\n    }\n\n    static let controlAlignment = HorizontalAlignment(ControlAlignment.self)\n}\n\n// Adapted from https://gist.github.com/marcprux/afd2f80baa5b6d60865182a828e83586\npublic extension View {\n    /// Attaches a label to this view for laying out in a `Form`\n    /// - Parameter view: the label view to use\n    /// - Returns: an `HStack` with an alignment guide for placing in a form\n    func formLabel<V: View>(_ view: V) -> some View {\n        HStack {\n            view\n            self\n                .alignmentGuide(.controlAlignment) { $0[.leading] }\n        }\n        .alignmentGuide(.leading) { $0[.controlAlignment] }\n    }\n}\n\nstruct SettingsView_Previews: PreviewProvider {\n    static var previews: some View {\n        Group {\n            SettingsView()\n            PreferenceSettingsView()\n            ConfigurationSettingsView()\n            AboutSettingsView()\n        }\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/UpdaterViewModel.swift",
    "content": "//\n//  UpdaterViewModel.swift\n//  PaimonMenuBar\n//\n//  Created by Spencer Woo on 2022/3/31.\n//\n\nimport Foundation\nimport Sparkle\n\nfinal class UpdaterViewModel: ObservableObject {\n    private let updaterController: SPUStandardUpdaterController\n    static let shared = UpdaterViewModel()\n\n    @Published var canCheckForUpdates = false\n\n    init() {\n        updaterController = SPUStandardUpdaterController(\n            startingUpdater: true,\n            updaterDelegate: nil,\n            userDriverDelegate: nil\n        )\n\n        updaterController.updater.publisher(for: \\.canCheckForUpdates)\n            .assign(to: &$canCheckForUpdates)\n    }\n\n    func checkForUpdates() {\n        updaterController.checkForUpdates(nil)\n    }\n}\n"
  },
  {
    "path": "PaimonMenuBar/en.lproj/Localizable.strings",
    "content": "\n"
  },
  {
    "path": "PaimonMenuBar/zh-Hans.lproj/Localizable.strings",
    "content": "\"*Updating every 1-3 hours is sufficient, to prevent from being captcha-ed. Don't worry, as Paimon will auto-update the data offline as time passes.\" = \"* 为了避免触发验证码，建议最短间隔 1 至 3 小时更新一次数据。不必担心，派蒙会自动在本地随着时间流逝更新数据。\";\n\n\"About\" = \"关于\";\n\n\"Check for Updates\" = \"检查更新\";\n\n\"Complete\" = \"完成\";\n\n\"Configuration\" = \"配置信息\";\n\n\"Current Resin\" = \"当前树脂\";\n\n\"Current version: %@ (%@)\" = \"当前版本：%@ (%@)\";\n\n\"Daily commissions\" = \"每日委托\";\n\n\"ETA\" = \"全部恢复于\";\n\n\"Expeditions %lld/%lld\" = \"探索派遣 %lld/%lld\";\n\n\"Fully replenished\" = \"距离全部恢复\";\n\n\"Launch at Login\" = \"登录时启动\";\n\n\"Paimon fetches data every %.0f hour(s)*\" = \"派蒙每隔 %.0f 小时更新一次数据 *\";\n\n\"Paste your cookie from:\" = \"从这里粘贴你的 Cookie：\";\n\n\"User\" = \"个人信息\";\n\n\"Preferences\" = \"偏好设置\";\n\n\"Realm currency\" = \"洞天宝钱\";\n\n\"UID:\" = \"游戏内 UID：\";\n\n\"Server:\" = \"服务器：\";\n\n\"Start:\" = \"启动：\";\n\n\"Test config\" = \"测试配置\";\n\n\"Update interval:\" = \"更新频率：\";\n\n\"Updates:\" = \"更新：\";\n\n\"Check for Updates\" = \"检查更新\";\n\n\"Tomorrow\" = \"明日\";\n\n\"Today\" = \"今日\";\n\n\"Exploring\" = \"剩余探索时间\";\n\n\"Complete\" = \"探索完成\";\n\n\"Weekly bosses\" = \"半价周本\";\n\n\"Quit\" = \"退出\";\n\n\"👌 It's working!\" = \"👌 成功！\";\n\n\"Your config is valid.\" = \"你的配置信息正确无误。\";\n\n\"🚫 Whoooops...\" = \"🚫 啊哦……\";\n\n\"Failed to fetch, check your config.\" = \"数据获取失败，请检查配置。\";\n\n\"Parametric transformer\" = \"参量质变仪\";\n\n\"Update: %@\" = \"更新时间：%@\";\n\n\"Not updated\" = \"未更新\";\n\n\"Update failed, check the official APP for captchas\" = \"更新失败：检查官方应用中是否触发验证码\";\n\n\"Menubar icon:\" = \"菜单栏图标：\";\n\n\"Native macOS adaptive icon.\" = \"原生 macOS 自适应图标。\";\n\n\"Colored icon.\" = \"彩色图标。\";\n\n\"With resin counter.\" = \"显示树脂。\";\n\n\"Resin counter hidden.\" = \"树脂隐藏。\";\n\n\"%@ left\" = \"剩 %@ 个\";\n\n\"Ready\" = \"冷却完成\";\n\n\"Unavailable\" = \"未解锁\";\n\n\"day\" = \"天\";\n\n\"days\" = \"天\";\n\n\"hour\" = \"时\";\n\n\"hours\" = \"时\";\n\n\"min\" = \"分\";\n\n\"mins\" = \"分\";\n\n\"NOT INITIALIZED\" = \"尚未配置\";\n\n\"Please go to _Preferences > Configuration_ and setup your in-game **_UID_**, **_server_**, and **_cookie_**.\" = \"请前往「偏好设置 > 配置信息」设置你的游戏内 **_UID_**、**_服务器_**、以及 **_米游社 cookie_**.\";\n\n\"Open preferences here\" = \"在这里打开偏好设置\";\n\n\"Parametric transformer is ready\" = \"参量质变仪已就绪\";\n\n\"Notify:\" = \"通知：\";\n\n\"... when parametric transformer is ready.\" = \"… 当参量质变仪就绪时。\";\n\n\"⚠️ Data fetch failed, check your configuration\" = \"⚠️ 数据获取失败，检查你的配置信息\";\n\n\"⚠️ Notification unauthorized.\" = \"⚠️ 没有推送通知权限。\";\n\n\"This cookie is only stored locally.\" = \"Cookie 仅保存于本地\";\n"
  },
  {
    "path": "PaimonMenuBar.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 55;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t320C466128254F8700C6932E /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 320C466028254F8700C6932E /* Kingfisher */; };\n\t\t323850E1282540EE0097B5C2 /* Compatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 323850E0282540EE0097B5C2 /* Compatibility.swift */; };\n\t\t76085E6127FC23B000960915 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 76085E6027FC23B000960915 /* LaunchAtLogin */; };\n\t\t76085E6427FC23EA00960915 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 76085E6327FC23EA00960915 /* Sparkle */; };\n\t\t7621675327F2FC080023F8B2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7621675527F2FC080023F8B2 /* Localizable.strings */; };\n\t\t7686474127EF082400BCC350 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7686474027EF082400BCC350 /* Bundle.swift */; };\n\t\t76C290F027EAFFB000A30C9F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76C290EF27EAFFB000A30C9F /* AppDelegate.swift */; };\n\t\t76CCDDDE27EAD1C4009CFC64 /* PaimonMenuBarApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76CCDDDD27EAD1C4009CFC64 /* PaimonMenuBarApp.swift */; };\n\t\t76CCDDE027EAD1C4009CFC64 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76CCDDDF27EAD1C4009CFC64 /* SettingsView.swift */; };\n\t\t76CCDDE227EAD1C5009CFC64 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76CCDDE127EAD1C5009CFC64 /* Assets.xcassets */; };\n\t\t76CCDDE527EAD1C5009CFC64 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 76CCDDE427EAD1C5009CFC64 /* Preview Assets.xcassets */; };\n\t\t76D73BBF27EC650500CCDEA6 /* DataModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D73BBE27EC650500CCDEA6 /* DataModels.swift */; };\n\t\t76D73BC127EC67D300CCDEA6 /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76D73BC027EC67D300CCDEA6 /* Networking.swift */; };\n\t\t76E429A927EDDE000032313C /* GameRecordUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76E429A827EDDE000032313C /* GameRecordUpdater.swift */; };\n\t\t76E986B627EDD5FC004ECC6C /* MenuExtrasView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76E986B527EDD5FC004ECC6C /* MenuExtrasView.swift */; };\n\t\t76F9AE6D27F570D90051CDC8 /* UpdaterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76F9AE6C27F570D90051CDC8 /* UpdaterViewModel.swift */; };\n\t\t9F38F96F281D1BE90004D240 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F38F96E281D1BE90004D240 /* Defaults.swift */; };\n\t\t9F38F972281D4D000004D240 /* Defaults in Frameworks */ = {isa = PBXBuildFile; productRef = 9F38F971281D4D000004D240 /* Defaults */; };\n\t\t9F38F974281D4D120004D240 /* Defaults+Workaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F38F973281D4D120004D240 /* Defaults+Workaround.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t323850E0282540EE0097B5C2 /* Compatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Compatibility.swift; sourceTree = \"<group>\"; };\n\t\t7621675427F2FC080023F8B2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = \"<group>\"; };\n\t\t7621675627F2FC0B0023F8B2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = \"zh-Hans\"; path = \"zh-Hans.lproj/Localizable.strings\"; sourceTree = \"<group>\"; };\n\t\t7686474027EF082400BCC350 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = \"<group>\"; };\n\t\t76B3F03127F2B76100833555 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = \"<group>\"; };\n\t\t76C290EF27EAFFB000A30C9F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t76CCDDDD27EAD1C4009CFC64 /* PaimonMenuBarApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaimonMenuBarApp.swift; sourceTree = \"<group>\"; };\n\t\t76CCDDDF27EAD1C4009CFC64 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = \"<group>\"; };\n\t\t76CCDDE127EAD1C5009CFC64 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t76CCDDE427EAD1C5009CFC64 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = \"Preview Assets.xcassets\"; sourceTree = \"<group>\"; };\n\t\t76CCDDE627EAD1C5009CFC64 /* PaimonMenuBar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PaimonMenuBar.entitlements; sourceTree = \"<group>\"; };\n\t\t76D73BBE27EC650500CCDEA6 /* DataModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModels.swift; sourceTree = \"<group>\"; };\n\t\t76D73BC027EC67D300CCDEA6 /* Networking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = \"<group>\"; };\n\t\t76E429A827EDDE000032313C /* GameRecordUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameRecordUpdater.swift; sourceTree = \"<group>\"; };\n\t\t76E986B527EDD5FC004ECC6C /* MenuExtrasView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuExtrasView.swift; sourceTree = \"<group>\"; };\n\t\t76F9AE6B27F570640051CDC8 /* PaimonMenuBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PaimonMenuBar.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t76F9AE6C27F570D90051CDC8 /* UpdaterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdaterViewModel.swift; sourceTree = \"<group>\"; };\n\t\t9F38F96E281D1BE90004D240 /* Defaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Defaults.swift; sourceTree = \"<group>\"; };\n\t\t9F38F973281D4D120004D240 /* Defaults+Workaround.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Defaults+Workaround.swift\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t76CCDDD727EAD1C4009CFC64 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t320C466128254F8700C6932E /* Kingfisher in Frameworks */,\n\t\t\t\t9F38F972281D4D000004D240 /* Defaults in Frameworks */,\n\t\t\t\t76085E6127FC23B000960915 /* LaunchAtLogin in Frameworks */,\n\t\t\t\t76085E6427FC23EA00960915 /* Sparkle in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t76CCDDD127EAD1C4009CFC64 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t76B3F03127F2B76100833555 /* README.md */,\n\t\t\t\t76CCDDDC27EAD1C4009CFC64 /* PaimonMenuBar */,\n\t\t\t\t76F9AE6B27F570640051CDC8 /* PaimonMenuBar.app */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t76CCDDDC27EAD1C4009CFC64 /* PaimonMenuBar */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7621675527F2FC080023F8B2 /* Localizable.strings */,\n\t\t\t\t76CCDDDD27EAD1C4009CFC64 /* PaimonMenuBarApp.swift */,\n\t\t\t\t76E986B527EDD5FC004ECC6C /* MenuExtrasView.swift */,\n\t\t\t\t76E429A827EDDE000032313C /* GameRecordUpdater.swift */,\n\t\t\t\t76CCDDDF27EAD1C4009CFC64 /* SettingsView.swift */,\n\t\t\t\t76F9AE6C27F570D90051CDC8 /* UpdaterViewModel.swift */,\n\t\t\t\t9F38F96E281D1BE90004D240 /* Defaults.swift */,\n\t\t\t\t7686474027EF082400BCC350 /* Bundle.swift */,\n\t\t\t\t76CCDDE127EAD1C5009CFC64 /* Assets.xcassets */,\n\t\t\t\t76CCDDE627EAD1C5009CFC64 /* PaimonMenuBar.entitlements */,\n\t\t\t\t76CCDDE327EAD1C5009CFC64 /* Preview Content */,\n\t\t\t\t76C290EF27EAFFB000A30C9F /* AppDelegate.swift */,\n\t\t\t\t76D73BBE27EC650500CCDEA6 /* DataModels.swift */,\n\t\t\t\t76D73BC027EC67D300CCDEA6 /* Networking.swift */,\n\t\t\t\t9F38F973281D4D120004D240 /* Defaults+Workaround.swift */,\n\t\t\t\t323850E0282540EE0097B5C2 /* Compatibility.swift */,\n\t\t\t);\n\t\t\tpath = PaimonMenuBar;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t76CCDDE327EAD1C5009CFC64 /* Preview Content */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t76CCDDE427EAD1C5009CFC64 /* Preview Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = \"Preview Content\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t76CCDDD927EAD1C4009CFC64 /* PaimonMenuBar */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 76CCDDE927EAD1C5009CFC64 /* Build configuration list for PBXNativeTarget \"PaimonMenuBar\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t76CCDDD627EAD1C4009CFC64 /* Sources */,\n\t\t\t\t76CCDDD727EAD1C4009CFC64 /* Frameworks */,\n\t\t\t\t76CCDDD827EAD1C4009CFC64 /* Resources */,\n\t\t\t\t7686473F27EEED4500BCC350 /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = PaimonMenuBar;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t76085E6027FC23B000960915 /* LaunchAtLogin */,\n\t\t\t\t76085E6327FC23EA00960915 /* Sparkle */,\n\t\t\t\t9F38F971281D4D000004D240 /* Defaults */,\n\t\t\t\t320C466028254F8700C6932E /* Kingfisher */,\n\t\t\t);\n\t\t\tproductName = PaimonMenuBar;\n\t\t\tproductReference = 76F9AE6B27F570640051CDC8 /* PaimonMenuBar.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t76CCDDD227EAD1C4009CFC64 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = 1;\n\t\t\t\tLastSwiftUpdateCheck = 1330;\n\t\t\t\tLastUpgradeCheck = 1410;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t76CCDDD927EAD1C4009CFC64 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.3;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 76CCDDD527EAD1C4009CFC64 /* Build configuration list for PBXProject \"PaimonMenuBar\" */;\n\t\t\tcompatibilityVersion = \"Xcode 13.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t\t\"zh-Hans\",\n\t\t\t);\n\t\t\tmainGroup = 76CCDDD127EAD1C4009CFC64;\n\t\t\tpackageReferences = (\n\t\t\t\t76085E5F27FC23B000960915 /* XCRemoteSwiftPackageReference \"LaunchAtLogin\" */,\n\t\t\t\t76085E6227FC23EA00960915 /* XCRemoteSwiftPackageReference \"Sparkle\" */,\n\t\t\t\t9F38F970281D4D000004D240 /* XCRemoteSwiftPackageReference \"Defaults\" */,\n\t\t\t\t320C465F28254F8700C6932E /* XCRemoteSwiftPackageReference \"Kingfisher\" */,\n\t\t\t);\n\t\t\tproductRefGroup = 76CCDDD127EAD1C4009CFC64;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t76CCDDD927EAD1C4009CFC64 /* PaimonMenuBar */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t76CCDDD827EAD1C4009CFC64 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t76CCDDE527EAD1C5009CFC64 /* Preview Assets.xcassets in Resources */,\n\t\t\t\t7621675327F2FC080023F8B2 /* Localizable.strings in Resources */,\n\t\t\t\t76CCDDE227EAD1C5009CFC64 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t7686473F27EEED4500BCC350 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\\\"\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t76CCDDD627EAD1C4009CFC64 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t76E986B627EDD5FC004ECC6C /* MenuExtrasView.swift in Sources */,\n\t\t\t\t7686474127EF082400BCC350 /* Bundle.swift in Sources */,\n\t\t\t\t76E429A927EDDE000032313C /* GameRecordUpdater.swift in Sources */,\n\t\t\t\t9F38F974281D4D120004D240 /* Defaults+Workaround.swift in Sources */,\n\t\t\t\t323850E1282540EE0097B5C2 /* Compatibility.swift in Sources */,\n\t\t\t\t76D73BBF27EC650500CCDEA6 /* DataModels.swift in Sources */,\n\t\t\t\t76C290F027EAFFB000A30C9F /* AppDelegate.swift in Sources */,\n\t\t\t\t76D73BC127EC67D300CCDEA6 /* Networking.swift in Sources */,\n\t\t\t\t76F9AE6D27F570D90051CDC8 /* UpdaterViewModel.swift in Sources */,\n\t\t\t\t9F38F96F281D1BE90004D240 /* Defaults.swift in Sources */,\n\t\t\t\t76CCDDE027EAD1C4009CFC64 /* SettingsView.swift in Sources */,\n\t\t\t\t76CCDDDE27EAD1C4009CFC64 /* PaimonMenuBarApp.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t7621675527F2FC080023F8B2 /* Localizable.strings */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t7621675427F2FC080023F8B2 /* en */,\n\t\t\t\t7621675627F2FC0B0023F8B2 /* zh-Hans */,\n\t\t\t);\n\t\t\tname = Localizable.strings;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t76CCDDE727EAD1C5009CFC64 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 12.2;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t76CCDDE827EAD1C5009CFC64 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 12.2;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t76CCDDEA27EAD1C5009CFC64 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = PaimonMenuBar/PaimonMenuBar.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 128;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"PaimonMenuBar/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = W2HGAU9MPP;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = PaimonMenuBar/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.entertainment\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMARKETING_VERSION = 1.12;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = spencerwoo.PaimonMenuBar;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t76CCDDEB27EAD1C5009CFC64 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = PaimonMenuBar/PaimonMenuBar.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 128;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"PaimonMenuBar/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = W2HGAU9MPP;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = PaimonMenuBar/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.entertainment\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMARKETING_VERSION = 1.12;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = spencerwoo.PaimonMenuBar;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t76CCDDD527EAD1C4009CFC64 /* Build configuration list for PBXProject \"PaimonMenuBar\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t76CCDDE727EAD1C5009CFC64 /* Debug */,\n\t\t\t\t76CCDDE827EAD1C5009CFC64 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t76CCDDE927EAD1C5009CFC64 /* Build configuration list for PBXNativeTarget \"PaimonMenuBar\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t76CCDDEA27EAD1C5009CFC64 /* Debug */,\n\t\t\t\t76CCDDEB27EAD1C5009CFC64 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\t320C465F28254F8700C6932E /* XCRemoteSwiftPackageReference \"Kingfisher\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/onevcat/Kingfisher.git\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 7.0.0;\n\t\t\t};\n\t\t};\n\t\t76085E5F27FC23B000960915 /* XCRemoteSwiftPackageReference \"LaunchAtLogin\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sindresorhus/LaunchAtLogin\";\n\t\t\trequirement = {\n\t\t\t\tbranch = main;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n\t\t76085E6227FC23EA00960915 /* XCRemoteSwiftPackageReference \"Sparkle\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sparkle-project/Sparkle\";\n\t\t\trequirement = {\n\t\t\t\tbranch = 2.x;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n\t\t9F38F970281D4D000004D240 /* XCRemoteSwiftPackageReference \"Defaults\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sindresorhus/Defaults\";\n\t\t\trequirement = {\n\t\t\t\tbranch = main;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\t320C466028254F8700C6932E /* Kingfisher */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 320C465F28254F8700C6932E /* XCRemoteSwiftPackageReference \"Kingfisher\" */;\n\t\t\tproductName = Kingfisher;\n\t\t};\n\t\t76085E6027FC23B000960915 /* LaunchAtLogin */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 76085E5F27FC23B000960915 /* XCRemoteSwiftPackageReference \"LaunchAtLogin\" */;\n\t\t\tproductName = LaunchAtLogin;\n\t\t};\n\t\t76085E6327FC23EA00960915 /* Sparkle */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 76085E6227FC23EA00960915 /* XCRemoteSwiftPackageReference \"Sparkle\" */;\n\t\t\tproductName = Sparkle;\n\t\t};\n\t\t9F38F971281D4D000004D240 /* Defaults */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 9F38F970281D4D000004D240 /* XCRemoteSwiftPackageReference \"Defaults\" */;\n\t\t\tproductName = Defaults;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = 76CCDDD227EAD1C4009CFC64 /* Project object */;\n}\n"
  },
  {
    "path": "PaimonMenuBar.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "PaimonMenuBar.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "PaimonMenuBar.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "PaimonMenuBar.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"pins\" : [\n    {\n      \"identity\" : \"defaults\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sindresorhus/Defaults\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"3535f3d088113cf24705014eec6a17f0fd73237f\"\n      }\n    },\n    {\n      \"identity\" : \"kingfisher\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/onevcat/Kingfisher.git\",\n      \"state\" : {\n        \"revision\" : \"59eb199fdb8dd47733624e8b0277822d0232579e\",\n        \"version\" : \"7.2.2\"\n      }\n    },\n    {\n      \"identity\" : \"launchatlogin\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sindresorhus/LaunchAtLogin\",\n      \"state\" : {\n        \"branch\" : \"main\",\n        \"revision\" : \"e8171b3e38a2816f579f58f3dac1522aa39efe41\"\n      }\n    },\n    {\n      \"identity\" : \"sparkle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sparkle-project/Sparkle\",\n      \"state\" : {\n        \"branch\" : \"2.x\",\n        \"revision\" : \"f250bead4b943ef9711c61274a1f52e380afa0e8\"\n      }\n    }\n  ],\n  \"version\" : 2\n}\n"
  },
  {
    "path": "PaimonMenuBar.xcodeproj/xcshareddata/xcschemes/PaimonMenuBar.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1410\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"76CCDDD927EAD1C4009CFC64\"\n               BuildableName = \"PaimonMenuBar.app\"\n               BlueprintName = \"PaimonMenuBar\"\n               ReferencedContainer = \"container:PaimonMenuBar.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"76CCDDD927EAD1C4009CFC64\"\n            BuildableName = \"PaimonMenuBar.app\"\n            BlueprintName = \"PaimonMenuBar\"\n            ReferencedContainer = \"container:PaimonMenuBar.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"76CCDDD927EAD1C4009CFC64\"\n            BuildableName = \"PaimonMenuBar.app\"\n            BlueprintName = \"PaimonMenuBar\"\n            ReferencedContainer = \"container:PaimonMenuBar.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <img src=\"Assets/logo.png\" alt=\"logo\" width=\"160\" height=\"160\" />\n  <h3><code>PaimonMenuBar</code></h3>\n  <p><em>Track real-time Genshin Impact stats in your macOS menubar</em></p>\n\n  <img src=\"https://img.shields.io/badge/uses-SwiftUI-f05138?labelColor=282c34&logo=swift\" alt=\"Use Swift\" />\n  <img src=\"https://img.shields.io/badge/macOS-11.0+-f05138?labelColor=282c34&logo=apple\" alt=\"macOS 11.0+\" />\n  <a href=\"https://github.com/spencerwooo/PaimonMenuBar/releases/latest\"><img src=\"https://img.shields.io/github/v/release/spencerwooo/PaimonMenuBar?labelColor=282c34&logo=GitHub\" alt=\"GitHub Release\" /></a>\n</div>\n\n## What's this?\n\n![screenshot](Assets/screenshot.jpg)\n\n> Paimon helps you track your Genshin Impact daily resin, expeditions, and more — straight in your macOS menu bar.\n\nPaimon can help you —\n\n* 🌙 Keep track of your daily resin.\n* 💰 Monitor your daily expeditions and real-time realm currency.\n* 🏁 Remind you about your daily commissions and weekly boss fights.\n* 🍯 And notify you when your parametric transformer is ready to use.\n\nBasically, `PaimonMenuBar` lives in your macOS menu bar quietly, and offers you a nice way of monitoring your in-game real-time stats when you need to check them.\n\n> **Note**\n>\n> `PaimonMenuBar` is made with SwiftUI, designed for and native to macOS.\n\n## Download\n\n[![GitHub Release](https://img.shields.io/github/v/release/spencerwooo/PaimonMenuBar?labelColor=282c34&logo=GitHub&style=for-the-badge)](https://github.com/spencerwooo/PaimonMenuBar/releases/latest)\n\n## Things to know\n\n1. Paimon uses the official Hoyoverse API found in either [米游社 (for CN players)](https://bbs.mihoyo.com/ys/) or [HoYoLAB (for Global players)](https://www.hoyolab.com/home).\n2. Yes, Paimon needs your cookie. It is so that Paimon can request said API on your behalf, and fetch those in-game stats periodically. Rest assured that **the cookie is only stored locally.**\n3. Check [FAQ](https://paimon.swo.moe/) if you have anymore questions.\n\n## Credits\n\n* Credits to [@Chawong](https://www.pixiv.net/en/artworks/92415888) for the logo. (Love from Hu Tao :heart:)\n* iOS widget (Scriptable): [[闲聊杂谈][工具分享] iOS 快捷指令/小组件](https://bbs.nga.cn/read.php?tid=29801567)\n* Friendly browser extension alternative: [daidr/paimon-webext](https://github.com/daidr/paimon-webext)\n* Friendly Windows alternative: [ArvinZJC/PaimonTray](https://github.com/ArvinZJC/PaimonTray)\n\n<details>\n<summary>Development notes.</summary>\n\n## TO-DO\n\n* [x] Menu bar of varying height.\n* [x] Configurable data refresh rate.\n* [x] Start at login.\n* [x] `i18n` support for at least Simplified Chinese and English.\n* [x] Manual refresh button.\n* [x] Code-sign and publish as `.dmg`.\n* [x] Auto-updates and check for update.\n* [x] Custom website and help for acquiring the cookie.\n* [x] Help button beside the text field for entering the cookie.\n* [x] Support for cn and global genshin accounts (米游社 and hoyolab).\n* [x] Backward-compatibility for macOS 11.0.\n* [x] Better first-time installation experience (guidance for initial setup).\n* ~~[ ] Support for multiple accounts?~~\n\n## Releasing a new version\n\n* Create a build in Xcode, bump the build number, and notarize build.\n* Create a new release on GitHub with a new version tag and increment the build number.\n* Use `create-dmg` to create the `.dmg` file:\n\n  ```bash\n  create-dmg PaimonMenuBar.app\n  ```\n\n* Update appcast.xml with the new version tag and build number:\n\n  ```bash\n  cd <PATH_TO_SPARKLE>/artifacts/sparkle/bin\n  ./generate_appcast <PATH_TO_PROJECT>/PaimonMenuBar/Build/\n  ```\n\n* Profit.\n\n</details>\n\n## License\n\n[MIT](LICENSE)\n\n<div align=\"center\">\n  <img src=\"Assets/footer.png\" />\n  <em>made with ❤️ by <a href=\"https://spencerwoo.com\">spencer woo</a></em>\n</div>\n"
  },
  {
    "path": "appcast.xml",
    "content": "<?xml version=\"1.0\" standalone=\"yes\"?>\n<rss xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\" version=\"2.0\">\n    <channel>\n        <title>PaimonMenuBar</title>\n        <item>\n            <title>1.12</title>\n            <pubDate>Thu, 08 Dec 2022 13:12:40 +0800</pubDate>\n            <sparkle:version>128</sparkle:version>\n            <sparkle:shortVersionString>1.12</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.12.128/PaimonMenuBar.1.12.dmg\" length=\"5637918\" type=\"application/octet-stream\" sparkle:edSignature=\"PrBMHAQ7DkzuCa/jODOndBxVExumkQaHu2YKxmbRBBu2KSGK1thFzJfFmAfJjRn6G/QeT4y+fm5yLI0w7mKOCQ==\"/>\n        </item>\n        <item>\n            <title>1.12</title>\n            <pubDate>Thu, 08 Dec 2022 01:42:15 +0800</pubDate>\n            <sparkle:version>126</sparkle:version>\n            <sparkle:shortVersionString>1.12</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.12.126/PaimonMenuBar.1.12.dmg\" length=\"5635852\" type=\"application/octet-stream\" sparkle:edSignature=\"WGyHmjHDNg0tYRq4G1kFhm40ObGPiw+7ms5Ddnbme/PHiSwS3NKegtZ7/0sjM0j3uw2BqTSQR9COLb6FesySDg==\"/>\n        </item>\n        <item>\n            <title>1.11</title>\n            <pubDate>Sun, 09 Oct 2022 15:29:45 +0800</pubDate>\n            <sparkle:version>124</sparkle:version>\n            <sparkle:shortVersionString>1.11</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.11.124/PaimonMenuBar.1.11.dmg\" length=\"5632634\" type=\"application/octet-stream\" sparkle:edSignature=\"fhprCrniicFz7VHnEG/cULokwhn6cXBbUscXcU03BnhkXgg6zTr8wxYtoDt+Ku4gYAV+2P3c6cesjmwhMUujBw==\"/>\n        </item>\n        <item>\n            <title>1.10</title>\n            <pubDate>Thu, 11 Aug 2022 16:42:19 +0800</pubDate>\n            <sparkle:version>123</sparkle:version>\n            <sparkle:shortVersionString>1.10</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.10.123/PaimonMenuBar.1.10.dmg\" length=\"5626931\" type=\"application/octet-stream\" sparkle:edSignature=\"ZRHF1dGDOf8eurp9O1Kpm2TiVyY+836+dYflkTQ87Gbq/fgjwRxz/g79nabjx6KlGI20q+Ve+Cybxc2+EUI6CQ==\"/>\n        </item>\n        <item>\n            <title>1.9</title>\n            <pubDate>Thu, 09 Jun 2022 20:19:39 +0800</pubDate>\n            <sparkle:version>120</sparkle:version>\n            <sparkle:shortVersionString>1.9</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.9.120/PaimonMenuBar.1.9.dmg\" length=\"4581848\" type=\"application/octet-stream\" sparkle:edSignature=\"KnEcO3MHDP9T+cxt8ru6M2BMtGox1RgbpqYKiW1I3vLEp1DqFNf6PqgefCgq7mGCwAIhC+9CvXhRqtwZcsgrDA==\"/>\n        </item>\n        <item>\n            <title>1.8</title>\n            <pubDate>Tue, 07 Jun 2022 21:24:06 +0800</pubDate>\n            <sparkle:version>114</sparkle:version>\n            <sparkle:shortVersionString>1.8</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.8.114/PaimonMenuBar.1.8.dmg\" length=\"4496866\" type=\"application/octet-stream\" sparkle:edSignature=\"qPcQ3fub9Ffoh7wvscFoPMmgZ8i/AmQ7LcqLYJFOAzvajpZpFbofKmFUFVTA6jzC83yBL5MNYdliCjhIjkmjBw==\"/>\n        </item>\n        <item>\n            <title>1.7</title>\n            <pubDate>Fri, 13 May 2022 23:20:25 +0800</pubDate>\n            <sparkle:version>113</sparkle:version>\n            <sparkle:shortVersionString>1.7</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.7.113/PaimonMenuBar.1.7.dmg\" length=\"4492440\" type=\"application/octet-stream\" sparkle:edSignature=\"TpXQhQYI85GCPlbJyqqboWUaBpAd9HXus/c06QvxxT1crZlpd0Wb4sscpN3Z4xSktGqBmJIsGNL58cXCe3BOBQ==\"/>\n        </item>\n        <item>\n            <title>1.7</title>\n            <pubDate>Mon, 09 May 2022 13:49:30 +0800</pubDate>\n            <sparkle:version>111</sparkle:version>\n            <sparkle:shortVersionString>1.7</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.7.111/PaimonMenuBar.1.7.dmg\" length=\"4488485\" type=\"application/octet-stream\" sparkle:edSignature=\"31aSlKt4D99sMHpIJg9Mz/aXLF3kpZqJPFyjJWZjK7/8agwH43WEc5vNiMd67uJwgJNRYNQXacfAj9F7oZaMAA==\"/>\n        </item>\n        <item>\n            <title>1.6</title>\n            <pubDate>Sun, 01 May 2022 20:37:03 +0800</pubDate>\n            <sparkle:version>110</sparkle:version>\n            <sparkle:shortVersionString>1.6</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.6.110/PaimonMenuBar.1.6.dmg\" length=\"3517801\" type=\"application/octet-stream\" sparkle:edSignature=\"beNk/XPJhjCTGbQjyMvlMTKeq4g1gosbmJw93k67m5rZ49k956WFeVnpCuXm7kBEd6yQLijV3AiTTiAn51xoDg==\"/>\n        </item>\n        <item>\n            <title>1.5</title>\n            <pubDate>Sat, 30 Apr 2022 15:07:47 +0800</pubDate>\n            <sparkle:version>108</sparkle:version>\n            <sparkle:shortVersionString>1.5</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.5.108/PaimonMenuBar.1.5.dmg\" length=\"3377362\" type=\"application/octet-stream\" sparkle:edSignature=\"tgup1mfveJhZaOzd0wB9uhSpdVv+ejRrWB2oybprGABRMU39qkm6fi/UGISfyhsVtw098IQo4syYNwIXWGyuDA==\"/>\n        </item>\n        <item>\n            <title>1.4</title>\n            <pubDate>Thu, 28 Apr 2022 22:35:09 +0800</pubDate>\n            <sparkle:version>106</sparkle:version>\n            <sparkle:shortVersionString>1.4</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.4.106/PaimonMenuBar.1.4.dmg\" length=\"3391221\" type=\"application/octet-stream\" sparkle:edSignature=\"rsh/lK4OdFEJbZSJUSgALXOAuhYj7aB0LKGjF+nFFn4aMmjmtEcB7GmIVMMQ0nYaKYkjQ0nVnzMj9bD0fpNLBg==\"/>\n        </item>\n        <item>\n            <title>1.4</title>\n            <pubDate>Tue, 26 Apr 2022 13:56:31 +0800</pubDate>\n            <sparkle:version>104</sparkle:version>\n            <sparkle:shortVersionString>1.4</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.4.104/PaimonMenuBar.1.4.dmg\" length=\"3390386\" type=\"application/octet-stream\" sparkle:edSignature=\"aUxb5JAhHTKpf8AOePkq2qA86vq7gWRHdz/uqzDX2WwyesoeOLOHKyK2Uxt1TvvaVTNLNI24VSXk5UKEObTyDA==\"/>\n        </item>\n        <item>\n            <title>1.3</title>\n            <pubDate>Tue, 26 Apr 2022 01:31:47 +0800</pubDate>\n            <sparkle:version>103</sparkle:version>\n            <sparkle:shortVersionString>1.3</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.3.103/PaimonMenuBar.1.3.dmg\" length=\"3387934\" type=\"application/octet-stream\" sparkle:edSignature=\"FUECvyK5S+M1mOhGX4NbNRpxXpfuHQt/dCamaKX2D4bNZ7bCn7CMz33VDv3C9W341Al6z7RLoexoxh7nmkvYAA==\"/>\n        </item>\n        <item>\n            <title>1.2</title>\n            <pubDate>Thu, 31 Mar 2022 16:20:33 +0800</pubDate>\n            <sparkle:version>102</sparkle:version>\n            <sparkle:shortVersionString>1.2</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.2.102/PaimonMenuBar.1.2.dmg\" length=\"3385535\" type=\"application/octet-stream\" sparkle:edSignature=\"O4RCB0x5RhaBps9bFRO1Goiltn2+JCrV4x7Yi8t0bruSUBAk1sY4Mt2sKh0WksHy4ouHtdy4niDadUTK8NnBAQ==\"/>\n        </item>\n        <item>\n            <title>1.2</title>\n            <pubDate>Thu, 31 Mar 2022 14:31:26 +0800</pubDate>\n            <sparkle:version>101</sparkle:version>\n            <sparkle:shortVersionString>1.2</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.2.101/PaimonMenuBar.1.2.dmg\" length=\"3385341\" type=\"application/octet-stream\" sparkle:edSignature=\"QFYmtv5jrU8z1/8+tTuQ3lLBlvDlIAtgCbIKCqySNdGmhnn9keH3DZH44myHiZYW7QJ6WZaqQVmOCdvJrEygAQ==\"/>\n        </item>\n        <item>\n            <title>1.2</title>\n            <pubDate>Thu, 31 Mar 2022 14:17:48 +0800</pubDate>\n            <sparkle:version>100</sparkle:version>\n            <sparkle:shortVersionString>1.2</sparkle:shortVersionString>\n            <sparkle:minimumSystemVersion>12.0</sparkle:minimumSystemVersion>\n            <enclosure url=\"https://github.com/spencerwooo/PaimonMenuBar/releases/download/v1.2.100/PaimonMenuBar.1.2.dmg\" length=\"3385317\" type=\"application/octet-stream\" sparkle:edSignature=\"nf8UE3ezWsBedF7EHxsvqihF3GJSsxx9lmukc0+LpNGo58Y6oMygMAFfc+yC4rU4BtG9qS7lpm9kk9Apwzs0Bg==\"/>\n        </item>\n    </channel>\n</rss>\n"
  }
]