[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Build plugin\n\non:\n  push:\n    # Sequence of patterns matched against refs/tags\n    tags:\n      - '*' # Push events to matching any tag format, i.e. 1.0, 20.15.10\n\nenv:\n  PLUGIN_NAME: logseq-hugo-plugin\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n      - name: Use Node.js\n        uses: actions/setup-node@v1\n        with:\n          node-version: '16.x' # You might need to adjust this value to your own version\n      - name: Build\n        id: build\n        run: |\n          npm i && npm run build\n          mkdir ${{ env.PLUGIN_NAME }}\n          cp README.md package.json icon.png ${{ env.PLUGIN_NAME }}\n          mv dist ${{ env.PLUGIN_NAME }}\n          zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}\n          ls\n          echo \"::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)\"\n      - name: Create Release\n        uses: ncipollo/release-action@v1\n        id: create_release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          VERSION: ${{ github.ref }}\n        with:\n          allowUpdates: true\n          draft: false\n          prerelease: false\n\n      - name: Upload zip file\n        id: upload_zip\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: ./${{ env.PLUGIN_NAME }}.zip\n          asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip\n          asset_content_type: application/zip\n\n      - name: Upload package.json\n        id: upload_metadata\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: ./package.json\n          asset_name: package.json\n          asset_content_type: application/json\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n.env.production\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\npackage-lock.json\nyarn.lock\n"
  },
  {
    "path": ".postcssrc",
    "content": "{\n  \"plugins\": {\n    \"postcss-import\": {},\n    \"tailwindcss/nesting\": {},\n    \"tailwindcss\": {}\n\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 sawhney17\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": "<div id=\"top\"></div>\n<!-- PROJECT SHIELDS -->\n\n[![Contributors][contributors-shield]][contributors-url]\n[![Forks][forks-shield]][forks-url]\n[![Stargazers][stars-shield]][stars-url]\n[![Issues][issues-shield]][issues-url]\n[![MIT License][license-shield]][license-url]\n\n<!-- PROJECT LOGO -->\n<br />\n<div align=\"center\">\n  <a href=\"https://github.com/sawhney17/logseq-schrodinger\">\n    <img src=\"icon.png\" alt=\"Logo\" width=\"80\" height=\"80\">\n  </a>\n\n  <h3 align=\"center\">logseq Schrödinger</h3>\n\n  <p align=\"center\">\n    An awesome <a href=\"https://logseq.com\">Logseq</a> plugin to jumpstart your digital garden 🌱!\n    <br />\n    <a href=\"https://github.com/sawhney17/logseq-schrodinger\"><strong>Explore the docs »</strong></a>\n    <br />\n    <br />\n    <a href=\"https://aryansawhney.com/\">View Demo</a>\n    ·\n    <a href=\"https://github.com/sawhney17/logseq-schrodinger/issues\">Report Bug</a>\n    ·\n    <a href=\"https://github.com/sawhney17/logseq-schrodinger/issues\">Request Feature</a>\n  </p>\n</div>\n\n<!-- TABLE OF CONTENTS -->\n<details>\n  <summary>Table of Contents</summary>\n  <ol>\n    <li><a href=\"#about-the-project\">About The Project</a></li>\n    <li><a href=\"#installation\">Installation</a></li>\n    <li><a href=\"#configuration\">Configuration</a>\n      <ul>\n        <li><a href=\"#meta-data\">Meta-data</a></li>\n        <li><a href=\"#configuring_hugo\">Configuring Hugo</a></li>\n        <li><a href=\"#admonitions\">Admonitions</a></li>\n      </ul>\n    </li>\n    <li><a href=\"#issues\">Issues</a></li>\n    <li><a href=\"#contributing\">Contributing</a></li>\n    <li><a href=\"#license\">License</a></li>\n    <li><a href=\"#contact\">Contact</a></li>\n    <li><a href=\"#acknowledgments\">Acknowledgments</a></li>\n  </ol>\n</details>\n\n<!-- ABOUT THE PROJECT -->\n\n## About The Project\n\n[![Product Name Screen Shot][product-screenshot]](https://github.com/sawhney17/logseq-schrodinger/)\n\n[Logseq](https://logseq.com) is a great PKM (personal knowledge management) tool, but keeping your knowledge for yourself only gets you so far. As [Erwin Schrödinger](https://simple.wikipedia.org/wiki/Erwin_Schrödinger) stated:\n\n> If a note is not published, does it really exist? — Erwin Schrödinger\n\nKnowledge is meant to be treasured and expanded, but before all shared. This plugin helps to make that possible, or at least easier.\n\n**Note:** This project is very much a work-in-progress. Please report <a href=\"#issues\">issues</a> and questions.\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n<!-- GETTING STARTED -->\n\n## Installation\n\n### Preparation\n\n- Click the 3 dots in the righthand corner and go to **Settings**.\n- Go to **Advanced** and enable **Plug-in system**.\n- Restart the application.\n- Click 3 dots and go to Plugins (or `Esc t p`).\n\n### Install plugin from the Marketplace (recommended)\n\n- Click the `Marketplace` button and then click `Plugins`.\n- Find the plugin and click `Install`.\n\n### Install plugin manually\n\n- Download a released version assets from Github.\n- Unzip it.\n- Click Load unpacked plugin, and select destination directory to the unzipped folder.\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n<!-- Configuration -->\n\n## Configuration\n\n- Click the 3 dots in the righthand corner and go to **Settings**.\n- Go to **Plugin Settings**.\n- Select correct plugin.\n\n[![Configuration screen][configuration-screenshot]](##configuration)\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n### Meta-data\n\nThis plugin uses YAML for the Hugo [front-matter](https://gohugo.io/content-management/front-matter/). It will convert Logseq page-properties to Hugo front matter.\n\nLogseq _keywords_ are lowercase converted to Hugo keywords, and **category** in Logseq is translated to _categories_ for use with Hugo. Logseq _links_ (`[[like_this]]`) are stripped of `[[` and `]]`.\n\nAll other _keywords_ are just converted to Hugo _keywords_.\n\nFor now you _must_ add **date** with the posts date in the form of \"2012-04-06\" to your Logseq page-properties.\n\n```markdown\ndate:: 2012-04-06\n```\n\n<h3 id=\"configuring_hugo\">Configuring Hugo</h3>\n\n[Hugo][hugo] does not by default support backlinks. Use a snippet like the following to simulate backlinks. It will parse every page for local links. This snippet should be placed in `~yourhugo/layouts/partials/backlinks.html`.\n\n```html\n{{ $re := $.File.BaseFileName }} {{ $backlinks := slice }} {{ range where\n.Site.RegularPages \"Type\" \"page\" }} {{ if and (findRE $re .RawContent) (not (eq\n$re .File.BaseFileName)) }} {{ $backlinks = $backlinks | append . }} {{ end }}\n{{ end }} {{ if gt (len $backlinks) 0 }}\n<aside>\n  <h3>Backlinks</h3>\n  <div class=\"backlinks\">\n    <ul>\n      {{ range $backlinks }}\n      <li class=\"capitalize\"><a href=\"{{ .RelPermalink }}\">{{ .Title }}</a></li>\n      {{ end }}\n    </ul>\n  </div>\n</aside>\n{{ else }}\n<aside>\n  <h4>No notes link to this note</h4>\n</aside>\n{{ end }}\n\n<aside class=\"related\">\n  {{ $related := .Site.RegularPages.Related . | complement $backlinks | first 3\n  -}} {{ with $related -}}\n  <h3>slightly related</h3>\n  <ul>\n    {{ range . -}}\n    <li class=\"capitalize\"><a href=\"{{ .RelPermalink }}\">{{ .Title }}</a></li>\n    {{ end -}}\n  </ul>\n  {{ end -}}\n</aside>\n```\n\n<img src=\"./images/backlinks.png\" width=\"200px\">\n\n### Admonitions\n\nLogseq has several built-in adminitions, namely:\n\n- caution\n- example\n- important\n- note\n- pinned\n- tip\n- quote\n- warning\n\nThese get converted to:\n\n```markdown\n{{< logseq/orgCAUTION >}}Caution here{{< / logseq/orgCAUTION >}}\n{{< logseq/orgEXAMPLE >}}This is an example{{< / logseq/orgEXAMPLE >}}\n{{< logseq/orgIMPORTANT >}}This is important{{< / logseq/orgIMPORTANT >}}\n{{< logseq/orgNOTE >}}This is a note{{< / logseq/orgNOTE >}}\n{{< logseq/orgPINNED >}}This is pinned{{< / logseq/orgPINNED >}}\n{{< logseq/orgTIP >}}This is a tip{{< / logseq/orgTIP >}}\n{{< logseq/orgQUOTE >}}This is a quote{{< / logseq/orgQUOTE >}}\n{{< logseq/orgWARNING >}}This is a warning{{< / logseq/orgWARNING >}}\n```\n\nSo Hugo needs those in `~yourhugo/layouts/shortcodes/logseq/`:\n\n```txt\norgCAUTION.html\norgEXAMPLE.html\norgIMPORTANT.html\norgNOTE.html\norgPINNED.html\norgQUOTE.html\norgTIP.html\norgWARNING.html\n```\n\nAnd they should contain something along the lines of:\n\n```html\n<div class=\"caution {{ .Get 0 }}\">{{ .Inner | $.Page.RenderString }}</div>\n```\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n## Website templates\n\nThere are some basic website templates you can take it as a reference.\n\n1. [logseq-hugo-template](https://github.com/sawhney17/logseq-hugo-template/), by [sawhney17](https://github.com/sawhney17).\n   1. You can host your personal website with your favorite web hosting providers.\n2. [Logseq-Hugo-Template](https://github.com/CharlesChiuGit/Logseq-Hugo-Template), by [CharlesChiuGit](https://github.com/CharlesChiuGit).\n   1. Host your personal website with free [GitHub pages](https://pages.github.com/).\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n<!-- Issues -->\n\n## Issues\n\nSee the [open issues](https://github.com/sawhney17/logseq-schrodinger/issues) for a full list of proposed features (and known issues).\n\n### What works\n\n- Local Hugo links (but Logseq uses one folder for everything, so Hugo does too)\n- Block refs(!) — On conversion the block is pulled from the other location\n- Images\n- Basic Markdown styling (including highlighting!)\n\n### What is known to _not_ work\n\n- Indentation Logseq ➡ Hugo is still a work-in-progress\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n<!-- CONTRIBUTING -->\n\n## Contributing\n\nContributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.\n\nIf you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag \"enhancement\".\nDon't forget to give the project a star! Thanks again!\n\n1. Fork the Project\n2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the Branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n<!-- LICENSE -->\n\n## License\n\nDistributed under the MIT License. See `LICENSE.txt` for more information.\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n<!-- CONTACT -->\n\n## Contact\n\nAryan Sawhney - [@Aryan Sawhney](https://twitter.com/aryansawhney17)\n\nProject Link: [https://github.com/sawhney17/logseq-schrodinger](https://github.com/sawhney17/logseq-schrodinger)\n\n<p align=\"right\">(<a href=\"#top\">back to top</a>)</p>\n\n## Acknowledgments\n\nI would like to thank Alex Qwxlea ([@twitter_handle](https://twitter.com/QwxleaA)) for the idea to write this Logseq plugin. Also for breaking the plugin after I wrote it. And finally, thank him for adding this note: Qwxlea, you're the best 😁!\n\n<!-- MARKDOWN LINKS & IMAGES -->\n<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->\n\n[contributors-shield]: https://img.shields.io/github/contributors/sawhney17/logseq-schrodinger.svg?style=for-the-badge\n[contributors-url]: https://github.com/sawhney17/logseq-schrodinger/graphs/contributors\n[forks-shield]: https://img.shields.io/github/forks/sawhney17/logseq-schrodinger.svg?style=for-the-badge\n[forks-url]: https://github.com/sawhney17/logseq-schrodinger/network/members\n[stars-shield]: https://img.shields.io/github/stars/sawhney17/logseq-schrodinger.svg?style=for-the-badge\n[stars-url]: https://github.com/sawhney17/logseq-schrodinger/stargazers\n[issues-shield]: https://img.shields.io/github/issues/sawhney17/logseq-schrodinger.svg?style=for-the-badge\n[issues-url]: https://github.com/sawhney17/logseq-schrodinger/issues\n[license-shield]: https://img.shields.io/github/license/sawhney17/logseq-schrodinger.svg?style=for-the-badge\n[license-url]: https://github.com/sawhney17/logseq-schrodinger/blob/master/LICENSE.txt\n[product-screenshot]: images/screenshot.jpg\n[configuration-screenshot]: ./images/configuration.png\n[hugo]: https://gohugo.io\n[logseq]: https://logseq.com\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>logseq-custom-workflows</title>\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script src=\"./src/index.tsx\" type=\"module\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"logseq-schrodinger\",\n  \"version\": \"1.3.2\",\n  \"description\": \"An awesome logseq plugin to export to Hugo Static sites and jumpstart your digital garden 🌱!\",\n  \"main\": \"dist/index.html\",\n  \"targets\": {\n    \"main\": false\n  },\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"build\": \"parcel build --no-source-maps index.html --public-url ./\"\n  },\n  \"keywords\": [],\n  \"author\": \"Aryan Sawhney & Alex Qwxlea\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"@logseq/libs\": \"^0.0.14\",\n    \"file-saver\": \"^2.0.5\",\n    \"jszip\": \"^3.8.0\",\n    \"postcss\": \"^8.4.12\",\n    \"react\": \"^18.0.0\",\n    \"react-dom\": \"^18.0.0\",\n    \"tailwindcss\": \"^3.0.23\"\n  },\n  \"logseq\": {\n    \"id\": \"logseq-hugo-plugin\",\n    \"title\": \"logseq-hugo-plugin\",\n    \"icon\": \"./icon.png\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^17.0.38\",\n    \"@types/react-dom\": \"^17.0.11\",\n    \"autoprefix\": \"^1.0.1\",\n    \"autoprefixer\": \"^10.4.2\",\n    \"buffer\": \"^6.0.3\",\n    \"parcel\": \"^2.0.0\",\n    \"postcss-cli\": \"^9.1.0\",\n    \"postcss-import\": \"^14.0.2\"\n  }\n}\n"
  },
  {
    "path": "src/App.css",
    "content": "html {\n  line-height: 1.5; /* 1 */\n  -webkit-text-size-adjust: 100%; /* 2 */\n  -moz-tab-size: 4; /* 3 */\n  -o-tab-size: 4;\n     tab-size: 4; /* 3 */\n  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */;\n  --cl-charcoal: #3f3f3f;\n  --cl-darkjungle: #1e1e1e;\n  --cl-thunder: #4c4c4c;\n\n\n}\n.overlay {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100vw;\n  height: 100vh;\n  background: hsla(0, 0%, 10%, 0.5);\n  z-index: 9;\n}\n.smartblock-popup {\n  padding: 1em;\n  background: rgb(40, 157, 165);\n  z-index: 9;\n  border-radius: 10px;\n\n  /* -ms-transform: translate(-50%, -50%);\n  transform: translate(-50%, -50%); */\n}\n.popup-content {\n  z-index: 10;\n}\n\n.centered-element {\n  margin: 0;\n  position: absolute;\n  top: 30%;\n  transform: translateY(-50%);\n}\n\n.grid-container {\n  display: grid;\n  grid-template-columns: 1fr 1fr;\n  grid-gap: 20px;\n  padding-top: 10px;\n}\n.label-class {\n  color: whitesmoke;\n  font-family: system-ui;\n  padding-bottom: 10px;\n  font-size: larger;\n}\n.smartblock-inserter {\n  background-color: var(--cl-darkjungle);\n  padding: 1em;\n  /* background: rgb(40, 157, 165);  */\n  z-index: 9;\n  border-radius: 15px;\n  /* top: 10%; */\n  width: 300px;\n  display: flex;\n  transform: translate(0, 5px);\n  flex-direction: column;\n  color: #ffffff;\n  /* align-items: center; */\n  background-color: var(--cl-darkjungle) !important;\n  border-top: 1px solid var(--cl-charcoal);\n  border-right: 1px solid var(--cl-charcoal);\n  border-bottom: 1px solid var(--cl-charcoal);\n  border-left: 1px solid var(--cl-charcoal);\n  font-size: 1.2em;\n  font-family: system-ui;\n  padding: 0.5em;\n  border-radius: 5px;\n  outline: none;\n  /* box-shadow: 0 0 0 0 transparent; */\n  /* transition: box-shadow 0.3s ease-in-out; */\n}\n.cp__palette-input {\n  width: 100%;\n  height: 100%;\n  border: none;\n  background: transparent;\n  color: #ffffff;\n  font-size: 1.2em;\n  font-family: system-ui;\n  padding: 0.5em;\n  border-radius: 5px;\n  outline: none;\n  box-shadow: 0 0 0 0 transparent;\n  transition: box-shadow 0.3s ease-in-out;\n}\n.searchItem {\n  color: #ffffff;\n}\n.full-width {\n  width: 100%;\n}"
  },
  {
    "path": "src/App.tsx",
    "content": "import React, { useRef, useState } from \"react\";\nimport ReactDOM from \"react-dom\";\nimport \"./App.css\";\nimport \"./tailwind.css\";\nimport { getBlocksInPage } from \"./utils\";\n\nconst App: React.FC = () => {\n  const [noteName, setNoteName] = useState(() => {\n    logseq.Editor.getCurrentPage().then((page) => {\n      setNoteName(page.originalName);\n      setHugoFileName(page.name)\n    });\n    return \"noteName\";\n  });\n  const [hugoFileName, setHugoFileName] = useState(\"\");\n  const [originalDate, setOriginalDate] = useState(\"\");\n  const [updatedDate, setUpdatedDate] = useState(\"\");\n  const [mappedtagsValues, setMappedtagsValues] = useState([{ tags: \"tag\" }]);\n  const [mappedCategoryValues, setMappedCategoryValues] = useState([\n    { category: \"category\" },\n  ]);\n\n  //create a function to handle change of inputs\n  const handletagsChange = (\n    event: React.ChangeEvent<HTMLInputElement>,\n    index\n  ) => {\n    const { name, value } = event.target;\n\n    let targettedValues = [...mappedtagsValues];\n    targettedValues[index].tags = value;\n    setMappedtagsValues(targettedValues);\n  };\n  const handleCategoryChange = (\n    event: React.ChangeEvent<HTMLInputElement>,\n    index\n  ) => {\n    const { name, value } = event.target;\n\n    let targettedValues = [...mappedCategoryValues];\n    targettedValues[index].category = value;\n    setMappedCategoryValues(targettedValues);\n  };\n  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n    const { name, value } = event.target;\n    if (name === \"noteName\") {\n      setNoteName(value);\n    } else if (name === \"hugoFileName\") {\n      setHugoFileName(value);\n    } else if (name === \"originalDate\") {\n      setOriginalDate(value);\n    } else if (name === \"updatedDate\") {\n      setUpdatedDate(value);\n    }\n  };\n\n  const createNewtags = () => {\n    setMappedtagsValues([...mappedtagsValues, { tags: \"tag\" }]);\n  };\n  const deletetags = (index: number) => {\n    let targettedValues = [...mappedtagsValues];\n    targettedValues.splice(index, 1);\n    setMappedtagsValues(targettedValues);\n  };\n  const createNewCategory = () => {\n    setMappedCategoryValues([...mappedCategoryValues, { category: \"category\" }]);\n  };\n  const deleteCategory = (index: number) => {\n    let targettedValues = [...mappedCategoryValues];\n    targettedValues.splice(index, 1);\n    setMappedCategoryValues(targettedValues);\n  };\n  return (\n    <div>\n      <div>\n        <div className=\"flex justify-center\">\n          <div className=\"smartblock-inserter\">\n            <h1 className=\"full-width text-left text-2xl font-bold\">\n              Hugo Export\n            </h1>\n            <br></br>\n            <div className=\"overflow-auto h-80\">\n              <div className=\"-\">\n                <div className=\"grid justify-between gap-2 px-2\">\n                  <p>Note Name</p>\n                  <input\n                    name=\"noteName\"\n                    type=\"text\"\n                    className=\"text-black rounded-md\"\n                    value={noteName}\n                    onChange={handleChange}\n                  />\n                  <p>Hugo File Name</p>\n                  <input\n                    name=\"hugoFileName\"\n                    type=\"text\"\n                    className=\"text-black rounded-md\"\n                    value={hugoFileName}\n                    onChange={handleChange}\n                  />\n                  <p>Original Date</p>\n                  <input\n                    name=\"originalDate\"\n                    type=\"date\"\n                    className=\"text-black rounded-md\"\n                    value={originalDate}\n                    onChange={handleChange}\n                  />\n                  <p>Updated Post Date</p>\n                  <input\n                    name=\"updatedDate\"\n                    type=\"date\"\n                    className=\"text-black rounded-md\"\n                    value={updatedDate}\n                    onChange={handleChange}\n                  />\n                  <div className=\"flex justify-between\">\n                    <p className=\"inline-block\">Tags</p>\n                    <div className=\"inline-block px-2\">\n                      <button\n                        onClick={createNewtags}\n                        className=\"bg-white text-black font-bold px-2 rounded-sm h-max\"\n                      >\n                        +\n                      </button>\n                    </div>\n                  </div>\n                  {mappedtagsValues.map((val, index) => {\n                    return (\n                      <div className=\"flex justify-between pb-2\">\n                        <input\n                          name={index.toString()}\n                          type=\"text\"\n                          className=\"text-black rounded-md\"\n                          value={mappedtagsValues[index].tags}\n                          onChange={(e) => handletagsChange(e, index)}\n                        ></input>\n                        <div className=\"px-2\">\n                          <button\n                            onClick={() => {\n                              deletetags(index);\n                            }}\n                            className=\"bg-white text-black font-bold px-3 pr-2 rounded-sm h-max\"\n                          >\n                            -\n                          </button>\n                        </div>\n                      </div>\n                    );\n                  })}\n\n                  <div>\n                    <div className=\"flex justify-between pb-2\"></div>\n                  </div>\n                  <div>\n                    <div className=\"flex justify-between\">\n                      <p className=\"inline-block\">Categories</p>\n                      <div className=\"p-2 inline-block\">\n                        <button\n                          onClick={createNewCategory}\n                          className=\"bg-white text-black font-bold px-2 pr-2 rounded-sm h-max\"\n                        >\n                          +\n                        </button>\n                      </div>\n                    </div>\n                    {mappedCategoryValues.map((val, index) => {\n                      return (\n                        <div className=\"flex justify-between pb-2\">\n                          <input\n                            name={index.toString()}\n                            type=\"Categories\"\n                            className=\"text-black rounded-md\"\n                            value={mappedCategoryValues[index].category}\n                            onChange={(e) => handleCategoryChange(e, index)}\n                          ></input>\n                          <div className=\"px-2\">\n                            <button\n                              onClick={() => {\n                                deleteCategory(index);\n                              }}\n                              className=\"bg-white text-black font-bold px-3 pr-2 rounded-sm h-max\"\n                            >\n                              -\n                            </button>\n                          </div>\n                        </div>\n                      );\n                    })}\n                  </div>\n                </div>\n              </div>\n            </div>\n            <div className=\"flex justify-center\">\n              <button\n                className=\" border-light-300 border-2 w-200px hover:bg-black p-3 px-5 rounded-lg\"\n                onClick={async () => {\n                  getBlocksInPage(\n                    { page: await ( logseq.Editor.getCurrentPage()) },\n                    true,\n                    true,\n                    mappedtagsValues,\n                    [\n                      { updatedDate: updatedDate },\n                      { originalDate: originalDate },\n                    ],\n                    [{ noteName: noteName }, { hugoFileName: hugoFileName }],\n                    mappedCategoryValues\n                  );\n                }}\n              >\n                Convert\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\nexport default App;\n"
  },
  {
    "path": "src/handleClosePopup.ts",
    "content": "export const handleClosePopup = () => {\n  //ESC\n  document.addEventListener(\n    'keydown',\n    function (e) {\n      if (e.keyCode === 27) {\n        logseq.hideMainUI({ restoreEditingCursor: true });\n      }\n      e.stopPropagation();\n    },\n    false\n  );\n};\n"
  },
  {
    "path": "src/index.tsx",
    "content": "import \"@logseq/libs\";\nimport {\n  BlockEntity,\n  PageEntity,\n  SettingSchemaDesc,\n} from \"@logseq/libs/dist/LSPlugin\";\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport App from \"./App\";\nimport { handleClosePopup } from \"./handleClosePopup\";\nimport { getAllPublicPages, getBlocksInPage } from \"./utils\";\n\nexport var path = \"\";\n\nconst linkFormats = [\"[[Logseq Format]]\", \"Without brackets\"];\n\nlet settings: SettingSchemaDesc[] = [\n  {\n    key: \"linkFormat\",\n    type: \"enum\",\n    enumChoices: linkFormats,\n    enumPicker: \"radio\",\n    title: \"How would you like links to be formatted\",\n    description:\n      \"Do you want your exported links with or without brackets? If a page that is linked to is public, the link will automatically be hyperlinked in the hugo export, otherwise, this setting will be applied\",\n    default: linkFormats[0],\n  },\n  {\n    key: \"bulletHandling\",\n    type: \"enum\",\n    enumChoices: [\"Convert Bullets\", \"Remove All Bullets\"],\n    enumPicker: \"radio\",\n    title: \"How would you like Logseq's bullets to be handled\",\n    description:\n      \"How would you like Logseq's bullets to be handled, convert to hugo's native style or remove all bullets?\",\n    default: \"Convert Bullets\",\n  },\n  {\n    key: \"exportTasks\",\n    type: \"boolean\",\n    title: \"Do you want tasks to exported to Hugo?\",\n    description:\n      \"Yes, blocks with tasks will be exported: (TODO DOING DONE LATER NOW WAITING)\",\n    default: false,\n  },\n  {\n    key: \"leafTitle\",\n    type: \"boolean\",\n    title: \"Do you want to use only the leaf title?\",\n    description:\n      \"Use the end of title (what is after the last / in Logseq page title) as title instead of the whole title hierarchy.\" +\n      \"\\nExample: for 'parent/branch/leaf', title would be 'leaf'\",\n    default: false,\n  },\n  {\n    key: \"tagsFromTitle\",\n    type: \"boolean\",\n    title: \"Do you want to add Logseq title hierarchy to tags?\",\n    description: \"Add title hierarchy to tags?\",\n    default: false,\n  },\n  {\n    key: \"assetsPath\",\n    type: \"string\",\n    title: \"Path to assets\",\n    description: \"Path of each assets will start with the following\",\n    default: \"assets\",\n  },\n  {\n    key: \"pagesPath\",\n    type: \"string\",\n    title: \"Path to pages\",\n    description: \"Path of each pages will start with the following\",\n    default: \"content/pages\",\n  },\n  {\n    key: \"journalPath\",\n    type: \"string\",\n    title: \"Path to journal pages\",\n    description: \"Path of each journal pages will start with the following\",\n    default: \"content/posts\",\n  },\n];\nconst main = async () => {\n  console.log(\"Logseq Schrödinger plugin loaded\");\n  ReactDOM.render(\n    //Render react component\n    <React.StrictMode>\n      <App />\n    </React.StrictMode>,\n    document.getElementById(\"app\"),\n  );\n\n  logseq.setMainUIInlineStyle({\n    position: \"fixed\",\n    zIndex: 999,\n    transform: \"translateX(-50%)\",\n  });\n  function createModel() {\n    return {\n      show(e: any) {\n        const { rect } = e;\n\n        logseq.setMainUIInlineStyle({\n          top: `${rect.top + 25}px`,\n          left: `${rect.right - 17}px`,\n        });\n\n        logseq.toggleMainUI();\n        handleClosePopup();\n      },\n\n      export() {\n        getAllPublicPages();\n      },\n    };\n  }\n\n  logseq.provideModel(createModel());\n  logseq.setMainUIInlineStyle({\n    zIndex: 11,\n  });\n  logseq.App.registerPageMenuItem(\n    \"Export all public pages to hugo\",\n    getAllPublicPages,\n  );\n\n  logseq.App.registerUIItem(\"toolbar\", {\n    key: \"hugo-single-export\",\n    template: `\n      <a class=\"button\" data-on-click=\"show\" data-rect>\n        <i class=\"ti ti-file-zip\"></i>\n      </a>\n    `,\n  });\n  logseq.App.registerUIItem(\"toolbar\", {\n    key: \"export-public-pages-to-hugo\",\n    template: `\n      <a class=\"button\" data-on-click=\"export\" data-rect>\n        <i class=\"ti ti-database-export\"></i>\n      </a>\n    `,\n  });\n\n  logseq.useSettingsSchema(settings);\n  path = (await logseq.App.getCurrentGraph()).path;\n};\n\nlogseq.ready(main).catch(console.error);\n"
  },
  {
    "path": "src/tailwind.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "src/utils.tsx",
    "content": "import \"@logseq/libs\";\n\nimport { saveAs } from \"file-saver\";\nimport JSZip, { file } from \"jszip\";\nimport { title } from \"process\";\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\n\nimport {\n  BlockEntity,\n  PageEntity,\n  SettingSchemaDesc,\n} from \"@logseq/libs/dist/LSPlugin\";\n\nimport App from \"./App\";\nimport { handleClosePopup } from \"./handleClosePopup\";\nimport { linkFormats, path } from \"./index\";\n\nexport var blocks2 = [];\nvar errorTracker = [];\nvar zip = new JSZip();\nvar imageTracker = [];\nlet allPublicPages;\nlet allPublicLinks = []; //list of all exported pages\n\n//Retired function\n//I kept on missing pages?!?!?!\n//Never figured out why\nexport async function getAllPublicPages_orig() {\n  errorTracker = [];\n  logseq.DB.q(\"(page-property public)\").then((result) => {\n    const mappedResults = result.map((page) => {\n      return page.name;\n    });\n    for (const x in mappedResults) {\n      if (x != `${mappedResults.length - 1}`) {\n        getBlocksInPage({ page: mappedResults[x] }, false, false);\n      } else {\n        getBlocksInPage({ page: mappedResults[x] }, false, true);\n      }\n    }\n  });\n}\n\nexport async function getAllPublicPages() {\n  //needs to be both public, and a page (with a name)\n  const query =\n    \"[:find (pull ?p [*]) :where [?p :block/properties ?pr] [(get ?pr :public) ?t] [(= true ?t)][?p :block/name ?n]]\";\n  allPublicPages = await logseq.DB.datascriptQuery(query);\n  allPublicPages = allPublicPages?.flat(); //FIXME is this needed?\n\n  for (const x of allPublicPages) {\n    allPublicLinks.push(x[\"original-name\"].toLowerCase());\n  }\n\n  for (const x in allPublicPages) {\n    if (x != `${allPublicPages.length - 1}`) {\n      await getBlocksInPage({ page: allPublicPages[x] }, false, false);\n    } else {\n      await getBlocksInPage({ page: allPublicPages[x] }, false, true);\n    }\n  }\n}\n\nfunction hugoDate(timestamp) {\n  let date = new Date(timestamp);\n\n  //if date.getdate does not have a zero, add A ZERO BEFORE IT\n  let month;\n  if (date.getMonth() + 1 < 10) {\n    month = `0${date.getMonth() + 1}`;\n  } else {\n    month = `${date.getMonth() + 1}`;\n  }\n  let day;\n  if (date.getDate() < 10) {\n    day = `0${date.getDate()}`;\n  } else {\n    day = `${date.getDate()}`;\n  }\n\n  return `${date.getFullYear()}-${month}-${day}`;\n}\n\n//parse files meta-data\nasync function parseMeta(\n  curPage,\n  tagsArray = [],\n  dateArray = [],\n  titleDetails = [],\n  categoriesArray = [],\n) {\n  let propList = [];\n\n  //get all properties - fix later\n  if (curPage?.page.properties != undefined) {\n    propList = curPage?.page.properties;\n  }\n  //Title\n  //FIXME is filename used?\n  if (logseq.settings.leafTitle) {\n    propList.title = curPage.page[\"original-name\"].split(\"/\").slice(-1)[0];\n  } else {\n    propList.title = curPage.page[\"original-name\"];\n  }\n  if (titleDetails.length > 0) {\n    propList.title = titleDetails[0].noteName;\n    propList.fileName = titleDetails[1].hugoFileName;\n  }\n\n  //Tags\n  propList.tags = curPage?.page.properties.tags\n    ? curPage?.page.properties.tags\n    : [];\n  for (const tag in tagsArray) {\n    propList.tags.push(tagsArray[tag].tags);\n  }\n  // Add tags from title\n  if (logseq.settings.tagsFromTitle) {\n    curPage.page[\"original-name\"].split(\"/\").slice(0, -1).forEach((tag) =>\n      propList.tags.push(tag)\n    );\n  }\n\n  //Categories - 2 possible spellings!\n  const tmpCat = curPage?.page.properties.category\n    ? curPage?.page.properties.category\n    : [];\n  propList.categories = curPage?.page.properties.categories\n    ? curPage?.page.properties.categories\n    : tmpCat;\n  if (categoriesArray != []) {\n    let formattedCategoriesArray = [];\n    for (const category in categoriesArray) {\n      formattedCategoriesArray.push(categoriesArray[category].category);\n    }\n    if (propList.categories != undefined) {\n      for (const category in formattedCategoriesArray) {\n        propList.categories.push(formattedCategoriesArray[category]);\n      }\n    } else {\n      propList.categories = formattedCategoriesArray;\n    }\n  }\n\n  //Date - if not defined, convert Logseq timestamp\n  propList.date = curPage?.page.properties.date\n    ? curPage?.page.properties.date\n    : hugoDate(curPage.page[\"created-at\"]);\n  propList.lastMod = curPage?.page.properties.lastmod\n    ? curPage?.page.properties.lastmod\n    : hugoDate(curPage.page[\"updated-at\"]);\n  if (dateArray.length > 0) {\n    propList.date = dateArray[1].originalDate;\n    propList.lastMod = dateArray[0].updatedDate;\n  }\n\n  //these properties should not be exported to Hugo\n  const nope = [\"filters\", \"public\"];\n  for (const nono of nope) {\n    delete propList[nono];\n  }\n\n  //convert propList to Hugo yaml\n  // https://gohugo.io/content-management/front-matter/\n  let ret = `---`;\n  for (let [prop, value] of Object.entries(propList)) {\n    if (Array.isArray(value)) {\n      ret += `\\n${prop}:`;\n      value.forEach((element) => (ret += `\\n- ${element}`));\n    } else {\n      ret += `\\n${prop}: ${value}`;\n    }\n  }\n  ret += \"\\n---\";\n  return ret;\n}\n\nexport async function getBlocksInPage(\n  e,\n  singleFile,\n  isLast,\n  tagsArray = [],\n  dateArray = [],\n  titleDetails = [],\n  categoriesArray = [],\n  allPublicPages = [],\n) {\n  //if e.page.originalName is undefined, set page to equal e.page.original-name\n  let curPage = e.page;\n  if (curPage.originalName != undefined) {\n    curPage[\"original-name\"] = curPage.originalName;\n  }\n\n  const docTree = await logseq.Editor.getPageBlocksTree(\n    curPage[\"original-name\"],\n  );\n\n  const metaData = await parseMeta(\n    e,\n    tagsArray,\n    dateArray,\n    titleDetails,\n    categoriesArray,\n  );\n  // parse page-content\n\n  let finalString = await parsePage(metaData, docTree);\n\n  // FIXME ??\n  if (singleFile) {\n    logseq.hideMainUI();\n    handleClosePopup();\n    download(`${titleDetails[1].hugoFileName}.md`, finalString);\n  } else {\n    // console.log(`e[\"original-name\"]: ${e[\"original-name\"]}`);\n    //page looks better in the URL\n    let path = curPage[\"journal?\"]\n      ? logseq.settings.journalPath\n      : logseq.settings.pagesPath;\n    path = path.replace(/\\/$/, \"\"); // remove trailing slash\n    zip.file(\n      `${path}/${\n        curPage[\"original-name\"].replaceAll(\n          /([\\u2700-\\u27BF]|[\\uE000-\\uF8FF]|\\uD83C[\\uDC00-\\uDFFF]|\\uD83D[\\uDC00-\\uDFFF]|[\\u2011-\\u26FF]|\\uD83E[\\uDD10-\\uDDFF])/g,\n          \"\",\n        )\n      }.md`,\n      finalString,\n    );\n\n    if (isLast) {\n      setTimeout(() => {\n        console.log(zip);\n        zip.generateAsync({ type: \"blob\" }).then(function (content) {\n          // see FileSaver.js\n          saveAs(content, \"publicExport.zip\");\n          //wait one second\n          // setTimeout(() => {\n          //   saveAs(content, \"publicExport.zip\");\n          // }, 1000);\n          zip = new JSZip();\n        });\n      }, imageTracker.length * 102);\n    }\n  }\n}\n\nasync function parsePage(finalString: string, docTree) {\n  // console.log(\"DB parsePage\")\n  for (const x in docTree) {\n    // skip meta-data\n    if (!(parseInt(x) === 0 && docTree[x].level === 1)) {\n      //parseText will return 'undefined' if a block skipped\n      const ret = await parseText(docTree[x]);\n      if (typeof ret != \"undefined\") {\n        finalString = `${finalString} \\n${ret} `;\n      }\n\n      if (docTree[x].children.length > 0) {\n        finalString = await parsePage(finalString, docTree[x].children);\n      }\n    }\n  }\n  return finalString;\n}\n\nfunction parseLinks_old(text: string, allPublicPages) {\n  //returns text withh all links converted\n\n  // FIXME This needs to be rewritten (later) so we don't loop all the pages twice\n  // conversion of links to hugo syntax https://gohugo.io/content-management/cross-references/\n  // Two kinds of links: [[a link]]\n  //                     [A description]([[a link]])\n  // Regular links are done by Hugo [logseq](https://logseq.com)\n  const reLink: RegExp = /\\[\\[.*?\\]\\]/g;\n  const reDescrLink: RegExp = /\\[([a-zA-Z ]*?)\\]\\(\\[\\[(.*?)\\]\\]\\)/g;\n  //[garden]([[digital garden]])\n  if (logseq.settings.linkFormat == \"Hugo Format\") {\n    if (reDescrLink.test(text)) {\n      text = text.replaceAll(reDescrLink, (result) => {\n        for (const x in allPublicPages) {\n          if (\n            result[2].toLowerCase ==\n              allPublicPages[x][\"original-name\"].toLowerCase\n          ) {\n            const txt = reDescrLink.exec(result);\n            return txt\n              ? `[${txt[1]}]({{ < ref \"${txt[2]}\" >}\n})`\n              : \"\";\n            // return (txt) ? `[${ txt[1] }]({{ < ref \"${txt[2].replaceAll(\" \",\"_\")}\" >}})` : \"\"\n          }\n        }\n      });\n    }\n    text = text.replaceAll(reLink, (match) => {\n      const txt = match.substring(2, match.length - 2);\n      for (const x in allPublicPages) {\n        if (\n          txt.toUpperCase() == allPublicPages[x][\"original-name\"].toUpperCase()\n        ) {\n          return `[${txt}]({{\n  < ref \"${\n            allPublicPages[x][\"original-name\"].replaceAll(\n              \" \",\n              \" \",\n            )\n          } \" >}})`;\n        }\n      }\n      return txt;\n    });\n  }\n  if (logseq.settings.linkFormat == \"Without brackets\") {\n    text = text.replaceAll(\"[[\", \"\");\n    text = text.replaceAll(\"]]\", \"\");\n  }\n  return text;\n}\n\nfunction parseLinks(text: string, allPublicPages) {\n  //returns text with all links converted\n\n  // conversion of links to hugo syntax https://gohugo.io/content-management/cross-references/\n  // Two kinds of links: [[a link]]\n  //                     [A description]([[a link]])\n  // Regular links are done by Hugo [logseq](https://logseq.com)\n  const reLink: RegExp = /\\[\\[(.*?)\\]\\]/gmi;\n  const reDescrLink: RegExp = /\\[([a-zA-Z ]*?)\\]\\(\\[\\[(.*?)\\]\\]\\)/gmi;\n\n  // FIXME why doesn't this work?\n  // if (! reDescrLink.test(text) && ! reLink.test(text)) return text\n\n  let result;\n  while (result = reDescrLink.exec(text) || reLink.exec(text)) {\n    if (allPublicLinks.includes(result[result.length - 1].toLowerCase())) {\n      text = text.replace(\n        result[0],\n        `[${result[1]}]({{< ref \"/pages/${result[result.length - 1]}\" >}})`,\n      );\n    }\n  }\n  if (logseq.settings.linkFormat == \"Without brackets\") {\n    text = text.replaceAll(\"[[\", \"\");\n    text = text.replaceAll(\"]]\", \"\");\n  }\n  return text;\n}\n\nasync function parseNamespaces(text: string, blockLevel: number) {\n  const namespace: RegExp = /{{namespace\\s([^}]+)}}/gmi;\n\n  let result;\n  while (result = namespace.exec(text)) {\n    const currentNamespaceName = result[result.length - 1];\n\n    const query =\n      `[:find (pull ?c [*]) :where [?p :block/name \"${currentNamespaceName.toLowerCase()}\"] [?c :block/namespace ?p]]`;\n    let namespacePages = await logseq.DB.datascriptQuery(query);\n    namespacePages = namespacePages?.flat(); //FIXME is this needed?\n\n    let txtBeforeNamespacePage: string = \"\";\n    if (logseq.settings.bulletHandling == \"Convert Bullets\") {\n      txtBeforeNamespacePage = \" \".repeat(blockLevel * 2) + \"+ \";\n    }\n\n    let namespaceContent = `**Namespace [[${currentNamespaceName}]]**\\n\\n`;\n    if (allPublicLinks.includes(currentNamespaceName.toLowerCase())) {\n      namespaceContent = namespaceContent.replace(\n        `[[${currentNamespaceName}]]`,\n        `[${currentNamespaceName}]({{< ref \"/pages/${currentNamespaceName}\" >}})`,\n      );\n    }\n\n    for (const page of namespacePages) {\n      const pageOrigName = page[\"original-name\"];\n      if (allPublicLinks.includes(page[\"original-name\"].toLowerCase())) {\n        const pageName = pageOrigName.replace(`${currentNamespaceName}/`, \"\");\n        namespaceContent = namespaceContent.concat(\n          txtBeforeNamespacePage +\n            `[${pageName}]({{< ref \"/pages/${pageOrigName}\" >}})\\n\\n`,\n        );\n      }\n    }\n\n    text = text.replace(result[0], namespaceContent);\n  }\n\n  return text;\n}\n\nfunction secondsToHms(d) {\n  d = Number(d);\n  var h = Math.floor(d / 3600);\n  var m = Math.floor(d % 3600 / 60);\n  var s = Math.floor(d % 3600 % 60);\n  var hDisplay = h > 9 ? String(h) : \"0\" + String(h);\n  var mDisplay = m > 9 ? String(m) : \"0\" + String(m);\n  var sDisplay = s > 9 ? String(s) : \"0\" + String(s);\n  return hDisplay + \":\" + mDisplay + \":\" + sDisplay;\n}\n\nasync function parseText(block: BlockEntity) {\n  //returns either a hugo block or `undefined`\n  let re: RegExp;\n  let text = block.content;\n  // console.log(\"block\", block)\n  let txtBefore: string = \"\";\n  let txtAfter: string = \"\\n\";\n  const prevBlock: BlockEntity = await logseq.Editor.getBlock(block.left.id, {\n    includeChildren: false,\n  });\n\n  //Block refs - needs to be at the beginning so the block gets parsed\n  //FIXME they need some indicator that it *was* an embed\n  const rxGetId =\n    /\\(\\(([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\)\\)/;\n  const rxGetEd =\n    /{{embed \\(\\(([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\)\\)}}/;\n  const blockId = rxGetEd.exec(text) || rxGetId.exec(text);\n  if (blockId != null) {\n    const block = await logseq.Editor.getBlock(blockId[1], {\n      includeChildren: true,\n    });\n\n    if (block != null) {\n      // console.log(\"DB blockId\", blockId)\n      text = text.replace(\n        blockId[0],\n        block.content.substring(0, block.content.indexOf(\"id::\")),\n      );\n    }\n  }\n\n  //task markers - skip\n  if (block.marker && !logseq.settings.exportTasks) return;\n\n  //Images\n  //FIXME ![image.png](../assets/image_1650196318593_0.png){:class medium, :height 506, :width 321}\n  //Logseq has extra info: height and width that can be used in an image template\n  //Get regex to check if text contains a md image\n  const reImage = /!\\[.*?\\]\\((.*?)\\)/g;\n  try {\n    text.match(reImage).forEach((element) => {\n      element.match(/(?<=!\\[.*\\])(.*)/g).forEach((match) => {\n        let finalLink = match.substring(1, match.length - 1);\n        // return (match.substring(1, match.length - 1))\n        text = text.replace(match, match.toLowerCase());\n        if (!finalLink.includes(\"http\") || !finalLink.includes(\".pdf\")) {\n          text = text.replace(\"../\", \"/\");\n          imageTracker.push(finalLink);\n          addImageToZip(finalLink);\n        }\n      });\n    });\n  } catch (error) {}\n\n  // FIXME for now all indention is stripped out\n  // Add indention — level zero is stripped of \"-\", rest are lists\n  // Experiment, no more lists, unless + or numbers\n  // (unless they're not)\n  if (logseq.settings.bulletHandling == \"Convert Bullets\") {\n    if (block.level > 1) {\n      txtBefore = \" \".repeat((block.level - 1) * 2) + \"+ \";\n      // txtBefore = \"\\n\" + txtBefore\n      if (prevBlock.level === block.level) txtAfter = \"\";\n    }\n  }\n  if (prevBlock.level === block.level) txtAfter = \"\";\n  //exceptions (logseq has \"-\" before every block, Hugo doesn't)\n  if (text.substring(0, 3) === \"```\") txtBefore = \"\";\n  // Don't - indent images\n  if (reImage.test(text)) txtBefore = \"\";\n  //indent text + add newline after block\n  text = txtBefore + text + txtAfter;\n\n  //internal links\n  text = parseLinks(text, allPublicPages);\n\n  //namespaces\n  text = await parseNamespaces(text, block.level);\n\n  //Change {{youtube-timestamp ts}} via regex\n  const yTimestamps = /{{youtube-timestamp (.*?)}}/g;\n  text = text.replaceAll(yTimestamps, (match) => {\n    const timestampRegex = /{{youtube-timestamp ([0-9]+)}}/;\n    const timestamp = timestampRegex.exec(match);\n    if (timestamp != null) {\n      return `@${secondsToHms(timestamp[1])}`;\n    }\n  });\n\n  //youtube embed\n  //Change {{youtube url}} via regex\n  const reYoutube = /{{youtube(.*?)}}/g;\n  text = text.replaceAll(reYoutube, (match) => {\n    const youtubeRegex = /(youtu(?:.*\\/v\\/|.*v\\=|\\.be\\/))([A-Za-z0-9_\\-]{11})/;\n    const youtubeId = youtubeRegex.exec(match);\n    if (youtubeId != null) {\n      return `{{< youtube ${youtubeId[2]} >}}`;\n    }\n  });\n\n  //height and width syntax regex\n  // {:height 239, :width 363}\n  const heightWidthRegex = /{:height\\s*[0-9]*,\\s*:width\\s*[0-9]*}/g;\n  text = text.replaceAll(heightWidthRegex, \"\");\n\n  //highlighted text, not supported in hugo by default!\n  re = /(==(.*?)==)/gm;\n  text = text.replace(re, \"{{< logseq/mark >}}$2{{< / logseq/mark >}}\");\n\n  re = /#\\+BEGIN_([A-Z]*)[^\\n]*\\n(.*)#\\+END_[^\\n]*/gms;\n  text = text.replace(re, \"{{< logseq/org$1 >}}$2{{< / logseq/org$1 >}}\");\n  // text = text.toLowerCase();\n\n  text = text.replace(/:LOGBOOK:|collapsed:: true/gi, \"\");\n  if (text.includes(\"CLOCK: [\")) {\n    text = text.substring(0, text.indexOf(\"CLOCK: [\"));\n  }\n\n  if (text.indexOf(`\\nid:: `) === -1) {\n    return text;\n  } else {\n    return text.substring(0, text.indexOf(`\\nid:: `));\n  }\n}\n\nfunction getBase64Image(img) {\n  var canvas = document.createElement(\"canvas\");\n  canvas.width = img.width;\n  canvas.height = img.height;\n  var ctx = canvas.getContext(\"2d\");\n  ctx.drawImage(img, 0, 0);\n  var dataURL = canvas.toDataURL(\"image/png\");\n  return dataURL.replace(/^data:image\\/(png|jpg);base64,/, \"\");\n}\n\nfunction addImageToZip(filePath) {\n  var element = document.createElement(\"img\");\n  let formattedFilePath = filePath.replace(\"..\", path);\n  element.setAttribute(\"src\", formattedFilePath);\n  element.style.display = \"none\";\n\n  document.body.appendChild(element);\n  setTimeout(() => {\n    var base64 = getBase64Image(element);\n    document.body.removeChild(element);\n    console.log(base64);\n    if (base64 != \"data:,\") {\n      zip.file(\n        logseq.settings.assetsPath + \"/\" +\n          filePath.split(\"/\")[filePath.split(\"/\").length - 1].toLowerCase(),\n        base64,\n        { base64: true },\n      );\n    } else {\n      // console.log(base64);\n    }\n  }, 100);\n}\n\n//FIXME don't get it, but it works\nfunction download(filename, text) {\n  var element = document.createElement(\"a\");\n  element.setAttribute(\n    \"href\",\n    \"data:text/plain;charset=utf-8,\" + encodeURIComponent(text),\n  );\n  // element.setAttribute('download', filename);\n  element.setAttribute(\"download\", filename);\n\n  element.style.display = \"none\";\n  document.body.appendChild(element);\n\n  element.click();\n\n  document.body.removeChild(element);\n}\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "module.exports = {\n  content: ['./src/**/*.{vue,js,ts,jsx,tsx,hbs,html}'],\n  darkMode: 'media', // or 'media' or 'class'\n  theme: {\n    extend: {\n      spacing: {\n        100: '50rem',\n      },\n    },\n  },\n  variants: {\n    extend: {},\n  },\n  plugins: [],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"ts-node\": {\n        \"target\": \"es2021\"\n    },\n}"
  }
]