[
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  push:\n    branches:\n      - main\njobs:\n  release:\n    name: Release\n    runs-on: ubuntu-20.04\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v3\n        with:\n          node-version: 18\n\n      - name: Install pnpm\n        uses: pnpm/action-setup@v2\n        with:\n          version: 8\n          run_install: false\n\n      - name: Install dependencies\n        run: pnpm install\n\n      - name: Build\n        run: pnpm build\n\n      - name: Release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: npx semantic-release"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n.DS_Store\ndist\ndist-ssr\n*.local\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# [1.7.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.6.0...v1.7.0) (2024-05-21)\n\n\n### Features\n\n* Added Custom Commit Message ([#60](https://github.com/haydenull/logseq-plugin-git/issues/60)) ([3773f8b](https://github.com/haydenull/logseq-plugin-git/commit/3773f8bbcf5a1004fa93d8af6c043c62a4104d9e))\n\n# [1.6.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.5...v1.6.0) (2024-05-21)\n\n\n### Bug Fixes\n\n* :bug: release action error ([52d170b](https://github.com/haydenull/logseq-plugin-git/commit/52d170b88f4f1edb28810fe26a501978b0cfde6b))\n\n\n### Features\n\n* New keyboard shortcut to commit without pushing ([#59](https://github.com/haydenull/logseq-plugin-git/issues/59)) ([fc799b8](https://github.com/haydenull/logseq-plugin-git/commit/fc799b8a8a364487db5c44631d3982323343926d))\n\n## [1.5.5](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.4...v1.5.5) (2023-12-28)\n\n\n### Bug Fixes\n\n* patch git exec command ([#50](https://github.com/haydenull/logseq-plugin-git/issues/50)) ([b4a4229](https://github.com/haydenull/logseq-plugin-git/commit/b4a422930288ff32cf2ff2fd23a71f0ca862b606))\n\n## [1.5.4](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.3...v1.5.4) (2023-07-27)\n\n\n### Bug Fixes\n\n* exported inProcess() was not set correctly breaking checkIsSynced ([#37](https://github.com/haydenull/logseq-plugin-git/issues/37)) ([cf78518](https://github.com/haydenull/logseq-plugin-git/commit/cf785180e4f68e589a153b71056a2fe1163fade6))\n\n## [1.5.3](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.2...v1.5.3) (2023-07-26)\n\n\n### Bug Fixes\n\n* [#30](https://github.com/haydenull/logseq-plugin-git/issues/30), shortcut mod+alt+s will pull rebase ([5f42bd5](https://github.com/haydenull/logseq-plugin-git/commit/5f42bd5674cdcdff6ae60e789ddf9bc66223ed38))\n* stack git operations not to get errors when auto actions are already performing ([e78bb2c](https://github.com/haydenull/logseq-plugin-git/commit/e78bb2c185787e731cb7b1af07487b6901389396)), closes [#34](https://github.com/haydenull/logseq-plugin-git/issues/34)\n\n## [1.5.2](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.1...v1.5.2) (2023-04-20)\n\n\n### Bug Fixes\n\n* :bug: the mask was not turned off after initilization ([a84acb5](https://github.com/haydenull/logseq-plugin-git/commit/a84acb5a7133da1f0693c15112ab5c903f0a1774))\n\n## [1.5.1](https://github.com/haydenull/logseq-plugin-git/compare/v1.5.0...v1.5.1) (2023-04-16)\n\n\n### Bug Fixes\n\n* In the Logseq 0.9.2 version, there is an issue with the panel display that needs to be fixed. ([a81bceb](https://github.com/haydenull/logseq-plugin-git/commit/a81bceb10996a6e3d3901fe1f1fef63d6d1c4052))\n\n# [1.5.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.4.2...v1.5.0) (2023-02-14)\n\n\n### Features\n\n* logseq sdk version ([#26](https://github.com/haydenull/logseq-plugin-git/issues/26)) ([d616d82](https://github.com/haydenull/logseq-plugin-git/commit/d616d820df800809cdc37e718fa0a43ff380498b))\n\n## [1.4.2](https://github.com/haydenull/logseq-plugin-git/compare/v1.4.1...v1.4.2) (2022-11-06)\n\n\n### Bug Fixes\n\n* button color ([d3a5bd1](https://github.com/haydenull/logseq-plugin-git/commit/d3a5bd169ee74634983c04f160677a9f34f8971e))\n\n## [1.4.1](https://github.com/haydenull/logseq-plugin-git/compare/v1.4.0...v1.4.1) (2022-11-01)\n\n\n### Bug Fixes\n\n* button style ([d32ef80](https://github.com/haydenull/logseq-plugin-git/commit/d32ef80c2105b3d7fb14da9b813c2eb29b7d3045))\n\n# [1.4.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.3.0...v1.4.0) (2022-08-23)\n\n\n### Features\n\n* ✨ Support for turning off auto-check sync ([a2ac2bd](https://github.com/haydenull/logseq-plugin-git/commit/a2ac2bd44cd9685c15827bdb82bdec6c18139823))\n\n# [1.3.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.2.0...v1.3.0) (2022-08-19)\n\n\n### Features\n\n* add default value: pull rebase ([90862ba](https://github.com/haydenull/logseq-plugin-git/commit/90862ba1d2d0d258c6d7b7a58f552fb0187f2df9))\n\n# [1.2.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.1.0...v1.2.0) (2022-06-11)\n\n\n### Features\n\n* supports disable checkWhenDBChanged ([85428aa](https://github.com/haydenull/logseq-plugin-git/commit/85428aa217361a797f64c5eadd8a008e39cb49b2))\n\n# [1.1.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.2...v1.1.0) (2022-05-22)\n\n\n### Features\n\n* ✨ add pull and checkout buttons ([f950626](https://github.com/haydenull/logseq-plugin-git/commit/f950626e2a3776c805a96267bbd37ba7a7eae9da))\n\n## [1.0.2](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.1...v1.0.2) (2022-05-20)\n\n\n### Bug Fixes\n\n* The id does not match expectations ([dffcaa3](https://github.com/haydenull/logseq-plugin-git/commit/dffcaa3b71086bad022350494c841bed7576d9c3))\n\n## [1.0.1](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.0...v1.0.1) (2022-05-05)\n\n\n### Bug Fixes\n\n* 🐛 Adapt logseq 0.6.7 ([02ffdf7](https://github.com/haydenull/logseq-plugin-git/commit/02ffdf70d493f0adc70d95d4847a7bb6ed6751b7))\n* default value error[skip ci] ([a5cfc2b](https://github.com/haydenull/logseq-plugin-git/commit/a5cfc2b9184119f820946fcb0d33f5e5dc098e5d))\n\n# 1.0.0 (2022-04-08)\n\n\n### Features\n\n* ✨ add icon ([0f52416](https://github.com/haydenull/logseq-plugin-git/commit/0f52416ef8594525fb2fb527bc05c98fa327e308))\n\n# [1.1.0](https://github.com/haydenull/logseq-plugin-git/compare/v1.0.0...v1.1.0) (2022-03-08)\n\n\n### Features\n\n* add antd ([036577d](https://github.com/haydenull/logseq-plugin-git/commit/036577dc529db4e4a5964c287a55d112bae654bc))\n\n# 1.0.0 (2022-01-18)\n\n\n### Features\n\n* ✨ initial commit ([cca0e7f](https://github.com/haydenull/logseq-plugin-git/commit/cca0e7fcba33830eaf534fd9ca6b867b57147de4))\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Hayden Chen\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": "README.md",
    "content": "# logseq-plugin-git\n> git plugin for logseq\n\n[![latest release version](https://img.shields.io/github/v/release/haydenull/logseq-plugin-git)](https://github.com/haydenull/logseq-plugin-git/releases)\n[![License](https://img.shields.io/github/license/haydenull/logseq-plugin-git?color=blue)](https://github.com/haydenull/logseq-plugin-git/blob/main/LICENSE)\n\nLogseq Git Plugin is a plugin for Logseq that provides easy access to common git commands, and helps you keep your notes synchronized with a remote git repository.\n\nWith this plugin, you can:\n\n- Know explicitly if there are unsaved notes in the current repository and if they are synchronized with the remote repository. When there are unsaved files locally, the plugin will show a red indicator in the toolbar icon.\n- Quickly commit changes to notes. The plugin provides shortcut keys (mod+s) to perform commit&push operations.\n- Provide buttons to quickly execute common commands. Click the plugin icon, and the drop-down menu will show you some buttons to help you quickly execute commands (you can specify the desired buttons in the settings).\n- Prompt you if you have synchronized with the remote repository. The plugin automatically detects if the current repository is up-to-date and prompts when logseq starts and other times.\n\n## Installation\n\nInstall from the Logseq Plugin Store\n\n## Shortcuts\nThe plugin provides a shortcut key (mod+s) to quickly commit and push changes to the remote repository.\n\n## Toolbar Icon\n\n- icon: everything is up-to-date.\n- icon with red dot: there are unsaved files locally.\n- icon with blinking red dot: the plugin is committing or pushing changes to the remote repository.\n\n## Commands\n\nThe drop-down menu shows the following commands:\n- Check Status: check the status of the current repository.\n- Show Log: show the log of the current repository.\n- Pull: pull changes from the remote repository.\n- Pull Rebase: pull changes from the remote repository and rebase.\n- Commit: commit changes to the current repository.\n- Push: push changes to the remote repository.\n- Commit & Push: commit changes and push to the remote repository.\n\n## Demo\n![demo](./demo.png)\n\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/src/favicon.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite App</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"logseq-plugin-git\",\n  \"version\": \"1.7.0\",\n  \"main\": \"dist/index.html\",\n  \"logseq\": {\n    \"id\": \"logseq-git\",\n    \"icon\": \"logo.png\"\n  },\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@logseq/libs\": \"^0.0.16\",\n    \"antd\": \"^4.18.9\",\n    \"react\": \"^17.0.2\",\n    \"react-dom\": \"^17.0.2\"\n  },\n  \"devDependencies\": {\n    \"@semantic-release/changelog\": \"^6.0.1\",\n    \"@semantic-release/exec\": \"^6.0.3\",\n    \"@semantic-release/git\": \"^10.0.1\",\n    \"@semantic-release/npm\": \"^10.0.4\",\n    \"@types/node\": \"^20.4.5\",\n    \"@types/react\": \"^18.2.17\",\n    \"@types/react-dom\": \"^18.2.7\",\n    \"@vitejs/plugin-react\": \"^4.0.3\",\n    \"autoprefixer\": \"^10.4.2\",\n    \"postcss\": \"^8.4.27\",\n    \"semantic-release\": \"^21.1.2\",\n    \"tailwindcss\": \"^3.3.3\",\n    \"typescript\": \"^5.1.6\",\n    \"vite\": \"^4.4.7\",\n    \"vite-plugin-importer\": \"^0.2.5\"\n  },\n  \"packageManager\": \"pnpm@8.6.10\"\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "release.config.js",
    "content": "const pluginName = require('./package.json').name\n\nmodule.exports = {\n  branches: 'main',\n  plugins: [\n    '@semantic-release/commit-analyzer',\n    '@semantic-release/release-notes-generator',\n    '@semantic-release/changelog',\n    [\n      '@semantic-release/npm',\n      { npmPublish: false }\n    ],\n    [\n      '@semantic-release/git',\n      {\n        assets: ['CHANGELOG.md', 'package.json'],\n        message: 'chore(release): ${nextRelease.version} [skip ci]\\n\\n${nextRelease.notes}'\n      }\n    ],\n    [\n      '@semantic-release/exec',\n      {\n        prepareCmd: 'zip -qq -r ' + pluginName + '-${nextRelease.version}.zip package.json logo.png dist/'\n      }\n    ],\n    [\n      '@semantic-release/github',\n      {\n        assets: [`${pluginName}-*.zip`]\n      }\n    ]\n  ]\n}"
  },
  {
    "path": "src/App.css",
    "content": ""
  },
  {
    "path": "src/App.tsx",
    "content": "import './App.css'\n\nconst App: React.FC<{ env: string }> = ({ env }) => {\n  return (\n    <div className=\"w-screen h-screen flex items-center justify-center text-white\">\n      <div className=\"w-screen h-screen fixed top-0 left-0\" onClick={() => logseq.hideMainUI()}></div>\n      <div className=\"w-5/6 h-5/6 bg-gradient-to-tr from-yellow-400 via-red-500 to-pink-500 flex flex-col items-center justify-center\">\n        <h1 className=\"font-bold text-4xl\">logseq-plugin-git</h1>\n        <h2 className=\"text-2xl mt-6\">Current Env: {env}</h2>\n      </div>\n    </div>\n  )\n}\n\nexport default App\n"
  },
  {
    "path": "src/helper/constants.ts",
    "content": "import { SettingSchemaDesc } from \"@logseq/libs/dist/LSPlugin.user\";\n\nexport const COMMON_STYLE = `\n.ui-items-container[data-type=toolbar] > .list-wrap {\n  overflow: visible;\n}\n#injected-ui-item-git-logseq-git {\n  position: relative;\n}\n.plugin-git-container {\n  display: none;\n}\n.plugin-git-container .plugin-git-mask {\n  position: fixed;\n  width: 100vw;\n  height: 100vh;\n  left: 0;\n  top: 0;\n  z-index: 99;\n}\n.plugin-git-container .plugin-git-popup {\n  position: fixed;\n  z-index: 99;\n  background-color: var(--ls-secondary-background-color);\n  padding: 10px;\n  border-radius: .375rem;\n  --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05);\n  box-shadow: var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);\n}\n.plugin-git-container .plugin-git-popup::before {\n  content: '';\n  position: absolute;\n  top: -8px;\n  left: 50%;\n  transform: translateX(-50%);\n  width: 0;\n  height: 0;\n  border-style: solid;\n  border-width: 0 10px 8px 10px;\n  border-color: transparent transparent var(--ls-secondary-background-color) transparent;\n}\n`;\n\nexport const SHOW_POPUP_STYLE = `\n.plugin-git-container {\n  display: block;\n}\n`;\nexport const HIDE_POPUP_STYLE = `\n.plugin-git-container {\n  display: none;\n}\n`;\n\nexport const INACTIVE_STYLE = `\n${COMMON_STYLE}\n#injected-ui-item-git-logseq-git::after {\n  display: none;\n}\n`;\nexport const ACTIVE_STYLE = `\n${COMMON_STYLE}\n#injected-ui-item-git-logseq-git::after {\n  display: block;\n  content: '';\n  position: absolute;\n  width: 8px;\n  height: 8px;\n  border-radius: 100%;\n  background-color: rgb(237, 66, 69);\n  right: 8px;\n  top: 6px;\n}\n`;\n\nexport const LOADING_STYLE = `\n${COMMON_STYLE}\n#injected-ui-item-git-logseq-git::after {\n  display: block;\n  content: '';\n  position: absolute;\n  width: 8px;\n  height: 8px;\n  border-radius: 100%;\n  background-color: rgb(237, 66, 69);\n  right: 8px;\n  top: 6px;\n  animation: blink 1s linear infinite;\n}\n@keyframes blink {\n  0% {\n    opacity: 0;\n  }\n  50% {\n    opacity: 1;\n  }\n  100% {\n    opacity: 0;\n  }\n}\n`;\n\nexport const BUTTONS = [\n  { key: \"status\", title: \"Check Status\", event: \"check\" },\n  { key: \"log\", title: \"Show Log\", event: \"log\" },\n  { key: \"pull\", title: \"Pull\", event: \"pull\" },\n  { key: \"pullRebase\", title: \"Pull Rebase\", event: \"pullRebase\" },\n  { key: \"checkout\", title: \"Checkout\", event: \"checkout\" },\n  { key: \"commit\", title: \"Commit\", event: \"commit\" },\n  { key: \"push\", title: \"Push\", event: \"push\" },\n  { key: \"commitAndPush\", title: \"Commit & Push\", event: \"commitAndPush\" },\n];\n\nexport const SETTINGS_SCHEMA: SettingSchemaDesc[] = [\n  {\n    key: \"buttons\",\n    title: \"Buttons\",\n    type: \"enum\",\n    default: [\"Check Status\", \"Show Log\", \"Pull Rebase\", \"Commit & Push\"],\n    description: \"Select buttons to show\",\n    enumPicker: \"checkbox\",\n    enumChoices: BUTTONS.map(({ title }) => title),\n  },\n  {\n    key: \"checkWhenDBChanged\",\n    title: \"Check Status when DB Changed\",\n    type: \"boolean\",\n    default: true,\n    description: \"Check status when DB changed, restart logseq to take effect\",\n  },\n  {\n    key: \"autoCheckSynced\",\n    title: \"Auto Check If Synced\",\n    type: \"boolean\",\n    default: false,\n    description:\n      \"Automatically check if the local version is the same as the remote\",\n  },\n  {\n    key: \"autoPush\",\n    title: \"Auto Push\",\n    type: \"boolean\",\n    default: false,\n    description: \"Auto push when logseq hide\",\n  },\n  {\n    key: \"typeCommitMessage\",\n    title: \"Type Commit Message\",\n    type: \"enum\",\n    default: \"Default Message With Date\",\n    description: \"Type of commit message to use\",\n    enumPicker: \"select\",\n    enumChoices: ['Custom Message' , 'Default Message', 'Custom Message With Date', 'Default Message With Date'],\n  },\n  {\n    key: \"customCommitMessage\",\n    title: \"Custom Commit Message\",\n    type: \"string\",\n    default: \"\",\n    description: \"Custom commit message for plugin (valid only if commit message is set to Custom Message)\",\n  }\n];\n"
  },
  {
    "path": "src/helper/git.ts",
    "content": "// https://logseq.github.io/plugins/interfaces/IAppProxy.html#execGitCommand\nimport type { IGitResult } from \"@logseq/libs/dist/LSPlugin.user\"\n\nlet _inProgress: Promise<IGitResult> | undefined = undefined\n\nexport const execGitCommand = async (args: string[]) : Promise<IGitResult> => {\n  if (_inProgress) await _inProgress\n\n  let res\n  try {\n    const currentGitFolder = (await logseq.App.getCurrentGraph())?.path\n    const runArgs = currentGitFolder ? ['-C', currentGitFolder, ...args] : args\n    _inProgress = logseq.Git.execCommand(runArgs)\n    res = await _inProgress\n  } finally {\n    _inProgress = undefined\n  }\n    return res\n}\n\nexport const inProgress = () => _inProgress\n\nexport const status = async (showRes = true): Promise<IGitResult> => {\n  // git status --porcelain | awk '{print $2}'\n  // git status --porcelain | wc -l\n  const res =  await execGitCommand(['status', '--porcelain'])\n  console.log('[faiz:] === git status', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git status success')\n    } else {\n      logseq.UI.showMsg(`Git status failed\\n${res.stderr}`, 'error')\n    }\n  }\n  /**\n   * res\n   * modify\n   * {\n   *  exitCode: 0,\n   *  stderr: '',\n   *  stdout: 'M foo.md\\n?? bar.md\\n',\n   * }\n   * ahead & uptodate & behind\n   * {\n   * exitCode: 0,\n   * stderr: '',\n   * stdout: '',\n   * }\n   */\n  // changed files    staged files\n  return res\n}\n\n// log with git log --pretty=format:\"%h %ad | %s%d [%an]\" --date=short\nexport const log = async (showRes = true): Promise<IGitResult> => {\n  // git log --pretty=format:\"%h %s\" -n 1\n  // git log --pretty=format:\"%h %ad | %s%d [%an]\" --date=short\n  // return await logseq.App.execGitCommand(['log', '--pretty=format:\"%h %s\"'])\n  const res = await execGitCommand(['log', '--pretty=format:\"%h %ad | %s [%an]\"', '--date=format:\"%Y-%m-%d %H:%M:%S\"', '--name-status'])\n  console.log('[faiz:] === git log', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git log success')\n    } else {\n      logseq.UI.showMsg(`Git log failed\\n${res.stderr}`, 'error')\n    }\n  }\n  return res\n}\n\n// git pull\nexport const pull = async (showRes = true): Promise<IGitResult> => {\n  const res = await execGitCommand(['pull'])\n  console.log('[faiz:] === git pull', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git pull success')\n    } else {\n      logseq.UI.showMsg(`Git pull failed\\n${res.stderr}`, 'error')\n    }\n  }\n  return res\n}\n\n// git pull --rebase\nexport const pullRebase = async (showRes = true): Promise<IGitResult> => {\n  const res = await execGitCommand(['pull', '--rebase'])\n  console.log('[faiz:] === git pull --rebase', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git pull --rebase success')\n    } else {\n      logseq.UI.showMsg(`Git pull --rebase failed\\n${res.stderr}`, 'error')\n    }\n  }\n  return res\n}\n\n// git checkout .\nexport const checkout = async (showRes = true): Promise<IGitResult> => {\n  const res = await execGitCommand(['checkout', '.'])\n  console.log('[faiz:] === git checkout .', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git checkout success')\n    } else {\n      logseq.UI.showMsg(`Git checkout failed\\n${res.stderr}`, 'error')\n    }\n  }\n  return res\n}\n\n// git commit\nexport const commit = async (showRes = true, message: string): Promise<IGitResult> => {\n  await execGitCommand(['add', '.'])\n  // git commit -m \"message\"\n  const res = await execGitCommand(['commit', '-m', message])\n  console.log('[faiz:] === git commit', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git commit success')\n    } else {\n      logseq.UI.showMsg(`Git commit failed\\n${res.stdout || res.stderr}`, 'error')\n    }\n  }\n  return res\n}\n\n// push\nexport const push = async (showRes = true): Promise<IGitResult> => {\n  // git push\n  const res = await execGitCommand(['push'])\n  console.log('[faiz:] === git push', res)\n  if (showRes) {\n    if (res.exitCode === 0) {\n      logseq.UI.showMsg('Git push success')\n    } else {\n      logseq.UI.showMsg(`Git push failed\\n${res.stderr}`, 'error')\n    }\n  }\n  return res\n}\n\n\n/**\n * Returns the commit message based on the selected commit message type in the logseq settings.\n * @returns The commit message.\n */\nexport const commitMessage = () : string => {\n  \n  let defaultMessage = \"[logseq-plugin-git:commit]\";\n\n  switch (logseq.settings?.typeCommitMessage as string) {\n    case \"Default Message\":\n      return defaultMessage;\n    case \"Default Message With Date\":\n      return defaultMessage + \" \" + new Date().toISOString();\n    case \"Custom Message\":\n      let customMessage = logseq.settings?.customCommitMessage as string;\n      if (customMessage.trim() === \"\") {\n        return defaultMessage;\n      } else {\n        return customMessage;\n      }\n    case \"Custom Message With Date\":\n      let customMessageWithDate = logseq.settings?.customCommitMessage as string;\n      if (customMessageWithDate.trim() === \"\") {\n        return defaultMessage + \" \" + new Date().toISOString();\n      } else {\n        return customMessageWithDate + \" \" + new Date().toISOString();\n      }\n    default:\n      return defaultMessage;\n  }\n}\n"
  },
  {
    "path": "src/helper/util.ts",
    "content": "import {\n  ACTIVE_STYLE,\n  HIDE_POPUP_STYLE,\n  INACTIVE_STYLE,\n  SHOW_POPUP_STYLE,\n} from \"./constants\";\nimport { status, inProgress, execGitCommand } from \"./git\";\n\nexport const checkStatus = async () => {\n  console.log(\"Checking status...\");\n  const statusRes = await status(false);\n  if (statusRes?.stdout === \"\") {\n    console.log(\"No changes\", statusRes);\n    setPluginStyle(INACTIVE_STYLE);\n  } else {\n    console.log(\"Need save\", statusRes);\n    setPluginStyle(ACTIVE_STYLE);\n  }\n  return statusRes;\n};\n\nlet pluginStyle = \"\";\nexport const setPluginStyle = (style: string) => {\n  pluginStyle = style;\n  logseq.provideStyle({ key: \"git\", style });\n};\nexport const getPluginStyle = () => pluginStyle;\n\nexport const showPopup = () => {\n  const _style = getPluginStyle();\n  logseq.UI.queryElementRect(\"#logseq-git--git\").then((triggerIconRect) => {\n    console.log(\"[faiz:] === triggerIconRect\", triggerIconRect);\n    if (!triggerIconRect) return;\n    const popupWidth = 120 + 10 * 2;\n    const left =\n      triggerIconRect.left + triggerIconRect.width / 2 - popupWidth / 2;\n    const top = triggerIconRect.top + triggerIconRect.height;\n    const _style = getPluginStyle();\n    setPluginStyle(\n      `${_style}\\n.plugin-git-popup{left:${left}px;top:${top}px;}`\n    );\n  });\n  setPluginStyle(`${_style}\\n${SHOW_POPUP_STYLE}`);\n};\nexport const hidePopup = () => {\n  const _style = getPluginStyle();\n  setPluginStyle(`${_style}\\n${HIDE_POPUP_STYLE}`);\n};\n\nexport const debounce = (fn, wait: number = 100, environment?: any) => {\n  let timer = null;\n  return function () {\n    // @ts-ignore\n    const context = environment || this;\n    const args = arguments;\n    if (timer) {\n      clearTimeout(timer);\n      timer = null;\n    }\n    // @ts-ignore\n    timer = setTimeout(function () {\n      fn.apply(context, args);\n    }, wait);\n  };\n};\n\nexport const checkStatusWithDebounce = debounce(() => {\n  checkStatus();\n}, 2000);\n\nexport const isRepoUpTodate = async () => {\n  await execGitCommand([\"fetch\"]);\n  const local = await execGitCommand([\"rev-parse\", \"HEAD\"]);\n  const remote = await execGitCommand([\"rev-parse\", \"@{u}\"]);\n  logseq.UI.showMsg(`${local.stdout} === ${remote.stdout}`, \"success\", { timeout: 30 });\n  return local.stdout === remote.stdout;\n};\n\nexport const checkIsSynced = async () => {\n  if (inProgress()) {\n    console.log(\"[faiz:] === checkIsSynced Git in progress, skip check\");\n    return\n  }\n\n  const isSynced = await isRepoUpTodate();\n  if (!isSynced)\n    logseq.UI.showMsg(\n      `The current repository is not synchronized with the remote repository, please check.`,\n      \"warning\",\n      { timeout: 0 }\n    );\n};\n"
  },
  {
    "path": "src/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nhtml, body {\n  background-color: transparent;\n}\n#root {\n  @apply w-screen h-screen;\n}\n"
  },
  {
    "path": "src/main.tsx",
    "content": "import \"@logseq/libs\";\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport App from \"./App\";\nimport { BUTTONS, LOADING_STYLE, SETTINGS_SCHEMA } from \"./helper/constants\";\nimport {\n  checkout,\n  commit,\n  commitMessage,\n  log,\n  pull,\n  pullRebase,\n  push,\n  status,\n} from \"./helper/git\";\nimport {\n  checkStatus,\n  debounce,\n  hidePopup,\n  setPluginStyle,\n  showPopup,\n  checkIsSynced,\n  checkStatusWithDebounce,\n  getPluginStyle,\n} from \"./helper/util\";\nimport \"./index.css\";\n\n// TODO: patch logseq Git command for the temporary fix solution\n// https://github.com/haydenull/logseq-plugin-git/issues/48\ntry {\n  // @ts-ignore\n  top.logseq.sdk.git.exec_command(['status'])\n} catch (e) {\n  // @ts-ignore\n  logseq.Git['execCommand'] = async function (args: string[]) {\n    const ret = await logseq.App.execGitCommand(args)\n    return {exitCode: ret == undefined ? 1 : 0, stdout: ret}\n  }\n}\n\nconst isDevelopment = import.meta.env.DEV\n\nif (isDevelopment) {\n  renderApp(\"browser\");\n} else {\n  console.log(\"=== logseq-plugin-git loaded ===\");\n  logseq.ready(() => {\n    const operations = {\n      check: debounce(async function () {\n        const status = await checkStatus();\n        if (status?.stdout === \"\") {\n          logseq.UI.showMsg(\"No changes detected.\");\n        } else {\n          logseq.UI.showMsg(\"Changes detected:\\n\" + status.stdout, \"success\", {\n            timeout: 0,\n          });\n        }\n        hidePopup();\n      }),\n      pull: debounce(async function () {\n        console.log(\"[faiz:] === pull click\");\n        setPluginStyle(LOADING_STYLE);\n        hidePopup();\n        await pull(false);\n        checkStatus();\n      }),\n      pullRebase: debounce(async function () {\n        console.log(\"[faiz:] === pullRebase click\");\n        setPluginStyle(LOADING_STYLE);\n        hidePopup();\n        await pullRebase();\n        checkStatus();\n      }),\n      checkout: debounce(async function () {\n        console.log(\"[faiz:] === checkout click\");\n        hidePopup();\n        checkout();\n      }),\n      commit: debounce(async function () {\n        hidePopup();\n        await commit(true, commitMessage());\n        checkStatus();\n      }),\n      push: debounce(async function () {\n        setPluginStyle(LOADING_STYLE);\n        hidePopup();\n        await push();\n        checkStatus();\n      }),\n      commitAndPush: debounce(async function () {\n        setPluginStyle(LOADING_STYLE);\n        hidePopup();\n\n        const status = await checkStatus();\n        const changed = status?.stdout !== \"\";\n        if (changed) {\n          const res = await commit(\n              true,\n              commitMessage()\n          );\n          if (res.exitCode === 0) await push(true);\n        }\n        checkStatus();\n      }),\n      log: debounce(async function () {\n        console.log(\"[faiz:] === log click\");\n        const res = await log(false);\n        logseq.UI.showMsg(res?.stdout, \"success\", { timeout: 0 });\n        hidePopup();\n      }),\n      showPopup: debounce(async function () {\n        console.log(\"[faiz:] === showPopup click\");\n        showPopup();\n      }),\n      hidePopup: debounce(function () {\n        console.log(\"[faiz:] === hidePopup click\");\n        hidePopup();\n      }),\n    };\n\n    logseq.provideModel(operations);\n\n    logseq.App.registerUIItem(\"toolbar\", {\n      key: \"git\",\n      template:\n        '<a data-on-click=\"showPopup\" class=\"button\"><i class=\"ti ti-brand-git\"></i></a><div id=\"plugin-git-content-wrapper\"></div>',\n    });\n    logseq.useSettingsSchema(SETTINGS_SCHEMA);\n    setTimeout(() => {\n      const buttons = (logseq.settings?.buttons as string[])\n        ?.map((title) => BUTTONS.find((b) => b.title === title))\n        .filter(Boolean);\n      if (top && buttons?.length) {\n        const parser = new DOMParser();\n        const doc = parser.parseFromString(\n          `\n          <div class=\"plugin-git-container\">\n            <div class=\"plugin-git-mask\"></div>\n            <div class=\"plugin-git-popup flex flex-col\">\n              ${buttons\n                .map(\n                  (button) =>\n                    `<button class=\"ui__button plugin-git-${button?.key} bg-indigo-600 hover:bg-indigo-700 focus:border-indigo-700 active:bg-indigo-700 text-center text-sm p-1\" style=\"margin: 4px 0; color: #fff;\">${button?.title}</button>`\n                )\n                .join(\"\\n\")}\n          </div>\n          `,\n          \"text/html\"\n        );\n        // remove .plugin-git-container if exists\n        const container = top?.document?.querySelector(\".plugin-git-container\");\n        console.log(\"[faiz:] === container\", container);\n        if (container) top?.document?.body.removeChild(container);\n        top?.document?.body.appendChild(\n          doc.body.childNodes?.[0]?.cloneNode(true)\n        );\n        top?.document\n          ?.querySelector(\".plugin-git-mask\")\n          ?.addEventListener(\"click\", hidePopup);\n        buttons.forEach((button) => {\n          top?.document\n            ?.querySelector(`.plugin-git-${button?.key}`)\n            ?.addEventListener(\"click\", operations?.[button!?.event]);\n        });\n      }\n    }, 1000);\n\n    logseq.App.onRouteChanged(async () => {\n      checkStatusWithDebounce();\n    });\n    if (logseq.settings?.checkWhenDBChanged) {\n      logseq.DB.onChanged(({ blocks, txData, txMeta }) => {\n        checkStatusWithDebounce();\n      });\n    }\n\n    if (logseq.settings?.autoCheckSynced) checkIsSynced();\n    checkStatusWithDebounce();\n\n    if (top) {\n      top.document?.addEventListener(\"visibilitychange\", async () => {\n        const visibilityState = top?.document?.visibilityState;\n\n        if (visibilityState === \"visible\") {\n          if (logseq.settings?.autoCheckSynced) checkIsSynced();\n        } else if (visibilityState === \"hidden\") {\n          // logseq.UI.showMsg(`Page is hidden: ${new Date()}`, 'success', { timeout: 0 })\n          // noChange void\n          // changed commit push\n          if (logseq.settings?.autoPush) {\n            operations.commitAndPush();\n          }\n        }\n      });\n    }\n\n    logseq.App.registerCommandPalette(\n      {\n        key: \"logseq-plugin-git:commit\",\n        label: \"Commit\",\n        keybinding: {\n          binding: \"alt+shift+s\",\n          mode: \"global\",\n        },\n      },\n      () => operations.commit()\n    );\n    logseq.App.registerCommandPalette(\n      {\n        key: \"logseq-plugin-git:commit&push\",\n        label: \"Commit & Push\",\n        keybinding: {\n          binding: \"mod+s\",\n          mode: \"global\",\n        },\n      },\n      () => operations.commitAndPush()\n    );\n    logseq.App.registerCommandPalette(\n        {\n          key: \"logseq-plugin-git:rebase\",\n          label: \"Pull Rebase\",\n          keybinding: {\n            binding: \"mod+alt+s\",\n            mode: \"global\",\n          },\n        },\n        () => operations.pullRebase()\n    );\n  });\n}\n\nfunction renderApp(env: string) {\n  ReactDOM.render(\n    <React.StrictMode>\n      <App env={env} />\n    </React.StrictMode>,\n    document.getElementById(\"root\")\n  );\n}\n"
  },
  {
    "path": "src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />"
  },
  {
    "path": "tailwind.config.js",
    "content": "module.exports = {\n  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx,css}'],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": false,\n    \"skipLibCheck\": false,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"noImplicitAny\": false,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\"./src\"]\n}\n"
  },
  {
    "path": "vite.config.ts",
    "content": "import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\nimport usePluginImport from 'vite-plugin-importer'\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [\n    react(),\n    usePluginImport({\n      libraryName: \"antd\",\n      libraryDirectory: \"es\",\n      style: \"css\",\n    }),\n  ],\n  base: './',\n})\n"
  }
]