[
  {
    "path": ".dockerignore",
    "content": "# flyctl launch added from .gitignore\n# Byte-compiled / optimized / DLL files\n**/__pycache__\n**/*.py[cod]\n**/*$py.class\n**/stdout\n\n# C extensions\n**/*.so\n\n# Distribution / packaging\n**/.Python\n**/build\n**/develop-eggs\n**/dist\n**/downloads\n**/eggs\n**/.eggs\n**/lib64\n**/parts\n**/sdist\n**/var\n**/wheels\n**/share/python-wheels\n**/*.egg-info\n**/.installed.cfg\n**/*.egg\n**/MANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n**/*.manifest\n**/*.spec\n\n# Installer logs\n**/pip-log.txt\n**/pip-delete-this-directory.txt\n\n# Unit test / coverage reports\n**/htmlcov\n**/.tox\n**/.nox\n**/.coverage\n**/.coverage.*\n**/.cache\n**/nosetests.xml\n**/coverage.xml\n**/*.cover\n**/*.py,cover\n**/.hypothesis\n**/.pytest_cache\n**/cover\n\n# Translations\n**/*.mo\n**/*.pot\n\n# Django stuff:\n**/*.log\n**/local_settings.py\n**/db.sqlite3\n**/db.sqlite3-journal\n\n# Flask stuff:\n**/instance\n**/.webassets-cache\n\n# Scrapy stuff:\n**/.scrapy\n\n# Sphinx documentation\n**/docs/_build\n\n# PyBuilder\n**/.pybuilder\n**/target\n\n# Jupyter Notebook\n**/.ipynb_checkpoints\n\n# IPython\n**/profile_default\n**/ipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control\n**/.pdm.toml\n**/.pdm-python\n**/.pdm-build\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n**/__pypackages__\n\n# Celery stuff\n**/celerybeat-schedule\n**/celerybeat.pid\n\n# SageMath parsed files\n**/*.sage.py\n\n# Environments\n**/.env\n**/.venv\n**/env\n**/venv\n**/ENV\n**/env.bak\n**/venv.bak\n\n# Spyder project settings\n**/.spyderproject\n**/.spyproject\n\n# Rope project settings\n**/.ropeproject\n\n# mkdocs documentation\nsite\n\n# mypy\n**/.mypy_cache\n**/.dmypy.json\n**/dmypy.json\n\n# Pyre type checker\n**/.pyre\n\n# pytype static type analyzer\n**/.pytype\n\n# Cython debug symbols\n**/cython_debug\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n\n# flyctl launch added from .pytest_cache/.gitignore\n# Created by pytest automatically.\n.pytest_cache/**/*\n\n# flyctl launch added from .ruff_cache/.gitignore\n# Automatically created by ruff.\n.ruff_cache/**/*\nfly.toml\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\nstdout\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# poetry\n#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control\n#poetry.lock\n\n# pdm\n#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.\n#pdm.lock\n#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it\n#   in version control.\n#   https://pdm.fming.dev/latest/usage/project/#working-with-version-control\n.pdm.toml\n.pdm-python\n.pdm-build/\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# PyCharm\n#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can\n#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore\n#  and can be added to the global gitignore or merged into this file.  For a more nuclear\n#  option (not recommended) you can uncomment the following to ignore the entire idea folder.\n#.idea/\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.5.0\n    hooks:\n      - id: check-ast\n      - id: check-merge-conflict\n      - id: check-case-conflict\n      - id: check-docstring-first\n      - id: end-of-file-fixer\n      - id: trailing-whitespace\n      - id: mixed-line-ending\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    # Ruff version.\n    rev: v0.5.1\n    hooks:\n      # Run the linter.\n      - id: ruff\n        args: [ --fix ]\n      # Run the formatter.\n      - id: ruff-format\n  # - repo: local\n  #   hooks:\n  #     - id: pytest\n  #       name: pytest\n  #       entry: pytest\n  #       language: system\n  #       types: [python]\n  #       pass_filenames: false\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM node:22.9.0-alpine3.20\n\nARG POCKETBASE_ADMIN_EMAIL\nARG POCKETBASE_ADMIN_PASSWORD\nARG POCKETBASE_URL\nARG TITLE\nARG API_KEY\nARG CAP1\nARG CAP2\nARG CAP3\n\n# Set environment variables to be overridden at runtime\nENV POCKETBASE_ADMIN_EMAIL=$POCKETBASE_ADMIN_EMAIL\nENV POCKETBASE_ADMIN_PASSWORD=$POCKETBASE_ADMIN_PASSWORD\nENV POCKETBASE_URL=$POCKETBASE_URL\nENV TITLE=$TITLE\nENV API_KEY=$API_KEY\nENV CAP1=$CAP1\nENV CAP2=$CAP2\nENV CAP3=$CAP3\n\n# Install required packages\nRUN apk update && apk add --no-cache \\\n    unzip \\\n    curl\n\n# download and unzip PocketBase\nADD https://github.com/pocketbase/pocketbase/releases/download/v0.22.21/pocketbase_0.22.21_linux_amd64.zip /tmp/pb.zip\nRUN unzip /tmp/pb.zip -d /pb/\n\n# create PocketBase data directory\nRUN mkdir -p /pb/pb_data\n# COPY start.sh /pb/start.sh\n\n# start PocketBase in a background process to set up the database\n# RUN chmod +x /pb/start.sh\n\n# uncomment to copy the local pb_migrations dir into the image\n# COPY ./pb_migrations /pb/pb_migrations\n\n# uncomment to copy the local pb_hooks dir into the image\n# COPY ./pb_hooks /pb/pb_hooks\n\nWORKDIR /app\nCOPY . .\nCOPY start_services.sh /app/start.sh\n\nRUN npm ci\n\n# RUN /pb/start.sh\n\nEXPOSE 3000 8080\n\n# start PocketBase\nCMD [\"/bin/sh\", \"/app/start.sh\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 Rishikanth Chandrasekaran\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": "## Introduction\nHi,\nI’m [Rishikanth](https://rishikanth.me), and I’m excited to introduce you to Markopolis! It’s a web app and API server I\nbuilt that lets you easily share your Markdown notes as websites while giving you full control to\ninteract with and manage your Markdown files via a powerful API. Just point Markopolis to a folder\nwith your Markdown files, and it’ll handle the rest. The idea is to help you create your own tools\nand features around your notes without being tied down by proprietary systems. It’s completely\nopen-source and free under the MIT License. Check out the [GitHub repo](https://github.com/rishikanthc/markopolis) and start exploring!\n\n**TLDR:** Self-hosted Obsidian publish with an API to extend functionality.\n\n## Features\n\n- **Easy setup** Extremely simple to deploy and use\n- **Easy publish** Publish notes online with a single command\n- **Markdown API interface** Interact with aspecs of markdown using REST APIs\n- **Extensible** Extendable using exposed APIs\n- **Develop your own frontend** You can use the api calls to get every section of markdown files to design your own frontend\n- **Instand rendering** Article is available online as soon as ypu publish\n- **Full text search** Fuzzy search across your entire notes vault\n- **Obsidian markdown flavor** Maintains compatibility with obsidian markdown syntax. Supports\n  callouts, equations, code highlighting etc.\n- **Dark & Light modes** Supports toggling between light and dark themes\n- **Easy maintenance** Requires very little to no maintenance\n- **Docker support** Available as docker images to self host\n\nand lots more to come. Checkout the [roadmap](https://markopolis.app/roadmap) page for planned features.\n\n## Demo\nThe documentation [website](https://markopolis.app) is hosted using Markopolis and is a live demo.\nThese notes are used to demonstrate the various aspects of Markopolis.\nCheckout the [Markdown Syntax](https://markopolis.app/Markdown%20Syntax.md) page for a full showcase of all supported markdown syntax.\n\nThank you for considering Markopolis for your Markdown note-sharing needs! If you like\nthe project considering starring the repository.\n\n## Versioning\n\nI try to follow semantic versioning as much as possible. However, I have still not\nstreamlined the process yet, so please bear with me if there are any mishaps. v2.0.0 achieves\ncode separation between backend and frontend because of which I had to fast forward the\ndocker versioning to match the python package. Going forward I'll try to avoid such mishaps\nand I'll be maintaining a detailed changelog at [changelog](https://markopolis.app/changelog).\n\nThis is my first open-source project and I'm excited to scale it well. I started building this\nmostly out of my personal need, but if there's public interest I'm more than happy to\naccept feature requests and contributions. Any and all feedback is welcome. This project\nwill always be open-source and maintained as I rely on it for my own notes system.\n\nIf you like the project please don't forget to star the [github repo](https://github.com/rishikanthc/markopolis.git).\n\n## Installation\nInstalling Markopolis involves two steps. First deploying the server. Second\ninstalling the CLI tool. The CLI tool provides a utility command to upload\nyour markdown files to the server. The articles are published as soon as\nthis command is run.\n\n## Step 1: Server installation\n\nWe will be using Docker for deploying Markopolis.\nCreate a docker-compose and configure environment variables.\nMake sure to generate and add a secure `API_KEY`.\nAllocate persistent storage for the Markdown files.\n\n\nNext create a `docker-compose.yaml` file with the following:\n\n```yaml\nversion: '3.8'\n\nservices:\n  markopolis:\n    image: ghcr.io/rishikanthc/markopolis:latest\n    ports:\n      - \"8080:8080\"\n      - \"3000:3000\"\n    environment:\n      - POCKETBASE_URL=http://127.0.0.1:8080\n      - API_KEY=test\n      - POCKETBASE_ADMIN_EMAIL=admin@admin.com\n      - POCKETBASE_ADMIN_PASSWORD=password\n      - TITLE=Markopolis\n      - CAP1=caption1\n      - CAP2=caption2\n      - CAP3=caption3\n    volumes:\n      - ./pb_data:/app/db\n```\n\nNow you can deploy Markopolis by running `docker-compse up -d`\n\nParameter | Description\n-- | --\nPOCKETBASE_URL | **DO NOT Change this**\nPOCKETBASE_ADMIN_EMAIL | The admin account email for the database\nPOCKETBASE_ADMIN_PASSWORD | The admin account password\nTITLE | SITE TITLE\nAPI_KEY | For security, most of the API endpoints are protected by an API key. Make sure to use a secure API key and don't share it publicly.\nCAP1 | Caption 1, text that appears below the site title\nCAP2 | Caption 2\nCAP3 | Caption 3\n\n\n\n## STEP 2: Local installation\n\nI highly recommend configuring a virtual environment for python to keep your environment clean and\nand prevent any dependency issues. Below I detail the steps to do this using Conda or pip. If you are familar\nwith this feel free to skip to the package installation section.\n\n> [!info]\n> You need to have python version >= 3.12\n\n### Setting up a virtual environment\n\nYou can use either `pip` or `conda` to do this. If you are using `pip` simply run\n```bash\npython3.12 -m venv <name>\n```\n\nReplace `<name>` with your desired virtual environment name. You can then activate the virtual environment\nusing:\n```bash\nsource <name>\n```\n\nFor conda, you can use\n```bash\nconda create -n <name> python==3.12\n```\n\nand activate it with\n```bash\nconda activate <name>\n```\n\n### Package installation\n\nSimply install the markopolis python package using your preferred package manager.\n\n**pip:**\n```bash\npip install markopolis\n```\n\n### Configuration\n\nSet the environment variables `MARKOPOLIS_DOMAIN` and `MARKOPOLIS_API`\n\n**bash or zsh (temporarily for current session)**\n```bash\nexport MARKOPOLIS_DOMAIN=https://markopolis.example.com\n```\n\n**bash or zsh (permanently for all sessions)**\n```bash\necho 'export MARKOPOLIS_DOMAIN=https://markopolis.example.com' >> ~/.zshrc\necho 'export MARKOPOLIS_DOMAIN=https://markopolis.example.com' >> ~/.bashrc\n\nsource ~/.zshrc\nsource ~/.bashrc\n```\n\n**fish (temporarily for current session)**\n```fish\nset -x MARKOPOLIS_DOMAIN https://markopolis.example.com\n```\n\n\n**fish (permanently for all sessions)**\n```fish\necho 'set -x MARKOPOLIS_DOMAIN \"https://markopolis.example.com\"' >> ~/.config/fish/config.fish\nsource ~/.config/fish/config.fish\n```\n\nFor more information on how to use Markopolis checkout the [Markopolis](https://markopolis.app) website.\nIf you like this project please considering starring it.\n"
  },
  {
    "path": "components.json",
    "content": "{\n\t\"$schema\": \"https://shadcn-svelte.com/schema.json\",\n\t\"style\": \"new-york\",\n\t\"tailwind\": {\n\t\t\"config\": \"tailwind.config.ts\",\n\t\t\"css\": \"src/app.css\",\n\t\t\"baseColor\": \"neutral\"\n\t},\n\t\"aliases\": {\n\t\t\"components\": \"$lib/components\",\n\t\t\"utils\": \"$lib/utils\"\n\t},\n\t\"typescript\": true\n}\n"
  },
  {
    "path": "docker-compose.yaml",
    "content": "services:\n  markopolis:\n    image: ghcr.io/rishikanthc/markopolis:3.0.0\n    ports:\n      - \"8080:8080\"\n      - \"3000:3000\"\n    environment:\n      - POCKETBASE_URL=http://127.0.0.1:8080\n      - API_KEY=test\n      - POCKETBASE_ADMIN_EMAIL=admin@gmail.com\n      - POCKETBASE_ADMIN_PASSWORD=password\n      - TITLE=Markopolis\n      - CAP1=Markdown\n      - CAP2=\"Self-hosting\"\n      - CAP3=\"Knowledge Garden\"\n    volumes:\n      - ./pb_data:/app/pb\n"
  },
  {
    "path": "docs/Changelog/3.0.0.md",
    "content": "---\ntitle: Version 3.0.0\ndate: 09-24-2024\ntags:\n  - 3.0.0\n\n---\n\n## Backend Rewrite\n* **Technology Shift:** The backend has been completely rewritten, moving from Python to SwellKit.\n* **Database:** Transitioned from file-based management to using PocketBase for the backend database.\n\nThis version is a full rewrite of the backend in sveltekit. The choice was made as managing both\nthe frontend and backend using the same language and framework simplifies the architecture and\nmanagement a lot. Additionally pocketbase provides a nice `js` interface which is also nice.\n\n## UI Enhancements\n- The UI has been refined using ShadeCN, and Tailwind CSS.\n\n## New Features\n* The new version introduces support for using relative paths in wiki links and images.\n* Tag Management\n  * Tag Pages: Tags now get their own dedicated pages.\n  * Menu Bar Icon: A new menu bar icon has been added to view the list of all tags.\n* Simplified Python CLI Tool\n  * The Python CLI tool has been simplified to remove most dependencies.\n"
  },
  {
    "path": "docs/Development/APIs.md",
    "content": "---\ntitle: API overview\ndate: 22-09-2024\ntags:\n  - api\n  - backend\npublish: true\n---\n\nThis section details the core operations of the app, specifically the backend.\nThe backend exposes various REST API endpoints which can serve different types of requests related\nto markdown files. Below we detail each of them.\n\n## Overview\nThis document provides an overview of the API endpoints for the Marco Polo's application. The application is built using SvelteKit for the backend API and PocketBase for the database. A Python package is also used to expose a CLI for uploading files to the server.\n## Endpoints\n### Upload API\n\n> [!important]\n> **Endpoint:** ==/api/upload==\n> **Method:** *POST*\n\n- **Description** Uploads markdown files to the server, sets up the database, parses HTML, and stores the compiled results and original files in the database.\n- **Parameters**\n  - `file`: The markdown file to be uploaded.\n  - `url`: Absolute path of file from root of vault.\n\n#### Details\n\nRefer [[Markdown Rendering]]\n\n---\n### LS API\n- **Endpoint** `/api/ls`\n- **Method** GET\n- **Description** Builds a file tree from the URL field in each record of the MDBase collection.\n- **Responses**:\n  - `200 OK`: Returns a JSON response with the file tree.\n  - `500 Internal Server Error`: Error in processing the request.\n---\n### Search API\n- **Endpoint** `/api/search`\n- **Method** GET\n- **Description** Performs a fuzzy search through the content stored in the database.\n- **Parameters**:\n  - `query`: The search query string.\n- **Responses**:\n  - `200 OK`: Returns search results with snippets containing matches.\n  - `404 Not Found`: No matches found.\n---\n### Links API\n- **Endpoint** `/api/links`\n- **Method** GET\n- **Description** Retrieves all links and backlinks for a given markdown file.\n- **Parameters**\n  - `url`: The URL of the markdown file.\n- **Responses**\n  - `200 OK`: Returns forward and backward links.\n  - `404 Not Found`: File not found.\n---\n### Backlinks API\n- **Endpoint** `/api/backlinks`\n- **Method** GET\n- **Description** Retrieves only the backlinks for a given markdown file.\n- **Parameters**\n  - `url`: The URL of the markdown file.\n- **Responses**:\n  - `200 OK`: Returns backlinks.\n  - `404 Not Found`: File not found.\n---\n### Image API\n- **Endpoint** `/api/image`\n- **Method** GET\n- **Description**: Fetches images from the database.\n- **Parameters**\n  - `url`: The URL of the image file.\n- **Responses**:\n  - `200 OK`: Returns the image file.\n  - `404 Not Found`: Image not found.\n"
  },
  {
    "path": "docs/Development/Markdown Rendering.md",
    "content": "---\ntitle: Rendering Markdown as HTML\ndate: 09-22-2024\ntags:\n  - markdown\n  - mdsvex\npublish: true\n---\n\nThis document provides an in-depth explanation of the markdown parsing process used in the Marco Polo's application. The parsing is implemented using the `md-swex` library, which allows for the integration of Svelte components within markdown files.\n## Parsing Process\n1. **Markdown to HTML Conversion**:\n   - The `md-swex` library is used to parse markdown files and convert them into HTML blocks.\n   - The `compile` function of `md-swex` is utilized to directly compile markdown content.\n2. **Database Storage**:\n   - Parsed content is stored in the PocketBase database.\n   - Fields include ID, title, parsed HTML content, URL, markdown file, front matter (as JSON), and relational fields for backlinks and forward links.\n3. **Plugins and Extensions**:\n   - **Remark Plugins**: Used for processing markdown abstract syntax trees.\n     - `Remark Math`: Renders LaTeX equations.\n     - `Remark Footnotes`: Processes footnotes.\n     - `Custom Wikilink Plugin`: Resolves relative links.\n     - `Custom Obsidian Image Plugin`: Handles inline images.\n     - `Custom Remark Mermaid Plugin`: Processes Mermaid diagrams.\n4. **Custom Wikilink Plugin**:\n   - Recognizes Wikilink syntax and evaluates relative paths.\n   - Uses front matter to access the file path and resolve links.\n5. **Handling Code Blocks**:\n   - Addresses issues with `md-swex` parsing code blocks as inline HTML.\n   - Cleanup functions remove unwanted syntax to ensure proper rendering.\n6. **Operational Checks**:\n   - Ensures the existence of necessary database collections (e.g., `mdbase`, `attachments`).\n   - Handles file uploads and updates, storing markdown and image files appropriately.\n## Conclusion\nThe markdown parsing component is integral to the Marco Polo's application, enabling efficient storage and retrieval of markdown content. The use of `md-swex` and various plugins ensures robust parsing and rendering capabilities.\n"
  },
  {
    "path": "docs/Development/components.md",
    "content": "---\ntitle: Components behind Markopolis\ntags:\n  - development\n  - working\ndate: 09/20/2024\npublish: true\n---\n\nMarkopolis is built using 2 frameworks: [sveltekit](https://svelte.dev) and [pocketbase](https://pocketbase.docs).\nPocketbase is used for database and the backend API is implemented using sveltekit. It additionally has a\nclient side convenience python CLI interface.\nThe sveltekit webapp exposes APIs for interaction, pre-renders and routes the websites. The functionality of the\napp begins with the python CLI interface. Running the sync command uploads files using an API. The API on\nrecieving the file, converts it to html and stores it in the database along with the original file.\nThe sveltekit app then uses dynamic routing to search and fetch the file from the database and renders it as a\nwebpage.\n\nOn the high-level Markopolis consists of a backend and a database. The front-end interacts with the backend\nand requests things needed to render pages. The backend, according to the requests, pulls data from the database\nand returns them.\nThe front-end interacts with the backend via REST APIs exposed by the backend and for webpages also uses\nServer Side Rendering(SSR). Below is a diagram illustrating the data and control flow:\n\n```mermaid\nsequenceDiagram\n  Frontend->>Backend: API calls\n  Backend-->>Database: fetch data using PocketBase API\n  Database-->>Backend: Requested data\n  Backend-)Frontend: Requested data\n```\n\n\n## Backend\n\nThe backends primary function is\n"
  },
  {
    "path": "docs/Markdown Syntax.md",
    "content": "---\npublish: true\ntags:\n- syntax\n- markdown\ntitle: Markdown Syntax\n---\n# Headings\n\n```markdown\n# Heading 1\n## Heading 2\n### Heading 3\n#### Heading 4\n##### Heading 5\n```\n\n\n# Heading 1\n## Heading 2\n### Heading 3\n#### Heading 4\n##### Heading 5\n\n## Horizontal line\n\n---\n\n## Tags\n\n```markdown\n#tag1 #tag2 #tag3\n```\n\n#tag1 #tag2 #tag3\n\n## Images\n\n### embed images\nImage names should be unique. Duplicate images will be overwritten.\n\n```markdown\n![[image.png]]\n\n![](image.png)\n```\n\n![[image.png]]\n\n![](image.png)\n\n### external images\n\n```markdown\n![Engelbart](https://history-computer.com/ModernComputer/Basis/images/Engelbart.jpg)\n```\n\n![Engelbart](https://history-computer.com/ModernComputer/Basis/images/Engelbart.jpg)\n\n## Wikilinks\n\n```markdown\n[[Installation]]\n[[f1/test]]\n[[f2/test]]\n```\n\n[[installation]]\n[[f1/test]]\n[[f2/test]]\n\n## Text formatting\n```markdown\n**Bold text**\n*Italic text*\n~~this puts a strikethrough~~\n==this highlights text==\n**Bold text and _nested italic_ text**\n***Bold and italic text***\n```\n\n**Bold text**\n*Italic text*\n~~this puts a strikethrough~~\n==this highlights text==\n**Bold text and _nested italic_ text**\n***Bold and italic text***\n\n## Equations\n\n```markdown\n$$\n\\sum_i = x\n$$\n```\n\n$$\n\\sum_i = x\n$$\n\n## Footnotes\n\n```markdown\nThis is a simple footnote[^1].\n\n\n[^1]: This is the referenced text.\n[^2]: Add 2 spaces at the start of each new line.\n  This lets you write footnotes that span multiple lines.\n[^note]: Named footnotes still appear as numbers, but can make it easier to identify and link references.\n```\n\nThis is a simple footnote[^1].\n\n\n## Quotes\n\n```markdown\n> Human beings face ever more complex and urgent problems, and their effectiveness in dealing with these problems is a matter that is critical to the stability and continued progress of society.\n\n\\- Doug Engelbart, 1961\n```\n\n> Human beings face ever more complex and urgent problems, and their effectiveness in dealing with these problems is a matter that is critical to the stability and continued progress of society.\n\n\\- Doug Engelbart, 1961\n\n## Tables\n\n```markdown\n| First name | Last name |\n| ---------- | --------- |\n| Max        | Planck    |\n| Marie      | Curie     |\n```\n\n| First name | Last name |\n| ---------- | --------- |\n| Max        | Planck    |\n| Marie      | Curie     |\n\nThe vertical bars on either side of the table are optional.\n\nCells don't need to be perfectly aligned with the columns. Each header row must have at least two hyphens.\n\n```markdown\nFirst name | Last name\n-- | --\nMax | Planck\nMarie | Curie\n```\n\nFirst name | Last name\n-- | --\nMax | Planck\nMarie | Curie\n\n## Mermaid diagrams\n\nSome text.\n\n```mermaid\ngraph TB\nA --> B\nB --> C\n```\n\n```mermaid\nflowchart LR\n\n\n\nA[Osaka 7-8] --> B[Tokyo 9-11]\nB -.Nagano .-> C[Matsumoto]\nC -.Nagano & Toyama.-> D[Takayama 12] <--> D1(Hida no Sato village)\nB -.Nagano & Toyama.-> D\nC <-.bus.-> D\nD --Toyama---> E <--> D2([Onsen 14]) --> F\nE[Kanazawa 13] ---> F[Kyoto 15-18] <--> F2(Uji) <--> F1(Nara)\nF <-.-> F4(Himeji)\n```\n\n### Large chart\n\n```mermaid\ntimeline\n    section .NET Framework\n        2000 - 2005\n             : .NET Framework 1.0\n             : .NET Framework 1.0 SP1\n             : .NET Framework 1.0 SP2\n             : .NET Framework 1.1\n             : .NET Framework 1.0 SP3\n             : .NET Framework 2.0\n        2006 - 2009\n             : .NET Framework 3.0\n             : .NET Framework 3.5\n             : .NET Framework 2.0 SP 1\n             : .NET Framework 3.0 SP 1\n             : .NET Framework 2.0 SP 2\n             : .NET Framework 3.0 SP 2\n             : .NET Framework 3.5 SP 1\n        2010 - 2015\n             : .NET Framework 4.0\n             : .NET Framework 4.5\n             : .NET Framework 4.5.1\n             : .NET Framework 4.5.2\n             : .NET Framework 4.6\n             : .NET Framework 4.6.1\n    section .NET Core\n        2016 - 2017\n             : .NET Core 1.0\n             : .NET Core 1.1\n             : .NET Framework 4.6.2\n             : .NET Core 2.0\n             : .NET Framework 4.7\n             : .NET Framework 4.7.1\n        2018 - 2019\n             : .NET Core 2.1\n             : .NET Core 2.2\n             : .NET Framework 4.7.2\n             : .NET Core 3.0\n             : .NET Core 3.1\n             : .NET Framework 4.8\n    section Modern .NET\n        2020 : .NET 5\n        2021 : .NET 6\n        2022 : .NET 7\n             : .NET Framework 4.8.1\n\n```\n\n## Callouts\n\n> [!abstract]\n> Lorem ipsum dolor sit amet\n\n> [!info]\n> Lorem ipsum dolor sit amet\n\n> [!todo]\n> Lorem ipsum dolor sit amet\n\n> [!tip]\n> Lorem ipsum dolor sit amet\n\n> [!success]\n> Lorem ipsum dolor sit amet\n\n> [!question]\n> Lorem ipsum dolor sit amet\n\n> [!warning]\n> Lorem ipsum dolor sit amet\n\n> [!failure]\n> Lorem ipsum dolor sit amet\n\n> [!danger]\n> Lorem ipsum dolor sit amet\n\n> [!bug]\n> Lorem ipsum dolor sit amet\n\n> [!example]\n> Lorem ipsum dolor sit amet\n\n> [!quote]\n> Lorem ipsum dolor sit amet\n\n> [!tip] Title-only callout\n\n[^1]: This is the referenced text.\n[^2]: Add 2 spaces at the start of each new line.\n  This lets you write footnotes that span multiple lines.\n[^note]: Named footnotes still appear as numbers, but can make it easier to identify and link references.\n"
  },
  {
    "path": "docs/installation.md",
    "content": "---\ntitle: Installation\ndate: 09-24-2024\ntags:\n  - install\n  - docker\n---\n\nInstalling Markopolis involves two steps. First deploying the server. Second\ninstalling the CLI tool. The CLI tool provides a utility command to upload\nyour markdown files to the server. The articles are published as soon as\nthis command is run.\n\n## Server installation\n\nWe will be using Docker for deploying Markopolis.\nCreate a docker-compose and configuring environment variables.\nMake sure to generate and add a secure `API_KEY`.\nAllocate persistent storage for the Markdown files.\n\nFirst create a `.env` file to configure the following environment variables.\n\n```bash\nPOCKETBASE_URL=http://127.0.0.1:8090\nAPI_KEY=<long alpha-numeric string\"\nPOCKETBASE_ADMIN_EMAIL=test@example.com\nPOCKETBASE_ADMIN_PASSWORD=1234567890\nTITLE=Markopolis\n```\n\nNext create a `docker-compose.yaml` file with the following:\n\n```yaml\nversion: '3.8'\n\nservices:\n  sveltekit-pocketbase:\n    image: ghcr.io/rishikanthc/markopolis:latest\n    ports:\n      - \"8080:8080\"\n      - \"3000:3000\"\n    environment:\n      - POCKETBASE_URL=http://127.0.0.1:8080\n      - API_KEY=test\n      - POCKETBASE_ADMIN_EMAIL=admin@admin.com\n      - POCKETBASE_ADMIN_PASSWORD=password\n      - TITLE=Markopolis\n    volumes:\n      - ./pb_data:/app/pb\n\n```\n\nNow you can deploy Markopolis by running `docker-compse up -d`\n\n\n## Local Installation\nRequirements: Python 3.12\n\nInstall:\n```sh\npip install markopolis\n```\n\n### Configuration:\nCreate a config file as a YAML file in any location.\nSet the `MARKOPOLIS_CONFIG_PATH` environment variable to point to the location of the config file.\nThe config file should specify the domain of the deployment including the protocol and\nthe api key. The api key should be the same as what you used for the deployment:\n\n```yaml\ndomain: \"https://your-domain.com\"\n```\n\nI recommend using a python virtual environment for the local installation.\n\n## Post-install\nInitially since the server hasn't seen any markdown files the app will throw a 500 Error.\nCheck [[usage]] for details on how to setup Markopolis\n"
  },
  {
    "path": "docs/introduction.md",
    "content": "---\ntitle: Hi,\ndate: 09-23-2024\n---\n\nWelcome to Markopolis, a self-hostable web app and API for managing Markdown knowledge gardens.\nMarkopolis renders markdown notes as `html` and exposes APIs for interacting with markdown files\nto implement a custom eco system around your notes.\nTLDR: Self-hosted version of Obsidian publish with future promises\n\n> Self-hostable Obsidian Publish\n\n## Why\nMarkdown files are my preferred choice for storing information. It's simple and is future proof.\nHaving used Obsidian and liking it a lot, I moved back to using my text editor as Obsidian was\ntoo distracting. The customizability is endless and I found myself frequently caught down rabbit\nholes, trying to optimize for the perfect setup. I had to end the insanity.\n\nI have been using Markdown-Oxide along with my editor and that keeps it super simple. However, I\nmiss some of the features offered by Obsidian via plugins like 1-click Publish, auto-tagging,\nnotes discovery, etc. I decided to build something that would help me to easily publish my notes\nonline, and can be self-hosted on my own hardware. This got me thinking about using REST APIs as\nan interface to work with markdown files. That way, I can implement my own features around my notes.\nHence, Markopolis.\n\n## Features\n\n- **Easy setup** Extremely simple to deploy and use\n- **Easy publish** Publish notes online with a single command\n- **Markdown API interface** Interact with aspecs of markdown using REST APIs\n- **Extensible** Extendable using exposed APIs\n- **Instand rendering** Article is available online as soon as ypu publish\n- **Full text search** Fuzzy search across your entire notes vault\n- **Obsidian markdown flavor** Maintains compatibility with obsidian markdown syntax. Supports\n  callouts, equations, code highlighting etc.\n- **Dark & Light modes** Supports toggling between light and dark themes\n- **Easy maintenance** Requires very little to no maintenance\n- **Docker support** Available as docker images to self host\n\n## Demo\n\nThis website is hosted using Markopolis and is a live demo. These notes are used to demonstrate\nthe various aspects of Markopolis. Checkout the [[Markdown Syntax]] page for a full showcase\nof all supported markdown syntax.\n\n## About me\nHi,\nI'm [Rishi](https://rishikanth.me), a recent PhD graduate and soon to start as an Applied Researcher.\nI'm an avid self-hoster and a strong proponent of open-source software. I'm based\nout of Washington and enjoy solving practical problems with code.\n"
  },
  {
    "path": "docs/roadmap.md",
    "content": "---\ntitle: Development Roadmap\ndate: 09-23-2024\npublish: true\n---\n\nThis page lists a bunch of features and improvements that I plan to\ntake on. These are based on my own needs, but I'm more than happy to\ntake feature requests. If you face an issue or want a particular feature\nfeel free to open a Github issue and I'll address it.\n\n## UX & UI improvements\n\n- **Better blockquote styling** Fix the odd alignment of quotes and text\n- **Nested checkbox lists** Handle formatting of nested to-do / checkbox lists\n- **Upload only changed files** Currently overwriting all files irrespective\n\n## Features\n- **Password protection** Hide specific pages behind a password\n- **Selective AI chat** Chat with selected notes using OpenAI / Claude\n- **Graph view** Visualize the backlinks network as a 3D graph\n- **Graph Navigation** navigate notes via graphs\n- **Graph interactions** Interact with your notes using 3D graphs\n  - advanced filtering\n  - AI chat with sub-graph as context\n- **Estimated reading time**\n- **ToDo management** Show, manage and edit ToDos\n- **Daily notes** Render daily notes under a personal login\n- **Auto tagging** Auto tagging using graph community detection\n\n## Bug fixes\n- **Cleanup dangling tags** Delete unused tags left behind by file deletion\n- **Highlight.js** Something weird is going and syntax highlighting doesn't work as intended\n"
  },
  {
    "path": "docs/usage.md",
    "content": "---\ntitle: Usage\ndate: 09-24-2024\n---\n\nInitially the app starts with an empty database as there are no notes. So we\nwill begin by uploading some notes.\n\n### Uploading files to server\n\nOpen a terminal and `cd` to the root directory of your notes vault. This is the directory\nof your notes. Then run `mdsync` in the vault directory. The command will scan for\nall markdown and image files.\n\n> [!note]\n> You can delete files on the server by simply deleting the file locally in your\n> vault and then running `mdsync` command.\n\n\n### WikiLinks and Images\n\nMarkopolis supports Obsidian style WikiLinks and Images. Previously\nMarkopolis only supported absolute path from note vault. However, from 3.0.0\nMarkopolis now supports relative path as well.\n\n\n> [!Tip]\n> Checkout [[Markdown Syntax]] to see how different markdown syntax renders.\n"
  },
  {
    "path": "eslint.config.js",
    "content": "import js from '@eslint/js';\nimport ts from 'typescript-eslint';\nimport svelte from 'eslint-plugin-svelte';\nimport prettier from 'eslint-config-prettier';\nimport globals from 'globals';\n\n/** @type {import('eslint').Linter.Config[]} */\nexport default [\n\tjs.configs.recommended,\n\t...ts.configs.recommended,\n\t...svelte.configs['flat/recommended'],\n\tprettier,\n\t...svelte.configs['flat/prettier'],\n\t{\n\t\tlanguageOptions: {\n\t\t\tglobals: {\n\t\t\t\t...globals.browser,\n\t\t\t\t...globals.node\n\t\t\t}\n\t\t}\n\t},\n\t{\n\t\tfiles: ['**/*.svelte'],\n\t\tlanguageOptions: {\n\t\t\tparserOptions: {\n\t\t\t\tparser: ts.parser\n\t\t\t}\n\t\t}\n\t},\n\t{\n\t\tignores: ['build/', '.svelte-kit/', 'dist/']\n\t}\n];\n"
  },
  {
    "path": "package.json",
    "content": "{\n\t\"name\": \"godamn\",\n\t\"version\": \"0.0.1\",\n\t\"private\": true,\n\t\"scripts\": {\n\t\t\"dev\": \"vite dev\",\n\t\t\"build\": \"vite build\",\n\t\t\"preview\": \"vite preview\",\n\t\t\"check\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json\",\n\t\t\"check:watch\": \"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch\",\n\t\t\"lint\": \"prettier --check . && eslint .\",\n\t\t\"format\": \"prettier --write .\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@sveltejs/adapter-auto\": \"^3.0.0\",\n\t\t\"@sveltejs/adapter-node\": \"^5.2.4\",\n\t\t\"@sveltejs/kit\": \"^2.0.0\",\n\t\t\"@sveltejs/vite-plugin-svelte\": \"^3.0.0\",\n\t\t\"@tailwindcss/typography\": \"^0.5.14\",\n\t\t\"@types/eslint\": \"^9.6.0\",\n\t\t\"autoprefixer\": \"^10.4.20\",\n\t\t\"eslint\": \"^9.0.0\",\n\t\t\"eslint-config-prettier\": \"^9.1.0\",\n\t\t\"eslint-plugin-svelte\": \"^2.36.0\",\n\t\t\"globals\": \"^15.0.0\",\n\t\t\"prettier\": \"^3.1.1\",\n\t\t\"prettier-plugin-svelte\": \"^3.1.2\",\n\t\t\"prettier-plugin-tailwindcss\": \"^0.6.5\",\n\t\t\"remark-footnotes\": \"2.0\",\n\t\t\"remark-math\": \"^3.0.0\",\n\t\t\"svelte\": \"^4.2.7\",\n\t\t\"svelte-check\": \"^4.0.0\",\n\t\t\"tailwindcss\": \"^3.4.9\",\n\t\t\"typescript\": \"^5.0.0\",\n\t\t\"typescript-eslint\": \"^8.0.0\",\n\t\t\"vite\": \"^5.0.3\"\n\t},\n\t\"type\": \"module\",\n\t\"dependencies\": {\n\t\t\"@leeoniya/ufuzzy\": \"^1.0.14\",\n\t\t\"@pondorasti/remark-img-links\": \"^1.0.8\",\n\t\t\"@threlte/core\": \"^7.3.1\",\n\t\t\"@threlte/extras\": \"^8.11.5\",\n\t\t\"3d-force-graph\": \"^1.73.3\",\n\t\t\"bits-ui\": \"^0.21.15\",\n\t\t\"clsx\": \"^2.1.1\",\n\t\t\"d3-force\": \"^3.0.0\",\n\t\t\"d3-force-3d\": \"^3.0.5\",\n\t\t\"formsnap\": \"^1.0.1\",\n\t\t\"highlight.js\": \"^11.10.0\",\n\t\t\"js-yaml\": \"^4.1.0\",\n\t\t\"katex\": \"^0.16.11\",\n\t\t\"lodash-es\": \"^4.17.21\",\n\t\t\"lucide-svelte\": \"^0.441.0\",\n\t\t\"marked\": \"^14.1.3\",\n\t\t\"marked-admonition-extension\": \"^0.0.4\",\n\t\t\"marked-alert\": \"^2.1.0\",\n\t\t\"mdsvex\": \"^0.12.3\",\n\t\t\"mdsvex-relative-images\": \"^1.0.3\",\n\t\t\"mermaid\": \"^11.2.1\",\n\t\t\"mode-watcher\": \"^0.4.1\",\n\t\t\"pocketbase\": \"^0.21.5\",\n\t\t\"rehype-autolink-headings\": \"^7.1.0\",\n\t\t\"rehype-callouts\": \"^1.0.3\",\n\t\t\"rehype-highlight\": \"^7.0.0\",\n\t\t\"rehype-katex\": \"^7.0.1\",\n\t\t\"rehype-katex-svelte\": \"^1.2.0\",\n\t\t\"rehype-mermaid\": \"^2.1.0\",\n\t\t\"remark-gfm\": \"^4.0.0\",\n\t\t\"remark-wiki-link\": \"^0.0.4\",\n\t\t\"svelte-markdown\": \"^0.4.1\",\n\t\t\"svelte-radix\": \"^1.1.1\",\n\t\t\"sveltekit-superforms\": \"^2.19.0\",\n\t\t\"tailwind-merge\": \"^2.5.2\",\n\t\t\"tailwind-variants\": \"^0.2.1\",\n\t\t\"threlte\": \"^3.13.1\",\n\t\t\"unist-util-visit\": \"^5.0.0\",\n\t\t\"zod\": \"^3.23.8\"\n\t}\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "export default {\n\tplugins: {\n\t\ttailwindcss: {},\n\t\tautoprefixer: {}\n\t}\n};\n"
  },
  {
    "path": "src/app.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n\t:root {\n\t\t--background: 0 0% 100%;\n\t\t--foreground: 0 0% 3.9%;\n\n\t\t--muted: 0 0% 96.1%;\n\t\t--muted-foreground: 0 0% 45.1%;\n\n\t\t--popover: 0 0% 100%;\n\t\t--popover-foreground: 0 0% 3.9%;\n\n\t\t--card: 0 0% 100%;\n\t\t--card-foreground: 0 0% 3.9%;\n\n\t\t--border: 0 0% 89.8%;\n\t\t--input: 0 0% 89.8%;\n\n\t\t--primary: 0 0% 9%;\n\t\t--primary-foreground: 0 0% 98%;\n\n\t\t--secondary: 0 0% 96.1%;\n\t\t--secondary-foreground: 0 0% 9%;\n\n\t\t--accent: 0 0% 96.1%;\n\t\t--accent-foreground: 0 0% 9%;\n\n\t\t--destructive: 0 72.2% 50.6%;\n\t\t--destructive-foreground: 0 0% 98%;\n\n\t\t--ring: 0 0% 3.9%;\n\n\t\t--radius: 0.5rem;\n\t}\n\n\t.dark {\n\t\t/* --background: 0 0% 3.9%; */\n\t\t/* --foreground: 0 0% 98%; */\n\n\t\t--background: 0 0% 9%;\n\t\t--foreground: 30 0% 96%;\n\t\t/* --foreground: 30 0% 78%; */\n\n\t\t/* --muted: 0 0% 14.9%;\n\t\t--muted-foreground: 0 0% 63.9%; */\n\t\t--muted: 0 0% 15%;\n\t\t--muted-foreground: 30 0% 78%;\n\n\t\t--popover: 0 0% 3.9%;\n\t\t--popover-foreground: 0 0% 98%;\n\n\t\t--card: 0 0% 3.9%;\n\t\t--card-foreground: 0 0% 98%;\n\n\t\t--border: 0 0% 14.9%;\n\t\t--input: 0 0% 14.9%;\n\n\t\t--primary: 0 0% 98%;\n\t\t--primary-foreground: 0 0% 9%;\n\n\t\t--secondary: 0 0% 14.9%;\n\t\t--secondary-foreground: 0 0% 98%;\n\n\t\t--accent: 0 0% 14.9%;\n\t\t--accent-foreground: 0 0% 98%;\n\n\t\t--destructive: 0 62.8% 30.6%;\n\t\t--destructive-foreground: 0 0% 98%;\n\n\t\t--ring: 0 0% 83.1%;\n\t}\n\n\th1 {\n\t\t@apply text-6xl;\n\t\t@apply mb-4 mt-10;\n\t}\n\th2 {\n\t\t@apply text-4xl;\n\t\t@apply mb-3 mt-8;\n\t}\n\th3 {\n\t\t@apply text-2xl;\n\t\t@apply mb-2 mt-6;\n\t}\n\th4 {\n\t\t@apply text-xl;\n\t\t@apply mb-1 mt-4;\n\t}\n\tp {\n\t\t@apply my-2;\n\t}\n\ta {\n\t\t@apply text-[#0f62fe] dark:text-[#78a9ff];\n\t}\n\ta:hover {\n\t\t@apply text-[#0043ce] dark:text-[#a6c8ff];\n\t}\n\n\t* {\n\t\t@apply border-border;\n\t}\n\tbody {\n\t\t@apply bg-background text-foreground;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Bold.ttf') format('truetype');\n\t\tfont-weight: bold;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-BoldItalic.ttf') format('truetype');\n\t\tfont-weight: bold;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-ExtraLight.ttf') format('truetype');\n\t\tfont-weight: 200;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-ExtraLightItalic.ttf') format('truetype');\n\t\tfont-weight: 200;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Italic.ttf') format('truetype');\n\t\tfont-weight: normal;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Light.ttf') format('truetype');\n\t\tfont-weight: 300;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-LightItalic.ttf') format('truetype');\n\t\tfont-weight: 300;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Medium.ttf') format('truetype');\n\t\tfont-weight: 500;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-MediumItalic.ttf') format('truetype');\n\t\tfont-weight: 500;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Regular.ttf') format('truetype');\n\t\tfont-weight: normal;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-SemiBold.ttf') format('truetype');\n\t\tfont-weight: 600;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-SemiBoldItalic.ttf') format('truetype');\n\t\tfont-weight: 600;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Text.ttf') format('truetype');\n\t\tfont-weight: 400;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-TextItalic.ttf') format('truetype');\n\t\tfont-weight: 400;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-Thin.ttf') format('truetype');\n\t\tfont-weight: 100;\n\t\tfont-style: normal;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Sans';\n\t\tsrc: url('/fonts/IBMPlexSans-ThinItalic.ttf') format('truetype');\n\t\tfont-weight: 100;\n\t\tfont-style: italic;\n\t}\n\n\t@font-face {\n\t\tfont-family: 'Megrim';\n\t\tsrc: url('/fonts/Megrim-Regular.ttf') format('truetype');\n\t\tfont-weight: normal;\n\t\tfont-style: normal;\n\t}\n\n\t/* ---- IBM Plex Mono ---- */\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 700;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-Bold.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 700;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-BoldItalic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 200;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-ExtraLight.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 200;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-ExtraLightItalic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 400;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-Italic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 300;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-Light.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 300;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-LightItalic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 500;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-Medium.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 500;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-MediumItalic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 400;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-Regular.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 600;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-SemiBold.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 600;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-SemiBoldItalic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 400;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-Text.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 400;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-TextItalic.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 100;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/IBMPlexMono-Thin.ttf') format('truetype');\n\t}\n\n\t@font-face {\n\t\tfont-family: 'IBM Plex Mono';\n\t\tfont-weight: 100;\n\t\tfont-style: italic;\n\t\tsrc: url('/fonts/IBMPlexMono-ThinItalic.ttf') format('truetype');\n\t}\n\n\t/* Lombok */\n\t@font-face {\n\t\tfont-family: 'Lombok';\n\t\tfont-weight: 400;\n\t\tfont-style: normal;\n\t\tsrc: url('/fonts/Lombok.otf') format('opentype');\n\t}\n}\n"
  },
  {
    "path": "src/app.d.ts",
    "content": "// See https://kit.svelte.dev/docs/types#app\n// for information about these interfaces\ndeclare global {\n\tnamespace App {\n\t\tinterface Locals {\n\t\t\tpb: import('pocketbase').default;\n\t\t}\n\t\t// interface Error {}\n\t\t// interface Locals {}\n\t\t// interface PageData {}\n\t\t// interface PageState {}\n\t\t// interface Platform {}\n\t}\n}\n\nexport {};\n"
  },
  {
    "path": "src/app.html",
    "content": "<!doctype html>\n<html lang=\"en\" class=\"dark\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"%sveltekit.assets%/favicon.png\" />\n\t\t<link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css\" integrity=\"sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X\" crossorigin=\"anonymous\">\n\n\t\t<link\n  rel=\"stylesheet\"\n  href=\"https://cdn.jsdelivr.net/npm/rehype-callouts/dist/themes/github/index.css\"\n/>\n\n\t\t<link\n  rel=\"stylesheet\"\n  href=\"%sveltekit.assets%/fonts.css\"\n/>\n\t\t<script src=\"https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js\"></script>\n\t\t<script src=\"//unpkg.com/3d-force-graph\"></script>\n\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t%sveltekit.head%\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">%sveltekit.body%</div>\n\t</body>\n</html>\n"
  },
  {
    "path": "src/lib/components/FileTree.svelte",
    "content": "<script lang=\"ts\">\n\timport { onMount } from \"svelte\";\n\timport { ChevronRight, ChevronDown, File } from \"lucide-svelte\";\n\n\tinterface FileNode {\n\t\tid: string;\n\t\ttitle: string;\n\t\tname: string;\n\t\turl: string;\n\t\tchildren: FileNode[];\n\t}\n\n\texport let fileTree: FileNode[] = [];\n\texport let node: FileNode | undefined = undefined;\n\texport let isExpanded = false;\n\n\tfunction toggleExpand() {\n\t\tisExpanded = !isExpanded;\n\t}\n\n\t$: isFolder = node && node.children.length > 0;\n</script>\n\n{#if node}\n\t<div class=\"file-node\">\n\t\t<div\n\t\t\tclass=\"flex cursor-pointer items-center rounded px-2 py-1 hover:bg-carbongray-100 dark:hover:bg-carbongray-700\"\n\t\t\ton:click={toggleExpand}\n\t\t\ton:keydown={(e) => e.key === \"Enter\" && toggleExpand()}\n\t\t\trole=\"button\"\n\t\t\ttabindex=\"0\"\n\t\t>\n\t\t\t{#if isFolder}\n\t\t\t\t{#if isExpanded}\n\t\t\t\t\t<ChevronDown class=\"mr-1 h-4 w-4 text-gray-500\" />\n\t\t\t\t{:else}\n\t\t\t\t\t<ChevronRight class=\"mr-1 h-4 w-4 text-gray-500\" />\n\t\t\t\t{/if}\n\t\t\t{:else}\n\t\t\t\t<File class=\"mr-1 h-4 w-4 text-gray-500 flex-shrink-0\" />\n\t\t\t{/if}\n\n\t\t\t{#if isFolder}\n\t\t\t\t<span class=\"font-semibold\">{node.name}</span> <!-- Folder name -->\n\t\t\t{:else if node.url}\n\t\t\t\t<a href={`/${node.url}`} class=\"text-carbongray-800 hover:underline\"\n\t\t\t\t\t>{node.title}</a\n\t\t\t\t>\n\t\t\t{:else}\n\t\t\t\t<span class=\"font-semibold\">{node.title}</span>\n\t\t\t\t<!-- Fallback for files without URLs -->\n\t\t\t{/if}\n\t\t</div>\n\n\t\t{#if isFolder && isExpanded}\n\t\t\t<div class=\"children ml-4 mt-1\">\n\t\t\t\t{#each node.children as childNode}\n\t\t\t\t\t<svelte:self node={childNode} />\n\t\t\t\t{/each}\n\t\t\t</div>\n\t\t{/if}\n\t</div>\n{:else}\n\t<div class=\"file-tree\">\n\t\t{#each fileTree as rootNode}\n\t\t\t<svelte:self node={rootNode} />\n\t\t{/each}\n\t</div>\n{/if}\n"
  },
  {
    "path": "src/lib/components/GraphScene.svelte",
    "content": "<script lang=\"ts\">\n\timport { T } from '@threlte/core';\n\timport { Grid, OrbitControls, interactivity } from '@threlte/extras';\n\timport { spring } from 'svelte/motion';\n\timport { Vector3 } from 'three';\n\tinteractivity();\n\n\tconst scale = spring(1);\n\tlet fromPosition = new Vector3(1, 1, 0);\n\tlet toPosition = new Vector3(2, 2, 0);\n\n\tlet edgeColor = 'red'; // Replace with your color logic\n\n\t// Define some sample graph data\n\tconst nodes = [\n\t\t{ id: 1, position: [0, 0, 0] },\n\t\t{ id: 2, position: [2, 1, 1] },\n\t\t{ id: 3, position: [-1, 2, -1] },\n\t\t{ id: 4, position: [1, -1, 2] }\n\t];\n\n\tconst edges = [\n\t\t{ from: 1, to: 2 },\n\t\t{ from: 1, to: 3 },\n\t\t{ from: 2, to: 4 },\n\t\t{ from: 3, to: 4 }\n\t];\n\n\tconst nodeRadius = 0.3;\n\tconst nodeColor = '#FE3D00';\n</script>\n\n<T.PerspectiveCamera\n\tmakeDefault\n\tposition={[10, 10, 10]}\n\ton:create={({ ref }) => {\n\t\tref.lookAt(0, 0, 0);\n\t}}\n>\n\t<OrbitControls />\n</T.PerspectiveCamera>\n\n<T.DirectionalLight position={[3, 10, 7]} intensity={Math.PI} />\n<T.AmbientLight intensity={0.3} />\n\n<T.Group scale={$scale} on:pointerenter={() => scale.set(1.2)} on:pointerleave={() => scale.set(1)}>\n\t{#each nodes as node}\n\t\t<T.Mesh position={node.position}>\n\t\t\t<T.SphereGeometry args={[nodeRadius]} />\n\t\t\t<T.MeshStandardMaterial color={nodeColor} toneMapped={false} />\n\t\t</T.Mesh>\n\t{/each}\n\n\t{#each edges as edge}\n\t\t<T.Line points={[fromPosition, toPosition]} color={edgeColor} lineWidth={2} />\n\t{/each}\n</T.Group>\n\n<Grid cellColor=\"#FE3D00\" sectionColor=\"#FE3D00\" />\n"
  },
  {
    "path": "src/lib/components/LoginForm.svelte",
    "content": "<script lang=\"ts\">\n\timport * as Form from '$lib/components/ui/form';\n\timport { Input } from '$lib/components/ui/input';\n\timport { formSchema, type FormSchema } from './schema';\n\timport { type SuperValidated, type Infer, superForm } from 'sveltekit-superforms';\n\timport { zodClient } from 'sveltekit-superforms/adapters';\n\n\timport * as Card from '$lib/components/ui/card';\n\n\texport let data: SuperValidated<Infer<FormSchema>>;\n\n\tconst form = superForm(data, {\n\t\tvalidators: zodClient(formSchema)\n\t});\n\n\tconst { form: formData, enhance } = form;\n</script>\n\n<Card.Root class=\"mx-auto my-32 h-fit w-[350px]\">\n\t<Card.Header>\n\t\t<Card.Title>Login</Card.Title>\n\t</Card.Header>\n\t<Card.Content>\n\t\t<form method=\"POST\" use:enhance>\n\t\t\t<Form.Field {form} name=\"username\">\n\t\t\t\t<Form.Control let:attrs>\n\t\t\t\t\t<Form.Label>Username</Form.Label>\n\t\t\t\t\t<Input {...attrs} bind:value={$formData.username} class=\"w-[250px]\" />\n\t\t\t\t</Form.Control>\n\t\t\t\t<Form.Description>This is your public display name.</Form.Description>\n\t\t\t\t<Form.FieldErrors />\n\t\t\t</Form.Field>\n\t\t\t<Form.Field {form} name=\"password\">\n\t\t\t\t<Form.Control let:attrs>\n\t\t\t\t\t<Form.Label>Password</Form.Label>\n\t\t\t\t\t<Input {...attrs} bind:value={$formData.password} class=\"w-[250px]\" />\n\t\t\t\t</Form.Control>\n\t\t\t\t<Form.Description>This is your public display name.</Form.Description>\n\t\t\t\t<Form.FieldErrors />\n\t\t\t</Form.Field>\n\t\t\t<Form.Button>Submit</Form.Button>\n\t\t</form>\n\t</Card.Content>\n</Card.Root>\n"
  },
  {
    "path": "src/lib/components/MDGraph.svelte",
    "content": "<script>\n\timport { onMount } from 'svelte';\n\timport { browser } from '$app/environment';\n\timport * as Card from '$lib/components/ui/card';\n\n\tlet graphElement;\n\tlet Graph;\n\n\t// Random tree\n\tconst N = 300;\n\tconst gData = {\n\t\tnodes: [...Array(N).keys()].map((i) => ({ id: i })),\n\t\tlinks: [...Array(N).keys()]\n\t\t\t.filter((id) => id)\n\t\t\t.map((id) => ({\n\t\t\t\tsource: id,\n\t\t\t\ttarget: Math.round(Math.random() * (id - 1))\n\t\t\t})),\n\t\tid: [...Array(N).keys()]\n\t};\n\n\tonMount(async () => {\n\t\tif (browser) {\n\t\t\tconst ForceGraph3D = (await import('3d-force-graph')).default;\n\t\t\tGraph = ForceGraph3D()(graphElement).graphData(gData).nodeLabel('id');\n\t\t\tGraph.width(300);\n\t\t\tGraph.height(300);\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (Graph) {\n\t\t\t\tGraph.pauseAnimation();\n\t\t\t\tGraph._destructor();\n\t\t\t}\n\t\t};\n\t});\n</script>\n\n<div class=\"fixed right-5 top-10 h-[300px] w-[300px]\">\n\t{#if browser}\n\t\t<div bind:this={graphElement}></div>\n\t{/if}\n</div>\n\n<style>\n</style>\n"
  },
  {
    "path": "src/lib/components/MDsvexRenderer.svelte",
    "content": "<!-- MDsveXContentRenderer.svelte -->\n<script lang=\"ts\">\n\timport { onMount, tick } from \"svelte\";\n\timport { afterNavigate } from \"$app/navigation\";\n\timport mermaid from \"mermaid\";\n\timport hljs from \"highlight.js\"; // Import highlight.js\n\timport SvelteMarkdown from \"svelte-markdown\";\n\t// import 'highlight.js/styles/nnfx-dark.min.css'; // Import a default style for highlight.js\n\timport \"highlight.js/styles/default.css\";\n\timport { marked } from \"marked\";\n\timport admonition from \"marked-admonition-extension\";\n\t// import 'highlight.js/styles/github-dark.css';\n\timport { browser } from \"$app/environment\";\n\timport markedAlert from \"marked-alert\";\n\n\texport let content: string;\n\n\tmarked.use(markedAlert());\n\t$: parsedContent = marked(content);\n\n\t// Function to initialize and render mermaid diagrams\n\tconst renderMermaid = async () => {\n\t\t// Reinitialize Mermaid every time we want to render\n\t\tmermaid.initialize({ startOnLoad: false });\n\t\tawait tick(); // Ensure DOM is updated\n\t\tmermaid.run({\n\t\t\tquerySelector: \".mermaid\", // Render all elements with the mermaid class\n\t\t});\n\t};\n\n\tconst updateHighlightTheme = () => {\n\t\tif (browser) {\n\t\t\tconst isDarkMode = window.matchMedia(\n\t\t\t\t\"(prefers-color-scheme: dark)\",\n\t\t\t).matches;\n\t\t\tdocument.documentElement.classList.toggle(\"theme-dark\", isDarkMode);\n\t\t\tdocument.documentElement.classList.toggle(\"theme-light\", !isDarkMode);\n\t\t\thljs.highlightAll();\n\t\t}\n\t};\n\n\tconst highlightCode = () => {\n\t\thljs.highlightAll();\n\t};\n\n\tonMount(async () => {\n\t\tawait renderMermaid();\n\t\thljs.highlightAll();\n\t\t// updateHighlightTheme();\n\n\t\t// if (browser) {\n\t\t// \tconst mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\t\t// \tmediaQuery.addListener(updateHighlightTheme);\n\n\t\t// \treturn () => mediaQuery.removeListener(updateHighlightTheme);\n\t\t// }\n\t});\n\t// Use the afterNavigate hook to re-render mermaid diagrams after every navigation\n\tafterNavigate(async () => {\n\t\tawait renderMermaid();\n\t\t// highlightCode();\n\t});\n</script>\n\n<div class=\"text-lg leading-relaxed md:w-[700px]\">\n\t<SvelteMarkdown bind:source={parsedContent} />\n</div>\n\n<style>\n\t:global(pre) {\n\t\t@apply my-4 rounded bg-carbongray-100 p-2 shadow-sm dark:bg-carbongray-400;\n\t\tmax-width: 100%;\n\t\twhite-space: pre-wrap;\n\t\tword-wrap: break-word;\n\t\toverflow-wrap: break-word;\n\t}\n\n\t:global(pre code) {\n\t\t@apply font-mono text-base;\n\t\tfont-family: monospace;\n\t\twhite-space: pre-wrap;\n\t\tword-wrap: break-word;\n\t\toverflow-wrap: break-word;\n\t}\n\n\t:global(code) {\n\t\t@apply font-mono text-sm;\n\t\tmax-width: 100%;\n\t}\n\n\t/* For inline code */\n\t:global(p code) {\n\t\t@apply bg-carbongray-100 rounded-sm px-1 py-0.5;\n\t\twhite-space: normal;\n\t\tword-wrap: break-word;\n\t\toverflow-wrap: break-word;\n\t}\n\t:global(.mermaid) {\n\t\ttext-align: center;\n\t}\n\n\t:global(.callout) {\n\t\twidth: 100%;\n\t}\n\t:global(.mermaid) {\n\t\t@apply sm:w-[80svw] md:w-[65svw] xl:w-[80svw];\n\t}\n\n\t:global(table) {\n\t\t@apply my-8 w-full text-left text-sm text-gray-500 dark:text-gray-400 rtl:text-right;\n\t}\n\t:global(thead) {\n\t\t@apply bg-gray-50 text-xs uppercase text-gray-700 dark:bg-carbongray-600 dark:text-gray-400;\n\t}\n\t:global(th) {\n\t\t@apply px-6 py-3;\n\t}\n\t:global(tbody tr) {\n\t\t@apply border-b bg-white dark:border-carbongray-600 dark:bg-carbongray-700;\n\t}\n\t:global(tbody td) {\n\t\t@apply whitespace-nowrap border-l-0 border-r-0 border-t-0 p-4 px-6 align-middle text-xs;\n\t}\n\n\t:global(.task-list-item) {\n\t\t@apply flex list-none items-center py-2;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]),\n\t:global(input[type=\"checkbox\"]) {\n\t\t@apply mr-2 h-4 w-4 cursor-pointer appearance-none border bg-white transition-all duration-200 ease-in-out;\n\t\tposition: relative;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:checked),\n\t:global(input[type=\"checkbox\"]:checked) {\n\t\t@apply border-blue-500 bg-blue-500;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:checked::before),\n\t:global(input[type=\"checkbox\"]:checked::before) {\n\t\tcontent: \"\\2713\";\n\t\t@apply absolute text-xs font-bold text-white;\n\t\ttop: 50%;\n\t\tleft: 50%;\n\t\ttransform: translate(-50%, -50%);\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled),\n\t:global(input[type=\"checkbox\"]:disabled) {\n\t\t@apply cursor-not-allowed border-carbongray-800;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled:checked),\n\t:global(input[type=\"checkbox\"]:disabled:checked) {\n\t\t@apply border-carbongray-700 bg-carbongray-100 dark:bg-carbongray-600;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled:checked::before),\n\t:global(input[type=\"checkbox\"]:disabled:checked::before) {\n\t\t@apply text-gray-500 dark:text-carbongray-100;\n\t}\n\n\t:global(.task-list-item),\n\t:global(input[type=\"checkbox\"] + *) {\n\t\t@apply select-none text-lg;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled ~ *),\n\t:global(input[type=\"checkbox\"]:disabled + *) {\n\t\t@apply text-gray-400;\n\t}\n\n\t:global(li) {\n\t\t@apply relative pb-2 pl-8 text-lg leading-relaxed;\n\t\t@apply flex flex-col items-start justify-start;\n\t}\n\n\t/* Unordered list styles with centered dot */\n\t:global(ul > li::before) {\n\t\tcontent: \"\";\n\t\t@apply absolute left-2 top-[35%] h-2 w-2 rounded-full bg-carbonblue-500;\n\t}\n\n\t/* Ordered list styles */\n\t/* :global(ol > li::before) {\n\t\tcontent: counter(list-counter);\n\t\tcounter-increment: list-counter;\n\t\t@apply absolute left-0 top-[0.3em] flex h-5 w-5 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-500;\n\t} */\n\n\t:global(ol) {\n\t\tcounter-reset: list-counter;\n\t\tlist-style-type: none;\n\t\tpadding-left: 0;\n\t}\n\n\t:global(ol > li) {\n\t\tcounter-increment: list-counter;\n\t\t@apply relative pb-2 pl-8 text-lg leading-relaxed;\n\t\t@apply flex flex-col items-start justify-start;\n\t}\n\n\t:global(ol > li::before) {\n\t\tcontent: counter(list-counter);\n\t\t@apply absolute left-0 top-[0.3em] flex h-5 w-5 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-500;\n\t}\n\n\t/* Task list item specific adjustments */\n\t:global(.task-list-item) {\n\t\t@apply flex flex-row items-start pl-0;\n\t}\n\n\t:global(.task-list-item::before) {\n\t\tcontent: none;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]) {\n\t\t@apply mr-2 mt-1;\n\t}\n\n\t/* Checkbox styles */\n\t:global(.task-list-item input[type=\"checkbox\"]),\n\t:global(input[type=\"checkbox\"]) {\n\t\t@apply h-4 w-4 cursor-pointer appearance-none rounded border border-gray-300 bg-white transition-all duration-200 ease-in-out;\n\t\tposition: relative;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:checked),\n\t:global(input[type=\"checkbox\"]:checked) {\n\t\t@apply border-blue-500 bg-blue-500;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:checked::before),\n\t:global(input[type=\"checkbox\"]:checked::before) {\n\t\tcontent: \"\\2713\";\n\t\t@apply absolute text-xs font-bold text-white;\n\t\ttop: 50%;\n\t\tleft: 50%;\n\t\ttransform: translate(-50%, -50%);\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled),\n\t:global(input[type=\"checkbox\"]:disabled) {\n\t\t@apply cursor-not-allowed border-gray-200 bg-gray-100;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled:checked),\n\t:global(input[type=\"checkbox\"]:disabled:checked) {\n\t\t@apply border-gray-300 bg-gray-300;\n\t}\n\n\t:global(.task-list-item input[type=\"checkbox\"]:disabled:checked::before),\n\t:global(input[type=\"checkbox\"]:disabled:checked::before) {\n\t\t@apply text-white;\n\t}\n\t:global(img) {\n\t\tmax-width: 100%;\n\t}\n\n\t:global(blockquote) {\n\t\t@apply relative p-4;\n\t}\n\n\t:global(blockquote::before) {\n\t\tcontent: \"\";\n\t\t@apply absolute -left-8 -top-2 h-20 w-20 bg-carbongray-100 dark:bg-carbongray-600; /* Positioning, size, and color */\n\t\tmask: url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M7.39762 10.3C7.39762 11.0733 7.14888 11.7 6.6514 12.18C6.15392 12.6333 5.52552 12.86 4.76621 12.86C3.84979 12.86 3.09047 12.5533 2.48825 11.94C1.91222 11.3266 1.62421 10.4467 1.62421 9.29999C1.62421 8.07332 1.96459 6.87332 2.64535 5.69999C3.35231 4.49999 4.33418 3.55332 5.59098 2.85999L6.4943 4.25999C5.81354 4.73999 5.26369 5.27332 4.84476 5.85999C4.45201 6.44666 4.19017 7.12666 4.05926 7.89999C4.29491 7.79332 4.56983 7.73999 4.88403 7.73999C5.61716 7.73999 6.21938 7.97999 6.69067 8.45999C7.16197 8.93999 7.39762 9.55333 7.39762 10.3ZM14.6242 10.3C14.6242 11.0733 14.3755 11.7 13.878 12.18C13.3805 12.6333 12.7521 12.86 11.9928 12.86C11.0764 12.86 10.3171 12.5533 9.71484 11.94C9.13881 11.3266 8.85079 10.4467 8.85079 9.29999C8.85079 8.07332 9.19117 6.87332 9.87194 5.69999C10.5789 4.49999 11.5608 3.55332 12.8176 2.85999L13.7209 4.25999C13.0401 4.73999 12.4903 5.27332 12.0713 5.85999C11.6786 6.44666 11.4168 7.12666 11.2858 7.89999C11.5215 7.79332 11.7964 7.73999 12.1106 7.73999C12.8437 7.73999 13.446 7.97999 13.9173 8.45999C14.3886 8.93999 14.6242 9.55333 14.6242 10.3Z\" fill=\"currentColor\"/></svg>');\n\t}\n\n\t:global(blockquote p) {\n\t\t@apply relative z-10 text-xl font-light italic;\n\t}\n\t:global(.highlight) {\n\t\t@apply bg-[#ef538c] p-0.5 text-carbongray-900;\n\t}\n\n\t:global(.tag a) {\n\t\t@apply mx-0.5 rounded-sm bg-[#fedc69] p-0.5 text-carbongray-900;\n\t}\n\t:global(.hljs-light) {\n\t\t--hljs-theme: initial;\n\t}\n\n\t:global(.hljs-dark) {\n\t\t--hljs-theme: github-dark;\n\t}\n\n\t:global(.hljs) {\n\t\tbackground: var(--hljs-theme);\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/MarkdownGraph.svelte",
    "content": "<script>\n\timport { onMount } from 'svelte';\n\timport { Canvas } from '@threlte/core';\n\t/* import Scene from '$lib/components/Scene.svelte'; */\n\timport Scene from '$lib/components/GraphScene.svelte';\n\n\texport let graphData;\n\tlet nodes = [];\n\tlet edges = [];\n\tlet simulation;\n\n\tonMount(() => {\n\t\tif (graphData) {\n\t\t\tnodes = graphData.nodes.map((node) => ({ ...node, x: 0, y: 0, z: 0 }));\n\t\t\tedges = graphData.edges.map((edge) => ({\n\t\t\t\tsource: nodes.find((node) => node.id === edge.from),\n\t\t\t\ttarget: nodes.find((node) => node.id === edge.to)\n\t\t\t}));\n\n\t\t\tsimulation = forceSimulation(nodes)\n\t\t\t\t.force(\n\t\t\t\t\t'link',\n\t\t\t\t\tforceLink(edges)\n\t\t\t\t\t\t.id((d) => d.id)\n\t\t\t\t\t\t.distance(100)\n\t\t\t\t)\n\t\t\t\t.force('charge', forceManyBody().strength(-200))\n\t\t\t\t.force('center', forceCenter(0, 0, 0));\n\n\t\t\tsimulation.on('tick', () => {\n\t\t\t\tnodes = [...nodes];\n\t\t\t\tedges = [...edges];\n\t\t\t});\n\t\t}\n\t});\n</script>\n\n<div class=\"h-[200px]\">\n\t<div class=\"wrapper\">\n\t\t<Canvas>\n\t\t\t<Scene />\n\t\t</Canvas>\n\t</div>\n</div>\n\n<style>\n\tdiv.wrapper {\n\t\theight: 100%;\n\t}\n\tdiv.description {\n\t\tposition: absolute;\n\t\tbottom: 10px;\n\t\tleft: 10px;\n\t\tz-index: 10;\n\t\tcolor: #fe3d00;\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/Scene.svelte",
    "content": "<script lang=\"ts\">\n\timport { T } from '@threlte/core';\n\timport { Grid, OrbitControls, interactivity } from '@threlte/extras';\n\timport { spring } from 'svelte/motion';\n\n\tinteractivity();\n\n\tconst scale = spring(1);\n</script>\n\n<T.PerspectiveCamera\n\tmakeDefault\n\tposition={[10, 10, 10]}\n\ton:create={({ ref }) => {\n\t\tref.lookAt(0, 0, 0);\n\t}}\n>\n\t<OrbitControls />\n</T.PerspectiveCamera>\n\n<T.DirectionalLight position={[3, 10, 7]} intensity={Math.PI} />\n\n<T.AmbientLight intensity={0.3} />\n\n<T.Group scale={$scale} on:pointerenter={() => scale.set(1.5)} on:pointerleave={() => scale.set(1)}>\n\t<T.Mesh position.y={1}>\n\t\t<T.SphereGeometry args={[1]} />\n\t\t<T.MeshStandardMaterial color=\"#FE3D00\" toneMapped={false} />\n\t</T.Mesh>\n</T.Group>\n\n<Grid cellColor=\"#FE3D00\" sectionColor=\"#FE3D00\" />\n"
  },
  {
    "path": "src/lib/components/SearchComponent.svelte",
    "content": "<script lang=\"ts\">\n\timport { onMount } from 'svelte';\n\timport { fade } from 'svelte/transition';\n\timport { debounce } from 'lodash-es';\n\timport { Search, Loader, X } from 'lucide-svelte';\n\timport { Button } from '$lib/components/ui/button';\n\timport { Input } from '$lib/components/ui/input';\n\timport * as Dialog from '$lib/components/ui/dialog/index.js';\n\n\tinterface SearchResult {\n\t\ttitle: string;\n\t\turl: string;\n\t\tsnippet: string;\n\t}\n\n\tlet searchQuery = '';\n\tlet searchResults: SearchResult[] = [];\n\tlet isLoading = false;\n\tlet isOpen = false;\n\tlet selectedIndex = -1;\n\n\tconst debouncedSearch = debounce(async () => {\n\t\tif (searchQuery.trim() === '') {\n\t\t\tsearchResults = [];\n\t\t\treturn;\n\t\t}\n\t\tisLoading = true;\n\t\tselectedIndex = -1;\n\t\ttry {\n\t\t\tconst response = await fetch(`/api/search?query=${encodeURIComponent(searchQuery)}`);\n\t\t\tif (response.ok) {\n\t\t\t\tconst data = await response.json();\n\t\t\t\tsearchResults = Array.isArray(data) ? data : [];\n\t\t\t} else {\n\t\t\t\tconsole.error('Search failed:', await response.text());\n\t\t\t\tsearchResults = [];\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error('Search error:', error);\n\t\t\tsearchResults = [];\n\t\t} finally {\n\t\t\tisLoading = false;\n\t\t}\n\t}, 300);\n\n\t$: {\n\t\tif (isOpen && searchQuery.trim() !== '') {\n\t\t\tdebouncedSearch();\n\t\t}\n\t}\n\n\tfunction toggleSearch() {\n\t\tisOpen = !isOpen;\n\t\tif (!isOpen) {\n\t\t\tclearSearch();\n\t\t}\n\t}\n\n\tfunction clearSearch() {\n\t\tsearchQuery = '';\n\t\tsearchResults = [];\n\t\tselectedIndex = -1;\n\t}\n\n\tfunction handleResultClick(url: string) {\n\t\tisOpen = false;\n\t\tclearSearch();\n\t\twindow.location.href = `/${url}`;\n\t}\n\n\tfunction highlightMatch(text: string, query: string) {\n\t\tif (!query.trim()) return text;\n\t\tconst regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`, 'gi');\n\t\treturn text.replace(\n\t\t\tregex,\n\t\t\t'<mark class=\"bg-yellow-200 text-gray-900 rounded px-0.5\">$1</mark>'\n\t\t);\n\t}\n\n\tfunction handleKeydown(event: KeyboardEvent) {\n\t\tif (!isOpen) return;\n\n\t\tswitch (event.key) {\n\t\t\tcase 'ArrowDown':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tselectedIndex = (selectedIndex + 1) % searchResults.length;\n\t\t\t\tbreak;\n\t\t\tcase 'ArrowUp':\n\t\t\t\tevent.preventDefault();\n\t\t\t\tselectedIndex = (selectedIndex - 1 + searchResults.length) % searchResults.length;\n\t\t\t\tbreak;\n\t\t\tcase 'Enter':\n\t\t\t\tif (selectedIndex >= 0 && selectedIndex < searchResults.length) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\thandleResultClick(searchResults[selectedIndex].url);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 'Escape':\n\t\t\t\tevent.preventDefault();\n\t\t\t\ttoggleSearch();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tonMount(() => {\n\t\tconst handleGlobalKeydown = (event: KeyboardEvent) => {\n\t\t\tif (event.key === 'k' && (event.ctrlKey || event.metaKey)) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\ttoggleSearch();\n\t\t\t}\n\t\t};\n\t\twindow.addEventListener('keydown', handleGlobalKeydown);\n\t\treturn () => {\n\t\t\twindow.removeEventListener('keydown', handleGlobalKeydown);\n\t\t};\n\t});\n</script>\n\n<svelte:window on:keydown={handleKeydown} />\n\n<Dialog.Root bind:open={isOpen} on:close={clearSearch}>\n\t<Dialog.Trigger>\n\t\t<Button variant=\"ghost\" size=\"icon\" on:click={toggleSearch} aria-label=\"Open search\">\n\t\t\t<Search size={20} />\n\t\t</Button>\n\t</Dialog.Trigger>\n\n\t<Dialog.Content class=\"sm:max-w-[560px]\">\n\t\t<Dialog.Header>\n\t\t\t<Dialog.Title>Search</Dialog.Title>\n\t\t</Dialog.Header>\n\n\t\t<div class=\"relative\">\n\t\t\t<Input type=\"text\" bind:value={searchQuery} placeholder=\"Search...\" class=\"pr-10\" />\n\t\t\t<div class=\"absolute inset-y-0 right-0 flex items-center pr-3\">\n\t\t\t\t{#if isLoading}\n\t\t\t\t\t<Loader size={20} class=\"animate-spin text-gray-400\" />\n\t\t\t\t{:else if searchQuery}\n\t\t\t\t\t<button\n\t\t\t\t\t\ton:click={clearSearch}\n\t\t\t\t\t\tclass=\"text-gray-400 hover:text-gray-600\"\n\t\t\t\t\t\taria-label=\"Clear search\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<X size={20} />\n\t\t\t\t\t</button>\n\t\t\t\t{:else}\n\t\t\t\t\t<Search size={20} class=\"text-gray-400\" />\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t</div>\n\n\t\t{#if searchResults.length > 0}\n\t\t\t<div class=\"mt-4 max-h-[calc(60vh-2rem)] divide-y divide-carbongray-700 overflow-y-auto\">\n\t\t\t\t{#each searchResults as result, index}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass=\"p-2 py-3 transition-colors duration-150 ease-in-out hover:bg-carbongray-700 {index ===\n\t\t\t\t\t\tselectedIndex\n\t\t\t\t\t\t\t? 'bg-blue-50'\n\t\t\t\t\t\t\t: ''}\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ton:click={() => handleResultClick(result.url)}\n\t\t\t\t\t\t\tclass=\"w-full rounded text-left focus:outline-none focus:ring-2 focus:ring-blue-500\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<h3 class=\"mb-1 text-lg font-semibold\">\n\t\t\t\t\t\t\t\t{@html highlightMatch(result.title, searchQuery)}\n\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t<p class=\"line-clamp-2 text-sm\">\n\t\t\t\t\t\t\t\t{@html highlightMatch(result.snippet, searchQuery)}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t{/each}\n\t\t\t</div>\n\t\t{:else if searchQuery && !isLoading}\n\t\t\t<p class=\"mt-4 text-center text-sm text-gray-500\">No results found</p>\n\t\t{/if}\n\t</Dialog.Content>\n</Dialog.Root>\n"
  },
  {
    "path": "src/lib/components/Sidebar.svelte",
    "content": "<script lang=\"ts\">\n\timport { Sun, Moon, User } from 'lucide-svelte';\n\n\timport { toggleMode } from 'mode-watcher';\n\timport FileTree from '$lib/components/FileTree.svelte';\n\timport SearchComponent from '$lib/components/SearchComponent.svelte';\n\timport { Button } from '$lib/components/ui/button/index.js';\n\n\texport let title;\n\texport let captions;\n</script>\n\n<div class=\"mx-auto my-6 flex w-5/6 flex-col justify-start gap-3\">\n\t<div class=\"stitle flex-shrink text-4xl uppercase text-carbonblue-400\">\n\t\t{title}\n\t</div>\n\t<div class=\"gap-0.3 mb-8 flex flex-col justify-start text-sm\">\n\t\t<div>{captions[0]} / {captions[1]}</div>\n\t\t<div>{captions[2]}</div>\n\t</div>\n\n\t<Button\n\t\ton:click={toggleMode}\n\t\tvariant=\"ghost\"\n\t\tsize=\"icon\"\n\t\tclass=\"absolute right-2 top-2 h-[15px] w-[15px]\"\n\t>\n\t\t<Sun\n\t\t\tclass=\"h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0\"\n\t\t/>\n\t\t<Moon\n\t\t\tclass=\"absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100\"\n\t\t/>\n\t\t<span class=\"sr-only\">Toggle theme</span>\n\t</Button>\n\n\t<slot></slot>\n</div>\n\n<style>\n\t.stitle {\n\t\tfont-family: 'Lombok';\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/TagBar.svelte",
    "content": "<script lang=\"ts\">\n\timport { afterNavigate, beforeNavigate } from \"$app/navigation\";\n\timport { Tags } from \"lucide-svelte\";\n\timport { Button } from \"$lib/components/ui/button\";\n\timport { ScrollArea } from \"$lib/components/ui/scroll-area/index.js\";\n\n\texport let tags;\n\tlet hidden = true;\n\n\tfunction toggle() {\n\t\thidden = !hidden;\n\t}\n\n\tbeforeNavigate(() => {\n\t\thidden = true;\n\t});\n</script>\n\n<Button variant=\"ghost\" size=\"icon\" on:click={toggle}>\n\t<Tags />\n</Button>\n\n<ScrollArea\n\tclass={`${hidden ? \"hidden\" : \"\"} fixed right-4 top-9 h-[500px] w-[200px] rounded-sm bg-carbongray-50 dark:bg-carbongray-700 p-2 shadow`}\n>\n\t<div class=\"flex flex-col justify-center gap-2 p-2\">\n\t\t<div class=\"my-2 text-base font-bold\">TAGS</div>\n\t\t{#each tags as tag (tag.name)}\n\t\t\t<div>\n\t\t\t\t<a\n\t\t\t\t\tclass=\"text-primary hover:bg-carbongray-100 w-full p-1 rounded-sm dark:hover:bg-carbongray-600\"\n\t\t\t\t\thref=\"/tags/{tag.name}\">#{tag.name}</a\n\t\t\t\t>\n\t\t\t</div>\n\t\t{/each}\n\t</div>\n</ScrollArea>\n"
  },
  {
    "path": "src/lib/components/TopBar.svelte",
    "content": "<script lang=\"ts\">\n\timport { Button } from '$lib/components/ui/button';\n\timport { Menu } from 'lucide-svelte';\n\timport { isSidebarVisible } from '$lib/stores/sidebarStore';\n\n\tfunction toggleSidebar() {\n\t\tisSidebarVisible.update((value) => !value);\n\t}\n\texport let title;\n</script>\n\n<div class=\"absolute left-0 top-0 z-30 flex h-[40px] items-center justify-between md:hidden\">\n\t<Button variant=\"ghost\" size=\"icon\" on:click={toggleSidebar}\n\t\t><Menu class=\"text-carbongray-300\" /></Button\n\t>\n\t{#if title}\n\t\t<div class=\"title text-2xl\">{title}</div>\n\t{/if}\n</div>\n\n<style>\n\t.title {\n\t\tfont-family: Megrim;\n\t}\n</style>\n"
  },
  {
    "path": "src/lib/components/award.svelte",
    "content": "<script>\n\texport let starColor = '#000';\n\texport let size = 30;\n</script>\n\n<svg\n\twidth=\"{size}px\"\n\theight=\"{size}px\"\n\tversion=\"1.1\"\n\tviewBox=\"0 0 100 100\"\n\txmlns=\"http://www.w3.org/2000/svg\"\n>\n\t<path\n\t\tfill={starColor}\n\t\td=\"m30.891 74.191h0.56641v-1.8633c0-1.5508 0.63281-2.9648 1.6562-3.9844 1.0195-1.0195 2.4297-1.6562 3.9844-1.6562h0.54688l-9.1719-41.957c-0.23438-1.0781 0.45312-2.1406 1.5312-2.375 0.29297-0.0625 0.58594-0.058594 0.85938 0.003906 6.2734 1.2227 13.332-0.28516 20.129-3.2578 8.4766-3.707 16.5-9.668 22.156-15.496 0.76953-0.78906 2.0312-0.80859 2.8203-0.039063 0.52734 0.51172 0.70703 1.2422 0.54688 1.9062l-15.148 61.211h0.47656c1.5469 0 2.957 0.63672 3.9805 1.6562 1.0273 1.0273 1.6641 2.4375 1.6641 3.9844v1.8633h0.56641c2.0547 0 3.9219 0.83984 5.2734 2.1914 1.3516 1.3516 2.1914 3.2227 2.1914 5.2734v13.344c0 1.1055-0.89453 2-2 2h-48.09c-1.1055 0-2-0.89453-2-2v-13.344c0-2.0547 0.83984-3.9219 2.1914-5.2734l0.12109-0.11328c1.3438-1.2852 3.1602-2.0781 5.1523-2.0781zm20.34-45.363 2.9062 5.4062 6.0312 1.0938c1.082 0.19531 1.8008 1.2305 1.6094 2.3125-0.070313 0.39844-0.25781 0.74609-0.51562 1.0195l-4.2422 4.4414 0.82422 6.082c0.14453 1.0898-0.62109 2.0977-1.7109 2.2422-0.40234 0.054688-0.79297-0.015625-1.1328-0.17969l-5.5273-2.6602-5.5273 2.6641c-0.99219 0.47656-2.1875 0.0625-2.6641-0.92969-0.18359-0.37891-0.23438-0.78906-0.17188-1.1758l0.82031-6.0391-4.2461-4.4375c-0.75781-0.79688-0.72656-2.0625 0.070312-2.8203 0.31641-0.30078 0.71094-0.47656 1.1133-0.53125l5.9414-1.0742 2.9062-5.4062c0.51953-0.96875 1.7344-1.332 2.7031-0.8125 0.35938 0.19141 0.63281 0.48047 0.8125 0.8125zm-0.15234 8.1406-1.6055-2.9805-1.6055 2.9805c-0.28125 0.51562-0.78516 0.90625-1.4062 1.0156l-3.332 0.60547 2.3477 2.457c0.40234 0.42188 0.61719 1.0195 0.53125 1.6406l-0.45703 3.3594 3.0508-1.4688c0.52734-0.25391 1.1641-0.27344 1.7344 0l3.0508 1.4688-0.45312-3.3594c-0.078125-0.57812 0.097656-1.1875 0.53125-1.6406l2.3477-2.457-3.2305-0.58594c-0.61719-0.078125-1.1914-0.44531-1.5078-1.0352zm-11.797 47.348c-1.1055 0-2-0.89453-2-2 0-1.1055 0.89453-2 2-2h20.383c1.1055 0 2 0.89453 2 2 0 1.1055-0.89453 2-2 2zm0 6.5586c-1.1055 0-2-0.89453-2-2s0.89453-2 2-2h20.383c1.1055 0 2 0.89453 2 2s-0.89453 2-2 2zm2.457-24.188h15.52l13.77-55.637c-5.3086 4.5273-11.734 8.7773-18.438 11.711-6.4883 2.8359-13.262 4.4492-19.598 3.9219l8.7461 40.008zm-6.2773 7.5039h28.027v-1.8633c0-0.45312-0.18359-0.86719-0.47656-1.1602-0.29688-0.29297-0.71094-0.47656-1.1641-0.47656h-24.75c-0.44922 0-0.85938 0.18359-1.1562 0.48047s-0.48047 0.70703-0.48047 1.1562zm32.594 4h-37.16c-0.91797 0-1.75 0.35938-2.3672 0.93359l-0.078125 0.082031c-0.62891 0.62891-1.0195 1.4922-1.0195 2.4453v11.34h44.086v-11.34c0-0.94922-0.39062-1.8203-1.0195-2.4453-0.62891-0.62891-1.4922-1.0195-2.4453-1.0195z\"\n\t/>\n</svg>\n"
  },
  {
    "path": "src/lib/components/schema.ts",
    "content": "import { z } from 'zod';\n\nexport const formSchema = z.object({\n\tusername: z.string(),\n\tpassword: z.string()\n});\n\nexport type FormSchema = typeof formSchema;\n"
  },
  {
    "path": "src/lib/components/ui/button/button.svelte",
    "content": "<script lang=\"ts\">\n\timport { Button as ButtonPrimitive } from \"bits-ui\";\n\timport { type Events, type Props, buttonVariants } from \"./index.js\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = Props;\n\ttype $$Events = Events;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let variant: $$Props[\"variant\"] = \"default\";\n\texport let size: $$Props[\"size\"] = \"default\";\n\texport let builders: $$Props[\"builders\"] = [];\n\texport { className as class };\n</script>\n\n<ButtonPrimitive.Root\n\t{builders}\n\tclass={cn(buttonVariants({ variant, size, className }))}\n\ttype=\"button\"\n\t{...$$restProps}\n\ton:click\n\ton:keydown\n>\n\t<slot />\n</ButtonPrimitive.Root>\n"
  },
  {
    "path": "src/lib/components/ui/button/index.ts",
    "content": "import type { Button as ButtonPrimitive } from \"bits-ui\";\nimport { type VariantProps, tv } from \"tailwind-variants\";\nimport Root from \"./button.svelte\";\n\nconst buttonVariants = tv({\n\tbase: \"focus-visible:ring-ring inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50\",\n\tvariants: {\n\t\tvariant: {\n\t\t\tdefault: \"bg-primary text-primary-foreground hover:bg-primary/90 shadow\",\n\t\t\tdestructive:\n\t\t\t\t\"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm\",\n\t\t\toutline:\n\t\t\t\t\"border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm\",\n\t\t\tsecondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm\",\n\t\t\tghost: \"hover:bg-accent hover:text-accent-foreground\",\n\t\t\tlink: \"text-primary underline-offset-4 hover:underline\",\n\t\t},\n\t\tsize: {\n\t\t\tdefault: \"h-9 px-4 py-2\",\n\t\t\tsm: \"h-8 rounded-md px-3 text-xs\",\n\t\t\tlg: \"h-10 rounded-md px-8\",\n\t\t\ticon: \"h-9 w-9\",\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tvariant: \"default\",\n\t\tsize: \"default\",\n\t},\n});\n\ntype Variant = VariantProps<typeof buttonVariants>[\"variant\"];\ntype Size = VariantProps<typeof buttonVariants>[\"size\"];\n\ntype Props = ButtonPrimitive.Props & {\n\tvariant?: Variant;\n\tsize?: Size;\n};\n\ntype Events = ButtonPrimitive.Events;\n\nexport {\n\tRoot,\n\ttype Props,\n\ttype Events,\n\t//\n\tRoot as Button,\n\ttype Props as ButtonProps,\n\ttype Events as ButtonEvents,\n\tbuttonVariants,\n};\n"
  },
  {
    "path": "src/lib/components/ui/card/card-content.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLDivElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<div class={cn(\"p-6 pt-0\", className)} {...$$restProps}>\n\t<slot />\n</div>\n"
  },
  {
    "path": "src/lib/components/ui/card/card-description.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLParagraphElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<p class={cn(\"text-muted-foreground text-sm\", className)} {...$$restProps}>\n\t<slot />\n</p>\n"
  },
  {
    "path": "src/lib/components/ui/card/card-footer.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLDivElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<div class={cn(\"flex items-center p-6 pt-0\", className)} {...$$restProps}>\n\t<slot />\n</div>\n"
  },
  {
    "path": "src/lib/components/ui/card/card-header.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLDivElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<div class={cn(\"flex flex-col space-y-1.5 p-6\", className)} {...$$restProps}>\n\t<slot />\n</div>\n"
  },
  {
    "path": "src/lib/components/ui/card/card-title.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport type { HeadingLevel } from \"./index.js\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLHeadingElement> & {\n\t\ttag?: HeadingLevel;\n\t};\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let tag: $$Props[\"tag\"] = \"h3\";\n\texport { className as class };\n</script>\n\n<svelte:element\n\tthis={tag}\n\tclass={cn(\"font-semibold leading-none tracking-tight\", className)}\n\t{...$$restProps}\n>\n\t<slot />\n</svelte:element>\n"
  },
  {
    "path": "src/lib/components/ui/card/card.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLDivElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<!-- svelte-ignore a11y-no-static-element-interactions -->\n<div\n\tclass={cn(\"bg-card text-card-foreground rounded-xl border shadow\", className)}\n\t{...$$restProps}\n\ton:click\n\ton:focusin\n\ton:focusout\n\ton:mouseenter\n\ton:mouseleave\n>\n\t<slot />\n</div>\n"
  },
  {
    "path": "src/lib/components/ui/card/index.ts",
    "content": "import Root from \"./card.svelte\";\nimport Content from \"./card-content.svelte\";\nimport Description from \"./card-description.svelte\";\nimport Footer from \"./card-footer.svelte\";\nimport Header from \"./card-header.svelte\";\nimport Title from \"./card-title.svelte\";\n\nexport {\n\tRoot,\n\tContent,\n\tDescription,\n\tFooter,\n\tHeader,\n\tTitle,\n\t//\n\tRoot as Card,\n\tContent as CardContent,\n\tDescription as CardDescription,\n\tFooter as CardFooter,\n\tHeader as CardHeader,\n\tTitle as CardTitle,\n};\n\nexport type HeadingLevel = \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-content.svelte",
    "content": "<script lang=\"ts\">\n\timport { Dialog as DialogPrimitive } from \"bits-ui\";\n\timport Cross2 from \"svelte-radix/Cross2.svelte\";\n\timport * as Dialog from \"./index.js\";\n\timport { cn, flyAndScale } from \"$lib/utils.js\";\n\n\ttype $$Props = DialogPrimitive.ContentProps;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let transition: $$Props[\"transition\"] = flyAndScale;\n\texport let transitionConfig: $$Props[\"transitionConfig\"] = {\n\t\tduration: 200,\n\t};\n\texport { className as class };\n</script>\n\n<Dialog.Portal>\n\t<Dialog.Overlay />\n\t<DialogPrimitive.Content\n\t\t{transition}\n\t\t{transitionConfig}\n\t\tclass={cn(\n\t\t\t\"bg-background fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg sm:rounded-lg md:w-full\",\n\t\t\tclassName\n\t\t)}\n\t\t{...$$restProps}\n\t>\n\t\t<slot />\n\t\t<DialogPrimitive.Close\n\t\t\tclass=\"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none\"\n\t\t>\n\t\t\t<Cross2 class=\"h-4 w-4\" />\n\t\t\t<span class=\"sr-only\">Close</span>\n\t\t</DialogPrimitive.Close>\n\t</DialogPrimitive.Content>\n</Dialog.Portal>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-description.svelte",
    "content": "<script lang=\"ts\">\n\timport { Dialog as DialogPrimitive } from \"bits-ui\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = DialogPrimitive.DescriptionProps;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<DialogPrimitive.Description\n\tclass={cn(\"text-muted-foreground text-sm\", className)}\n\t{...$$restProps}\n>\n\t<slot />\n</DialogPrimitive.Description>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-footer.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLDivElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<div\n\tclass={cn(\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\", className)}\n\t{...$$restProps}\n>\n\t<slot />\n</div>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-header.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLAttributes<HTMLDivElement>;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<div class={cn(\"flex flex-col space-y-1.5 text-center sm:text-left\", className)} {...$$restProps}>\n\t<slot />\n</div>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-overlay.svelte",
    "content": "<script lang=\"ts\">\n\timport { Dialog as DialogPrimitive } from \"bits-ui\";\n\timport { fade } from \"svelte/transition\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = DialogPrimitive.OverlayProps;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let transition: $$Props[\"transition\"] = fade;\n\texport let transitionConfig: $$Props[\"transitionConfig\"] = {\n\t\tduration: 150,\n\t};\n\texport { className as class };\n</script>\n\n<DialogPrimitive.Overlay\n\t{transition}\n\t{transitionConfig}\n\tclass={cn(\"bg-background/80 fixed inset-0 z-50 backdrop-blur-sm \", className)}\n\t{...$$restProps}\n/>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-portal.svelte",
    "content": "<script lang=\"ts\">\n\timport { Dialog as DialogPrimitive } from \"bits-ui\";\n\n\ttype $$Props = DialogPrimitive.PortalProps;\n</script>\n\n<DialogPrimitive.Portal {...$$restProps}>\n\t<slot />\n</DialogPrimitive.Portal>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/dialog-title.svelte",
    "content": "<script lang=\"ts\">\n\timport { Dialog as DialogPrimitive } from \"bits-ui\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = DialogPrimitive.TitleProps;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<DialogPrimitive.Title\n\tclass={cn(\"text-lg font-semibold leading-none tracking-tight\", className)}\n\t{...$$restProps}\n>\n\t<slot />\n</DialogPrimitive.Title>\n"
  },
  {
    "path": "src/lib/components/ui/dialog/index.ts",
    "content": "import { Dialog as DialogPrimitive } from \"bits-ui\";\n\nimport Title from \"./dialog-title.svelte\";\nimport Portal from \"./dialog-portal.svelte\";\nimport Footer from \"./dialog-footer.svelte\";\nimport Header from \"./dialog-header.svelte\";\nimport Overlay from \"./dialog-overlay.svelte\";\nimport Content from \"./dialog-content.svelte\";\nimport Description from \"./dialog-description.svelte\";\n\nconst Root = DialogPrimitive.Root;\nconst Trigger = DialogPrimitive.Trigger;\nconst Close = DialogPrimitive.Close;\n\nexport {\n\tRoot,\n\tTitle,\n\tPortal,\n\tFooter,\n\tHeader,\n\tTrigger,\n\tOverlay,\n\tContent,\n\tDescription,\n\tClose,\n\t//\n\tRoot as Dialog,\n\tTitle as DialogTitle,\n\tPortal as DialogPortal,\n\tFooter as DialogFooter,\n\tHeader as DialogHeader,\n\tTrigger as DialogTrigger,\n\tOverlay as DialogOverlay,\n\tContent as DialogContent,\n\tDescription as DialogDescription,\n\tClose as DialogClose,\n};\n"
  },
  {
    "path": "src/lib/components/ui/form/form-button.svelte",
    "content": "<script lang=\"ts\">\n\timport * as Button from \"$lib/components/ui/button/index.js\";\n\n\ttype $$Props = Button.Props;\n\ttype $$Events = Button.Events;\n</script>\n\n<Button.Root type=\"submit\" on:click on:keydown {...$$restProps}>\n\t<slot />\n</Button.Root>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-description.svelte",
    "content": "<script lang=\"ts\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = FormPrimitive.DescriptionProps;\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<FormPrimitive.Description\n\tclass={cn(\"text-muted-foreground text-[0.8rem]\", className)}\n\t{...$$restProps}\n\tlet:descriptionAttrs\n>\n\t<slot {descriptionAttrs} />\n</FormPrimitive.Description>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-element-field.svelte",
    "content": "<script lang=\"ts\" context=\"module\">\n\timport type { FormPathLeaves, SuperForm } from \"sveltekit-superforms\";\n\ttype T = Record<string, unknown>;\n\ttype U = FormPathLeaves<T>;\n</script>\n\n<script lang=\"ts\" generics=\"T extends Record<string, unknown>, U extends FormPathLeaves<T>\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport * as FormPrimitive from \"formsnap\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = FormPrimitive.ElementFieldProps<T, U> & HTMLAttributes<HTMLDivElement>;\n\n\texport let form: SuperForm<T>;\n\texport let name: U;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<FormPrimitive.ElementField {form} {name} let:constraints let:errors let:tainted let:value>\n\t<div class={cn(\"space-y-2\", className)}>\n\t\t<slot {constraints} {errors} {tainted} {value} />\n\t</div>\n</FormPrimitive.ElementField>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-field-errors.svelte",
    "content": "<script lang=\"ts\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = FormPrimitive.FieldErrorsProps & {\n\t\terrorClasses?: string | undefined | null;\n\t};\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n\texport let errorClasses: $$Props[\"class\"] = undefined;\n</script>\n\n<FormPrimitive.FieldErrors\n\tclass={cn(\"text-destructive text-[0.8rem] font-medium\", className)}\n\t{...$$restProps}\n\tlet:errors\n\tlet:fieldErrorsAttrs\n\tlet:errorAttrs\n>\n\t<slot {errors} {fieldErrorsAttrs} {errorAttrs}>\n\t\t{#each errors as error}\n\t\t\t<div {...errorAttrs} class={cn(errorClasses)}>{error}</div>\n\t\t{/each}\n\t</slot>\n</FormPrimitive.FieldErrors>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-field.svelte",
    "content": "<script lang=\"ts\" context=\"module\">\n\timport type { FormPath, SuperForm } from \"sveltekit-superforms\";\n\ttype T = Record<string, unknown>;\n\ttype U = FormPath<T>;\n</script>\n\n<script lang=\"ts\" generics=\"T extends Record<string, unknown>, U extends FormPath<T>\">\n\timport type { HTMLAttributes } from \"svelte/elements\";\n\timport * as FormPrimitive from \"formsnap\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = FormPrimitive.FieldProps<T, U> & HTMLAttributes<HTMLElement>;\n\n\texport let form: SuperForm<T>;\n\texport let name: U;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<FormPrimitive.Field {form} {name} let:constraints let:errors let:tainted let:value>\n\t<div class={cn(\"space-y-2\", className)}>\n\t\t<slot {constraints} {errors} {tainted} {value} />\n\t</div>\n</FormPrimitive.Field>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-fieldset.svelte",
    "content": "<script lang=\"ts\" context=\"module\">\n\timport type { FormPath, SuperForm } from \"sveltekit-superforms\";\n\ttype T = Record<string, unknown>;\n\ttype U = FormPath<T>;\n</script>\n\n<script lang=\"ts\" generics=\"T extends Record<string, unknown>, U extends FormPath<T>\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = FormPrimitive.FieldsetProps<T, U>;\n\n\texport let form: SuperForm<T>;\n\texport let name: U;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<FormPrimitive.Fieldset\n\t{form}\n\t{name}\n\tlet:constraints\n\tlet:errors\n\tlet:tainted\n\tlet:value\n\tclass={cn(\"space-y-2\", className)}\n>\n\t<slot {constraints} {errors} {tainted} {value} />\n</FormPrimitive.Fieldset>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-label.svelte",
    "content": "<script lang=\"ts\">\n\timport type { Label as LabelPrimitive } from \"bits-ui\";\n\timport { getFormControl } from \"formsnap\";\n\timport { Label } from \"$lib/components/ui/label/index.js\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = LabelPrimitive.Props;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n\n\tconst { labelAttrs } = getFormControl();\n</script>\n\n<Label {...$labelAttrs} class={cn(\"data-[fs-error]:text-destructive\", className)} {...$$restProps}>\n\t<slot {labelAttrs} />\n</Label>\n"
  },
  {
    "path": "src/lib/components/ui/form/form-legend.svelte",
    "content": "<script lang=\"ts\">\n\timport * as FormPrimitive from \"formsnap\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = FormPrimitive.LegendProps;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<FormPrimitive.Legend\n\t{...$$restProps}\n\tclass={cn(\"data-[fs-error]:text-destructive text-sm font-medium leading-none\", className)}\n\tlet:legendAttrs\n>\n\t<slot {legendAttrs} />\n</FormPrimitive.Legend>\n"
  },
  {
    "path": "src/lib/components/ui/form/index.ts",
    "content": "import * as FormPrimitive from \"formsnap\";\nimport Description from \"./form-description.svelte\";\nimport Label from \"./form-label.svelte\";\nimport FieldErrors from \"./form-field-errors.svelte\";\nimport Field from \"./form-field.svelte\";\nimport Button from \"./form-button.svelte\";\nimport Fieldset from \"./form-fieldset.svelte\";\nimport Legend from \"./form-legend.svelte\";\nimport ElementField from \"./form-element-field.svelte\";\n\nconst Control = FormPrimitive.Control;\n\nexport {\n\tField,\n\tControl,\n\tLabel,\n\tFieldErrors,\n\tDescription,\n\tFieldset,\n\tLegend,\n\tElementField,\n\tButton,\n\t//\n\tField as FormField,\n\tControl as FormControl,\n\tDescription as FormDescription,\n\tLabel as FormLabel,\n\tFieldErrors as FormFieldErrors,\n\tFieldset as FormFieldset,\n\tLegend as FormLegend,\n\tElementField as FormElementField,\n\tButton as FormButton,\n};\n"
  },
  {
    "path": "src/lib/components/ui/input/index.ts",
    "content": "import Root from \"./input.svelte\";\n\nexport type FormInputEvent<T extends Event = Event> = T & {\n\tcurrentTarget: EventTarget & HTMLInputElement;\n};\nexport type InputEvents = {\n\tblur: FormInputEvent<FocusEvent>;\n\tchange: FormInputEvent<Event>;\n\tclick: FormInputEvent<MouseEvent>;\n\tfocus: FormInputEvent<FocusEvent>;\n\tfocusin: FormInputEvent<FocusEvent>;\n\tfocusout: FormInputEvent<FocusEvent>;\n\tkeydown: FormInputEvent<KeyboardEvent>;\n\tkeypress: FormInputEvent<KeyboardEvent>;\n\tkeyup: FormInputEvent<KeyboardEvent>;\n\tmouseover: FormInputEvent<MouseEvent>;\n\tmouseenter: FormInputEvent<MouseEvent>;\n\tmouseleave: FormInputEvent<MouseEvent>;\n\tmousemove: FormInputEvent<MouseEvent>;\n\tpaste: FormInputEvent<ClipboardEvent>;\n\tinput: FormInputEvent<InputEvent>;\n\twheel: FormInputEvent<WheelEvent>;\n};\n\nexport {\n\tRoot,\n\t//\n\tRoot as Input,\n};\n"
  },
  {
    "path": "src/lib/components/ui/input/input.svelte",
    "content": "<script lang=\"ts\">\n\timport type { HTMLInputAttributes } from \"svelte/elements\";\n\timport type { InputEvents } from \"./index.js\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = HTMLInputAttributes;\n\ttype $$Events = InputEvents;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let value: $$Props[\"value\"] = undefined;\n\texport { className as class };\n\n\t// Workaround for https://github.com/sveltejs/svelte/issues/9305\n\t// Fixed in Svelte 5, but not backported to 4.x.\n\texport let readonly: $$Props[\"readonly\"] = undefined;\n</script>\n\n<input\n\tclass={cn(\n\t\t\"border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50\",\n\t\tclassName\n\t)}\n\tbind:value\n\t{readonly}\n\ton:blur\n\ton:change\n\ton:click\n\ton:focus\n\ton:focusin\n\ton:focusout\n\ton:keydown\n\ton:keypress\n\ton:keyup\n\ton:mouseover\n\ton:mouseenter\n\ton:mouseleave\n\ton:mousemove\n\ton:paste\n\ton:input\n\ton:wheel|passive\n\t{...$$restProps}\n/>\n"
  },
  {
    "path": "src/lib/components/ui/label/index.ts",
    "content": "import Root from \"./label.svelte\";\n\nexport {\n\tRoot,\n\t//\n\tRoot as Label,\n};\n"
  },
  {
    "path": "src/lib/components/ui/label/label.svelte",
    "content": "<script lang=\"ts\">\n\timport { Label as LabelPrimitive } from \"bits-ui\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = LabelPrimitive.Props;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n</script>\n\n<LabelPrimitive.Root\n\tclass={cn(\n\t\t\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n\t\tclassName\n\t)}\n\t{...$$restProps}\n>\n\t<slot />\n</LabelPrimitive.Root>\n"
  },
  {
    "path": "src/lib/components/ui/scroll-area/index.ts",
    "content": "import Scrollbar from \"./scroll-area-scrollbar.svelte\";\nimport Root from \"./scroll-area.svelte\";\n\nexport {\n\tRoot,\n\tScrollbar,\n\t//,\n\tRoot as ScrollArea,\n\tScrollbar as ScrollAreaScrollbar,\n};\n"
  },
  {
    "path": "src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte",
    "content": "<script lang=\"ts\">\n\timport { ScrollArea as ScrollAreaPrimitive } from \"bits-ui\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = ScrollAreaPrimitive.ScrollbarProps & {\n\t\torientation?: \"vertical\" | \"horizontal\";\n\t};\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let orientation: $$Props[\"orientation\"] = \"vertical\";\n\texport { className as class };\n</script>\n\n<ScrollAreaPrimitive.Scrollbar\n\t{orientation}\n\tclass={cn(\n\t\t\"flex touch-none select-none transition-colors\",\n\t\torientation === \"vertical\" && \"h-full w-2.5 border-l border-l-transparent p-px\",\n\t\torientation === \"horizontal\" && \"h-2.5 w-full border-t border-t-transparent p-px\",\n\t\tclassName\n\t)}\n>\n\t<slot />\n\t<ScrollAreaPrimitive.Thumb\n\t\tclass={cn(\"bg-border relative rounded-full\", orientation === \"vertical\" && \"flex-1\")}\n\t/>\n</ScrollAreaPrimitive.Scrollbar>\n"
  },
  {
    "path": "src/lib/components/ui/scroll-area/scroll-area.svelte",
    "content": "<script lang=\"ts\">\n\timport { ScrollArea as ScrollAreaPrimitive } from \"bits-ui\";\n\timport { Scrollbar } from \"./index.js\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = ScrollAreaPrimitive.Props & {\n\t\torientation?: \"vertical\" | \"horizontal\" | \"both\";\n\t\tscrollbarXClasses?: string;\n\t\tscrollbarYClasses?: string;\n\t};\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport { className as class };\n\texport let orientation = \"vertical\";\n\texport let scrollbarXClasses: string = \"\";\n\texport let scrollbarYClasses: string = \"\";\n</script>\n\n<ScrollAreaPrimitive.Root {...$$restProps} class={cn(\"relative overflow-hidden\", className)}>\n\t<ScrollAreaPrimitive.Viewport class=\"h-full w-full rounded-[inherit]\">\n\t\t<ScrollAreaPrimitive.Content>\n\t\t\t<slot />\n\t\t</ScrollAreaPrimitive.Content>\n\t</ScrollAreaPrimitive.Viewport>\n\t{#if orientation === \"vertical\" || orientation === \"both\"}\n\t\t<Scrollbar orientation=\"vertical\" class={scrollbarYClasses} />\n\t{/if}\n\t{#if orientation === \"horizontal\" || orientation === \"both\"}\n\t\t<Scrollbar orientation=\"horizontal\" class={scrollbarXClasses} />\n\t{/if}\n\t<ScrollAreaPrimitive.Corner />\n</ScrollAreaPrimitive.Root>\n"
  },
  {
    "path": "src/lib/components/ui/separator/index.ts",
    "content": "import Root from \"./separator.svelte\";\n\nexport {\n\tRoot,\n\t//\n\tRoot as Separator,\n};\n"
  },
  {
    "path": "src/lib/components/ui/separator/separator.svelte",
    "content": "<script lang=\"ts\">\n\timport { Separator as SeparatorPrimitive } from \"bits-ui\";\n\timport { cn } from \"$lib/utils.js\";\n\n\ttype $$Props = SeparatorPrimitive.Props;\n\n\tlet className: $$Props[\"class\"] = undefined;\n\texport let orientation: $$Props[\"orientation\"] = \"horizontal\";\n\texport let decorative: $$Props[\"decorative\"] = undefined;\n\texport { className as class };\n</script>\n\n<SeparatorPrimitive.Root\n\tclass={cn(\n\t\t\"bg-border shrink-0\",\n\t\torientation === \"horizontal\" ? \"h-[1px] w-full\" : \"min-h-full w-[1px]\",\n\t\tclassName\n\t)}\n\t{orientation}\n\t{decorative}\n\t{...$$restProps}\n/>\n"
  },
  {
    "path": "src/lib/highlightCode.ts",
    "content": "import { writable } from 'svelte/store';\nimport { mode } from 'mode-watcher';\n\nfunction createHighlightStore() {\n\tconst { subscribe, set } = writable('github');\n\n\treturn {\n\t\tsubscribe,\n\t\tsetTheme: (isDark: boolean) => {\n\t\t\tset(isDark ? 'github-dark' : 'github');\n\t\t}\n\t};\n}\n\nexport const highlightTheme = createHighlightStore();\n"
  },
  {
    "path": "src/lib/index.ts",
    "content": "// place files you want to import through the `$lib` alias in this folder.\n"
  },
  {
    "path": "src/lib/md.ts",
    "content": "// src/lib/md.ts\n\nimport yaml from 'js-yaml';\n\n/**\n * Function to add or update frontmatter in Markdown content.\n *\n * @param fileContent - The content of the Markdown file as a string.\n * @param url - The URL to be added to the frontmatter.\n * @returns The modified Markdown content with updated frontmatter.\n */\nexport function addFrontmatterToMarkdown(fileContent: string, url: string): string {\n  // Regular expression to detect existing YAML frontmatter\n  const frontmatterRegex = /^---\\n([\\s\\S]*?)\\n---\\n/;\n  const match = fileContent.match(frontmatterRegex);\n\n  let newContent: string;\n\n  if (match) {\n    // YAML frontmatter exists, parse the existing frontmatter\n    const existingFrontmatter = yaml.load(match[1]) as Record<string, any> || {};\n\n    // Add or update the 'url' field in the frontmatter\n    existingFrontmatter['mdpath'] = url;\n\n    // Convert the updated frontmatter back to YAML format\n    const updatedFrontmatter = yaml.dump(existingFrontmatter);\n\n    // Replace the old frontmatter with the updated one\n    newContent = fileContent.replace(frontmatterRegex, `---\\n${updatedFrontmatter}---\\n`);\n  } else {\n    // No frontmatter exists, create new frontmatter\n    const newFrontmatter = yaml.dump({ url });\n\n    // Prepend the new frontmatter to the file content\n    newContent = `---\\n${newFrontmatter}---\\n${fileContent}`;\n  }\n\n  // Return the updated content\n  return newContent;\n}\n"
  },
  {
    "path": "src/lib/pbStore.ts",
    "content": "import PocketBase from 'pocketbase';\nimport { writable } from 'svelte/store';\nimport { browser } from '$app/environment';\n\n// Client-side PocketBase instance\nexport const pb = new PocketBase('http://127.0.0.1:8090'); // Replace with your PocketBase URL\n\nexport const currentUser = writable(pb.authStore.model);\n\nif (browser) {\n\tpb.authStore.onChange((auth) => {\n\t\tconsole.log('Client AuthStore changed', auth);\n\t\tcurrentUser.set(pb.authStore.model);\n\t});\n}\n\nexport async function login(email: string, password: string) {\n\ttry {\n\t\tconst authData = await pb.admins.authWithPassword(email, password);\n\t\tconsole.log('Logged in successfully', authData);\n\t\treturn authData;\n\t} catch (error) {\n\t\tconsole.error('Login failed', error);\n\t\tthrow error;\n\t}\n}\n\nexport function logout() {\n\tpb.authStore.clear();\n}\n"
  },
  {
    "path": "src/lib/pocketbase.ts",
    "content": "import PocketBase from 'pocketbase';\nimport {\n\tPOCKETBASE_URL,\n\tPOCKETBASE_ADMIN_EMAIL,\n\tPOCKETBASE_ADMIN_PASSWORD\n} from '$env/static/private';\n\nlet serverPb: PocketBase | null = null;\n\nexport async function getAuthenticatedPocketBase() {\n\tif (!serverPb) {\n\t\tserverPb = new PocketBase(POCKETBASE_URL);\n\t\tserverPb.autoCancellation(false);\n\t}\n\n\t// Check if already authenticated and try refreshing the token\n\tif (serverPb.authStore.isValid) {\n\t\ttry {\n\t\t\tawait serverPb.collection('users').authRefresh();\n\t\t\tconsole.log('Using existing server authentication');\n\t\t\treturn serverPb;\n\t\t} catch (error) {\n\t\t\tconsole.log('Server token refresh failed, re-authenticating');\n\t\t}\n\t}\n\n\t// If not authenticated or refresh failed, login as admin\n\ttry {\n\t\tawait serverPb.admins.authWithPassword(POCKETBASE_ADMIN_EMAIL, POCKETBASE_ADMIN_PASSWORD);\n\t\tconsole.log('New server authentication successful');\n\t\treturn serverPb;\n\t} catch (error) {\n\t\tconsole.error('Server authentication failed:', error);\n\t\tthrow error;\n\t}\n}\n"
  },
  {
    "path": "src/lib/remark-plugins/footNotes.js",
    "content": "// src/lib/plugins/remark-footnote-html.js\nimport { visit } from 'unist-util-visit';\n\n\nfunction remarkFootnoteHTML() {\n  return (tree) => {\n    const footnotes = [];\n    const footnoteMap = {};\n\n    // Collect footnote definitions\n    visit(tree, 'footnoteDefinition', (node) => {\n      const identifier = node.identifier;\n      const content = node.children;\n      footnoteMap[identifier] = content;\n      console.log(node)\n    });\n\n    // Replace footnote references with custom HTML\n    visit(tree, 'footnoteReference', (node, index, parent) => {\n      const identifier = node.identifier;\n      const footnoteNumber = Object.keys(footnoteMap).indexOf(identifier) + 1;\n\n      if (footnoteNumber === 0) return;\n\n      const sup = {\n        type: 'html',\n        value: `<sup id=\"fnref${footnoteNumber}\"><a href=\"#fn${footnoteNumber}\" aria-describedby=\"footnote\">${footnoteNumber}</a></sup>`,\n      };\n\n      parent.children.splice(index, 1, sup);\n    });\n\n    // Remove footnote definitions from the tree\n    tree.children = tree.children.filter((node) => node.type !== 'footnoteDefinition');\n\n    // Append the footnotes section at the end\n    const footnotesSection = {\n      type: 'html',\n      value: '<section class=\"footnotes\"><ol>',\n    };\n\n    tree.children.push(footnotesSection);\n\n    Object.keys(footnoteMap).forEach((identifier, idx) => {\n      const footnoteNumber = idx + 1;\n      const content = footnoteMap[identifier]\n        .map((child) => {\n          if (child.type === 'text') {\n            return child.value;\n          } else {\n            // Handle other node types as needed\n            return '';\n          }\n        })\n        .join('');\n\n      const footnoteItem = {\n        type: 'html',\n        value: `<li id=\"fn${footnoteNumber}\">${content} <a href=\"#fnref${footnoteNumber}\" aria-label=\"Back to content\">↩︎</a></li>`,\n      };\n\n      tree.children.push(footnoteItem);\n    });\n\n    // Close the ordered list and section\n    tree.children.push({\n      type: 'html',\n      value: '</ol></section>',\n    });\n  };\n}\n\nexport default remarkFootnoteHTML;\n"
  },
  {
    "path": "src/lib/remark-plugins/highlightSyn.js",
    "content": "import { visit } from 'unist-util-visit'\n\nfunction remarkHighlight() {\n  return (tree) => {\n    visit(tree, 'text', (node, index, parent) => {\n      const matches = node.value.match(/==(.*?)==/g)\n      if (!matches) return\n\n      const children = []\n      let lastIndex = 0\n\n      matches.forEach((match) => {\n        const startIndex = node.value.indexOf(match, lastIndex)\n        const endIndex = startIndex + match.length\n\n        // Add text before the highlight\n        if (startIndex > lastIndex) {\n          children.push({\n            type: 'text',\n            value: node.value.slice(lastIndex, startIndex)\n          })\n        }\n\n        // Add the highlighted text with a span and class\n        children.push({\n          type: 'span',\n          data: {\n          \thName: 'span',\n          \thProperties: {\n          \t\tclassName: ['highlight']\n          \t}\n          },\n          children: [\n            {\n              type: 'text',\n              value: match.slice(2, -2) // Remove '==' from the start and end\n            }\n          ]\n        })\n\n        lastIndex = endIndex\n      })\n\n      // Add any remaining text after the last highlight\n      if (lastIndex < node.value.length) {\n        children.push({\n          type: 'text',\n          value: node.value.slice(lastIndex)\n        })\n      }\n\n      // Replace the original node with the new children\n      parent.children.splice(index, 1, ...children)\n    })\n  }\n}\n\nexport default remarkHighlight\n"
  },
  {
    "path": "src/lib/remark-plugins/imgRel.js",
    "content": "import { visit } from 'unist-util-visit';\nimport path from 'path';\n\nexport default function remarkLogImages() {\n\treturn function transformer(tree, file) {\n\t\tif (!file || !file.data || !file.data.fm || !file.data.fm.mdpath) {\n\t\t\tthrow new Error('File metadata with url is missing.');\n\t\t}\n\n\n\t\tconst url = file.data.fm.mdpath; // e.g., '/writing/f2/test'\n\n\t\tvisit(tree, 'image', (node) => {\n\t\t\t// Extract the link part before any pipe (e.g., [[link|alias]])\n\t\t\tconst rawLink = node.url.trim(); // e.g., '../f1/test'\n\n\t\t\tconsole.log(node)\n\n\t\t\tif (!rawLink.includes('/api/img') && !rawLink.includes('://')) {\n\t\t\t\tconst folder = path.dirname(url.split('.')[0]);\n\t\t\t\tconst absPath = path.join(folder, rawLink); // e.g., 'mdpath/f1/test'\n\t\t\t\tconsole.log(\"============>\", rawLink, absPath)\n\t\t\t\tnode.url = `/api/img/${absPath}`;\n\t\t\t}\n\n\n\t\t});\n\n\t}\n}\n"
  },
  {
    "path": "src/lib/remark-plugins/mermaidDiag.js",
    "content": "import { visit } from 'unist-util-visit';\n\n// Create the plugin\nconst remarkMermaid = () => {\n  return (tree) => {\n    visit(tree, 'code', (node) => {\n      if (node.lang === 'mermaid') {\n        // Replace the code block with a custom HTML structure\n        node.type = 'html';\n        node.value = `<div class=\"mermaid\">${node.value}</div>`;\n      }\n    });\n  };\n};\n\nexport default remarkMermaid;\n"
  },
  {
    "path": "src/lib/remark-plugins/obsidianImage.js",
    "content": "import { visit } from 'unist-util-visit';\n\nfunction obsidianImagePlugin() {\n  return (tree) => {\n    visit(tree, 'paragraph', (node) => {\n      const newChildren = [];\n      let i = 0;\n\n      while (i < node.children.length) {\n        const currentNode = node.children[i];\n\n        // Check if the current node is a 'text' node with a '!'\n        if (currentNode.type === 'text' && currentNode.value === '!') {\n          // Check if the next node is a 'wikiLink' node with an image file extension\n          const nextNode = node.children[i + 1];\n          if (\n            nextNode &&\n            nextNode.type === 'wikiLink' &&\n            /\\.(png|jpe?g|gif|svg|webp)$/.test(nextNode.value)\n          ) {\n            // Replace the '!' and 'wikiLink' with an 'image' node\n            let newUrl = '/api/img/' + nextNode.value;\n            newChildren.push({\n              type: 'image',\n              url: newUrl,\n              alt: nextNode.value.split('/').pop() // Use the filename as the alt text\n            });\n            i += 2; // Skip both the 'text' and 'wikiLink' nodes\n            continue;\n          }\n        }\n\n        // If no match, just push the current node as-is\n        newChildren.push(currentNode);\n        i++;\n      }\n\n      // Replace the old children with the new set of children\n      node.children = newChildren;\n    });\n  };\n}\n\nexport default obsidianImagePlugin;\n"
  },
  {
    "path": "src/lib/remark-plugins/remarkTags.ts",
    "content": "import { visit } from 'unist-util-visit';\n\nfunction remarkTags() {\n\treturn (tree) => {\n\t\tvisit(tree, 'text', (node, index, parent) => {\n\t\t\tconst matches = node.value.match(/#[a-zA-Z0-9_-]+/g);\n\t\t\tif (!matches) return;\n\n\t\t\tconst children = [];\n\t\t\tlet lastIndex = 0;\n\n\t\t\tmatches.forEach((match) => {\n\t\t\t\tconst startIndex = node.value.indexOf(match, lastIndex);\n\t\t\t\tconst endIndex = startIndex + match.length;\n\n\t\t\t\t// Add text before the tag\n\t\t\t\tif (startIndex > lastIndex) {\n\t\t\t\t\tchildren.push({\n\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\tvalue: node.value.slice(lastIndex, startIndex)\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Add the tag with a span and class, including an anchor tag\n\t\t\t\tconst tagName = match.slice(1); // Remove '#' from the start\n\t\t\t\tchildren.push({\n\t\t\t\t\ttype: 'span',\n\t\t\t\t\tdata: {\n\t\t\t\t\t\thName: 'span',\n\t\t\t\t\t\thProperties: {\n\t\t\t\t\t\t\tclassName: ['tag']\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tchildren: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: 'element',\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\thName: 'a',\n\t\t\t\t\t\t\t\thProperties: {\n\t\t\t\t\t\t\t\t\thref: `/tags/${tagName}`,\n\t\t\t\t\t\t\t\t\tclassName: ['tag-link']\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tchildren: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: 'text',\n\t\t\t\t\t\t\t\t\tvalue: tagName\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t});\n\n\t\t\t\tlastIndex = endIndex;\n\t\t\t});\n\n\t\t\t// Add any remaining text after the last tag\n\t\t\tif (lastIndex < node.value.length) {\n\t\t\t\tchildren.push({\n\t\t\t\t\ttype: 'text',\n\t\t\t\t\tvalue: node.value.slice(lastIndex)\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Replace the original node with the new children\n\t\t\tparent.children.splice(index, 1, ...children);\n\t\t});\n\t};\n}\n\nexport default remarkTags;\n"
  },
  {
    "path": "src/lib/server/auth.ts",
    "content": "import PocketBase from 'pocketbase';\nimport {\n\tPOCKETBASE_URL,\n\tPOCKETBASE_ADMIN_EMAIL,\n\tPOCKETBASE_ADMIN_PASSWORD\n} from '$env/static/private';\n\nlet pocketBaseInstance: PocketBase | null = null;\n\nexport async function getAuthenticatedPocketBase() {\n\tif (!pocketBaseInstance) {\n\t\tpocketBaseInstance = new PocketBase(POCKETBASE_URL);\n\t\tpocketBaseInstance.autoCancellation(false); // Prevent cancellation of overlapping requests\n\t}\n\n\t// Check if the current authentication is valid\n\tif (pocketBaseInstance.authStore.isValid) {\n\t\ttry {\n\t\t\tconsole.log('login valid');\n\n\t\t\t// Check if logged in as a user (not admin) and refresh token\n\t\t\tif (pocketBaseInstance.authStore.model?.email !== POCKETBASE_ADMIN_EMAIL) {\n\t\t\t\t// Only refresh tokens for non-admin users\n\t\t\t\tawait pocketBaseInstance.collection('users').authRefresh();\n\t\t\t\tconsole.log('Token refreshed successfully');\n\t\t\t}\n\n\t\t\treturn pocketBaseInstance;\n\t\t} catch (error) {\n\t\t\tconsole.error('Token refresh failed:', error);\n\t\t}\n\t}\n\n\t// Login as admin if token is invalid or refresh failed\n\ttry {\n\t\tconsole.log('Attempting to log in as admin...');\n\t\tawait pocketBaseInstance.admins.authWithPassword(\n\t\t\tPOCKETBASE_ADMIN_EMAIL,\n\t\t\tPOCKETBASE_ADMIN_PASSWORD\n\t\t);\n\t\tconsole.log('New admin authentication successful');\n\t\treturn pocketBaseInstance;\n\t} catch (error) {\n\t\t// Log any error encountered during login\n\t\tconsole.error('Admin login failed:', error);\n\t\tthrow error;\n\t}\n}\n"
  },
  {
    "path": "src/lib/stores/sidebarStore.ts",
    "content": "import { writable } from 'svelte/store';\n\nexport const isSidebarVisible = writable(true);\n"
  },
  {
    "path": "src/lib/utils.ts",
    "content": "import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport { cubicOut } from \"svelte/easing\";\nimport type { TransitionConfig } from \"svelte/transition\";\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\ntype FlyAndScaleParams = {\n\ty?: number;\n\tx?: number;\n\tstart?: number;\n\tduration?: number;\n};\n\nexport const flyAndScale = (\n\tnode: Element,\n\tparams: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }\n): TransitionConfig => {\n\tconst style = getComputedStyle(node);\n\tconst transform = style.transform === \"none\" ? \"\" : style.transform;\n\n\tconst scaleConversion = (\n\t\tvalueA: number,\n\t\tscaleA: [number, number],\n\t\tscaleB: [number, number]\n\t) => {\n\t\tconst [minA, maxA] = scaleA;\n\t\tconst [minB, maxB] = scaleB;\n\n\t\tconst percentage = (valueA - minA) / (maxA - minA);\n\t\tconst valueB = percentage * (maxB - minB) + minB;\n\n\t\treturn valueB;\n\t};\n\n\tconst styleToString = (\n\t\tstyle: Record<string, number | string | undefined>\n\t): string => {\n\t\treturn Object.keys(style).reduce((str, key) => {\n\t\t\tif (style[key] === undefined) return str;\n\t\t\treturn str + `${key}:${style[key]};`;\n\t\t}, \"\");\n\t};\n\n\treturn {\n\t\tduration: params.duration ?? 200,\n\t\tdelay: 0,\n\t\tcss: (t) => {\n\t\t\tconst y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);\n\t\t\tconst x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);\n\t\t\tconst scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);\n\n\t\t\treturn styleToString({\n\t\t\t\ttransform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,\n\t\t\t\topacity: t\n\t\t\t});\n\t\t},\n\t\teasing: cubicOut\n\t};\n};\n"
  },
  {
    "path": "src/routes/+layout.server.ts",
    "content": "import { json } from \"@sveltejs/kit\";\nimport type { RequestHandler } from \"./$types\";\nimport { superValidate } from \"sveltekit-superforms\";\nimport { formSchema } from \"$lib/components/schema\";\nimport { zod } from \"sveltekit-superforms/adapters\";\nimport {\n  TITLE,\n  POCKETBASE_ADMIN_EMAIL,\n  POCKETBASE_ADMIN_PASSWORD,\n} from \"$env/static/private\";\nimport { CAP1, CAP2, CAP3 } from \"$env/static/private\";\n\nexport async function load({ fetch, params }) {\n  const ftree = await fetch(\"/api/ls\");\n  const tagresp = await fetch(\"/api/tags\");\n  const tags = await tagresp.json();\n  const filetree = await ftree.json();\n  const siteTitle = TITLE;\n  const captions = [CAP1, CAP2, CAP3];\n\n  console.log(\n    \"logged in with: \",\n    POCKETBASE_ADMIN_EMAIL,\n    POCKETBASE_ADMIN_PASSWORD,\n    CAP1,\n    CAP2,\n  );\n\n  return { filetree, siteTitle, tags, captions };\n}\n"
  },
  {
    "path": "src/routes/+layout.svelte",
    "content": "<script lang=\"ts\">\n\timport '../app.css';\n\timport { ModeWatcher } from 'mode-watcher';\n\timport { Separator } from '$lib/components/ui/separator';\n\timport Sidebar from '$lib/components/Sidebar.svelte';\n\timport FileTree from '$lib/components/FileTree.svelte';\n\timport { onMount } from 'svelte';\n\timport { Button } from '$lib/components/ui/button';\n\timport { Menu, Grip, SquareX } from 'lucide-svelte';\n\timport { beforeNavigate } from '$app/navigation';\n\timport SearchComponent from '$lib/components/SearchComponent.svelte';\n\timport TagBar from '$lib/components/TagBar.svelte';\n\n\texport let data;\n\tlet showSidebar = false;\n\tlet sidebarRef;\n\tlet toggleButtonRef;\n\t$: fileTree = data?.filetree;\n\t$: siteTitle = data?.siteTitle;\n\t$: tags = data?.tags;\n\n\t// Toggle sidebar visibility\n\tfunction toggleSidebar() {\n\t\tshowSidebar = !showSidebar;\n\t}\n\n\t// Close sidebar when clicking outside\n\tfunction handleClickOutside(event) {\n\t\tconsole.log('clicked');\n\t\tif (\n\t\t\tsidebarRef &&\n\t\t\t!sidebarRef.contains(event.target) &&\n\t\t\ttoggleButtonRef &&\n\t\t\t!toggleButtonRef.contains(event.target)\n\t\t) {\n\t\t\tshowSidebar = false;\n\t\t}\n\t}\n\n\tonMount(() => {\n\t\tdocument.addEventListener('click', handleClickOutside);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('click', handleClickOutside);\n\t\t};\n\t});\n\n\tbeforeNavigate(() => {\n\t\tshowSidebar = false;\n\t});\n</script>\n\n<!-- Toggle button to control sidebar visibility -->\n<div\n\tbind:this={toggleButtonRef}\n\tclass=\"fixed left-0 top-0 flex w-[100%] flex-col items-start justify-center gap-2 bg-background lg:hidden\"\n>\n\t<div class=\"flex w-[100%] items-center justify-between\">\n\t\t<div class=\"flex items-center justify-start\">\n\t\t\t<Button class=\"z-20\" on:click={toggleSidebar} variant=\"ghost\" size=\"icon\">\n\t\t\t\t<Grip />\n\t\t\t</Button>\n\t\t\t<div class=\"title text-2xl break-words\">{siteTitle}</div>\n\t\t</div>\n\t\t<div class=\"gap-0.1 flex\">\n\t\t\t<TagBar {tags} />\n\t\t\t<SearchComponent />\n\t\t</div>\n\t</div>\n</div>\n\n<div class=\"fixed right-0 top-0 hidden w-[100%] items-center justify-end bg-background lg:flex\">\n\t<TagBar {tags} />\n\t<SearchComponent />\n</div>\n<!-- Sidebar -->\n<div\n\tbind:this={sidebarRef}\n\tclass={`fixed left-0 top-0 z-30 flex h-svh w-72 justify-between bg-background transition-transform lg:translate-x-0 ${showSidebar ? '' : '-translate-x-full'}`}\n>\n\t<Sidebar title={siteTitle} captions={data.captions}>\n\t\t{#if fileTree}\n\t\t\t<FileTree bind:fileTree />\n\t\t{:else}\n\t\t\tLoading...\n\t\t{/if}\n\t</Sidebar>\n\t<Separator orientation=\"vertical\" class=\"h-full\"></Separator>\n</div>\n\n<!-- Main content -->\n<div class=\"z-10 w-[100%] p-2 pt-12 lg:pl-80 lg:pt-6\">\n\t<ModeWatcher />\n\t<slot></slot>\n</div>\n\n<style>\n\t/* Ensure smooth transition when showing/hiding the sidebar */\n\t.transition-transform {\n\t\ttransition: transform 0.2s ease-in-out;\n\t}\n\t.translate-x-full {\n\t\ttransform: translateX(-100%);\n\t}\n\t.title {\n\t\tfont-family: Megrim;\n\t}\n</style>\n"
  },
  {
    "path": "src/routes/+page.server.ts",
    "content": "import PocketBase from \"pocketbase\";\nimport { getAuthenticatedPocketBase } from \"$lib/server/auth\";\n\nconst pb = await getAuthenticatedPocketBase();\n\nexport async function load({ params }) {\n  try {\n    const mdbase = await pb.collections.getOne(\"mdbase\");\n\n    const records = await pb.collection(\"mdbase\").getList(1, 1, {\n      filter: \"frontmatter.home = true\",\n      sort: \"-created\",\n    });\n\n    let post = null;\n    if (records.items.length > 0) {\n      post = records.items[0];\n    }\n\n    if (post) {\n      const backlinks = await getBacklinks(`${post.frontmatter.mdpath}`);\n\n      const tags = post.expand?.tags.map((tag) => {\n        return {\n          name: tag.tag,\n        };\n      });\n      return { post, title: post.title, backlinks, tags };\n    } else {\n      return { post: null, title: \"\", backlinks: [], tags: [] };\n    }\n  } catch (error) {\n    console.error(`Failed to fetch post: ${error}`);\n    return { message: `Failed to fetch post: ${error}` };\n  }\n}\n\nasync function getBacklinks(url) {\n  const mdbaseCollection = pb.collection(\"mdbase\");\n  const documentUrl = url;\n  try {\n    if (!documentUrl) {\n      return new Response(\n        JSON.stringify({ message: \"URL parameter is required\" }),\n        {\n          status: 400,\n        },\n      );\n    }\n\n    const documents = await mdbaseCollection.getList(1, 1, {\n      filter: `url=\"${documentUrl}\"`,\n      expand: \"backlinks\",\n    });\n\n    if (documents.items.length === 0) {\n      return new Response(JSON.stringify({ message: \"Document not found\" }), {\n        status: 404,\n      });\n    }\n\n    const document = documents.items[0];\n\n    const backLinks = (document.expand?.backlinks || []).map((link) => ({\n      id: link.id,\n      title: link.title,\n      url: link.url,\n    }));\n\n    return backLinks;\n  } catch (error: any) {\n    console.error(\"Error in backlinks API:\", error);\n    return {};\n  }\n}\n"
  },
  {
    "path": "src/routes/+page.svelte",
    "content": "<script lang=\"ts\">\n\timport MDsvexRenderer from '$lib/components/MDsvexRenderer.svelte';\n\timport MDGraph from '$lib/components/MDGraph.svelte';\n\timport { CalendarDays } from 'lucide-svelte';\n\n\texport let data: { content: string };\n\t$: content = data.post?.content;\n\t$: tags = data?.tags;\n\t$: date = data.post?.created;\n</script>\n\n{#if data.post}\n<div class=\"mb-10 mt-6 text-wrap md:w-[700px]\">\n\t<div class=\"my-4 text-6xl md:text-8xl\">\n\t\t{data.title}\n\t</div>\n\n\t<div class=\"flex flex-col justify-center gap-1\">\n\t\t{#if data?.post?.frontmatter?.date}\n\t\t<div class=\"flex items-center gap-1\">\n\t\t\t<CalendarDays class=\"text-carbongray-400\" size={15} />\n\t\t\t<div class=\"text-base text-carbongray-400\">{data.post.frontmatter.date.split(' ')[0]}</div>\n\t\t</div>\n\t\t{/if}\n\t\t{#if tags}\n\t\t\t<div class=\"flex flex-wrap gap-1\">\n\t\t\t\t{#each tags as tag (tag.name)}\n\t\t\t\t\t<span class=\"mx-0.5 rounded-sm bg-[#fedc69] p-0.5 text-carbongray-900\"\n\t\t\t\t\t\t><a class=\"text-carbongray-900 dark:text-carbongray-900\" href=\"/tags/{tag.name}\"\n\t\t\t\t\t\t\t>{tag.name}</a\n\t\t\t\t\t\t></span\n\t\t\t\t\t>\n\t\t\t\t{/each}\n\t\t\t</div>\n\t\t{/if}\n\t</div>\n\t<hr class=\"mt-6\" />\n</div>\n<MDsvexRenderer bind:content />\n<div>\n\t{#if data?.backlinks?.length > 0}\n\t\t<hr class=\"my-4 w-[700px]\" />\n\t\t<div class=\"mb-2 mt-4 text-2xl font-light\">BACKLINKS</div>\n\t{/if}\n\t{#each data?.backlinks || [] as bl (bl.id)}\n\t\t<div><a href={`/${bl.url.trim()}`} class=\"text-large\">{bl.title}</a></div>\n\t{/each}\n</div>\n{:else}\n<div class=\"mb-10 mt-6 text-wrap md:w-[700px]\">\nSet homepage in the frontmatter of one of your markdown files to display it as home\n</div>\n{/if}\n\n<style>\n\t:global(.tag a) {\n\t\t@apply mx-0.5 bg-[#fedc69] p-1 text-carbongray-900;\n\t}\n</style>\n"
  },
  {
    "path": "src/routes/[...post].md/+page.server.ts",
    "content": "import { json } from \"@sveltejs/kit\";\nimport type { RequestHandler } from \"./$types\";\nimport PocketBase from \"pocketbase\";\nimport { promises as fs } from \"fs\";\nimport { getAuthenticatedPocketBase } from \"$lib/server/auth\";\n\nconst pb = await getAuthenticatedPocketBase();\n\nasync function getBacklinks(url) {\n  const mdbaseCollection = pb.collection(\"mdbase\");\n  const documentUrl = url;\n  try {\n    if (!documentUrl) {\n      return new Response(\n        JSON.stringify({ message: \"URL parameter is required\" }),\n        {\n          status: 400,\n        },\n      );\n    }\n\n    const documents = await mdbaseCollection.getList(1, 1, {\n      filter: `url=\"${documentUrl}\"`,\n      expand: \"backlinks\",\n    });\n\n    if (documents.items.length === 0) {\n      return new Response(JSON.stringify({ message: \"Document not found\" }), {\n        status: 404,\n      });\n    }\n\n    const document = documents.items[0];\n\n    const backLinks = (document.expand?.backlinks || []).map((link) => ({\n      id: link.id,\n      title: link.title,\n      url: link.url,\n    }));\n\n    return backLinks;\n  } catch (error: any) {\n    console.error(\"Error in backlinks API:\", error);\n    return {};\n  }\n}\n\nasync function computeGraphData(fileUrl) {\n  const currentPage = await pb\n    .collection(\"mdbase\")\n    .getFirstListItem(`url=\"${fileUrl}\"`);\n  const relatedPages = await pb.collection(\"mdbase\").getList(1, 50, {\n    filter: `id ?~ \"${currentPage.backlinks}\" || id ?~ \"${currentPage.links}\"`,\n  });\n\n  // Use a Set to store unique node IDs\n  const uniqueNodeIds = new Set([currentPage.id]);\n\n  // Create nodes array with current page\n  const nodes = [\n    { id: currentPage.id, label: currentPage.title, color: \"#ff0000\" },\n  ];\n\n  // Add related pages to nodes array, avoiding duplicates\n  relatedPages.items.forEach((p) => {\n    if (!uniqueNodeIds.has(p.id)) {\n      uniqueNodeIds.add(p.id);\n      nodes.push({ id: p.id, label: p.title, color: \"#00ff00\" });\n    }\n  });\n\n  // Create edges array\n  const edges = [\n    ...currentPage.links.map((link) => ({ from: currentPage.id, to: link })),\n    ...currentPage.backlinks.map((backlink) => ({\n      from: backlink,\n      to: currentPage.id,\n    })),\n  ];\n\n  return { nodes, edges };\n}\n// Main load function\nexport async function load({ params, fetch, locals }) {\n  try {\n    // Step 1: Authenticate\n    /* console.log(pb); */\n    console.log(params.post);\n    const post = await pb\n      .collection(\"mdbase\")\n      .getFirstListItem(`url=\"${params.post}.md\"`, { expand: \"tags\" });\n    const backlinks = await getBacklinks(`${params.post}.md`);\n    // const graphData = await computeGraphData(`${params.post}.md`);\n\n    const tags = post.expand?.tags.map((tag) => {\n      return {\n        name: tag.tag,\n      };\n    });\n    console.log(tags);\n\n    return { post, title: post.title, backlinks, tags };\n  } catch (error) {\n    console.error(`Failed to fetch post: ${error}`);\n    return { message: `Failed to fetch post: ${error}` };\n  }\n}\n"
  },
  {
    "path": "src/routes/[...post].md/+page.svelte",
    "content": "<script lang=\"ts\">\n\timport MDsvexRenderer from '$lib/components/MDsvexRenderer.svelte';\n\timport MDGraph from '$lib/components/MDGraph.svelte';\n\timport { CalendarDays } from 'lucide-svelte';\n\n\texport let data: { content: string };\n\t$: content = data.post?.content;\n\t$: tags = data?.tags;\n\t$: date = data.post?.created;\n</script>\n\n<div class=\"mb-10 mt-6 text-wrap md:w-[700px]\">\n\t<div class=\"my-4 text-6xl md:text-8xl\">\n\t\t{data.title}\n\t</div>\n\n\t<div class=\"flex flex-col justify-center gap-1\">\n\t\t{#if data?.post?.frontmatter?.date}\n\t\t\t<div class=\"flex items-center gap-1\">\n\t\t\t\t<CalendarDays class=\"text-carbongray-400\" size={15} />\n\t\t\t\t<div class=\"text-base text-carbongray-400\">{data.post.frontmatter.date.split(' ')[0]}</div>\n\t\t\t</div>\n\t\t{/if}\n\t\t{#if tags}\n\t\t\t<div class=\"flex flex-wrap gap-1\">\n\t\t\t\t{#each tags as tag (tag.name)}\n\t\t\t\t\t<span class=\"mx-0.5 rounded-sm bg-[#fedc69] p-0.5 text-carbongray-900\"\n\t\t\t\t\t\t><a class=\"text-carbongray-900 dark:text-carbongray-900\" href=\"/tags/{tag.name}\"\n\t\t\t\t\t\t\t>{tag.name}</a\n\t\t\t\t\t\t></span\n\t\t\t\t\t>\n\t\t\t\t{/each}\n\t\t\t</div>\n\t\t{/if}\n\t</div>\n\t<hr class=\"mt-6\" />\n</div>\n<MDsvexRenderer bind:content />\n<div>\n\t{#if data?.backlinks?.length > 0}\n\t\t<hr class=\"my-4 w-[700px]\" />\n\t\t<div class=\"mb-2 mt-4 text-2xl font-light\">BACKLINKS</div>\n\t{/if}\n\t{#each data?.backlinks || [] as bl (bl.id)}\n\t\t<div><a href={`/${bl.url.trim()}`} class=\"text-large\">{bl.title}</a></div>\n\t{/each}\n</div>\n\n<style>\n\t:global(.tag a) {\n\t\t@apply mx-0.5 bg-[#fedc69] p-1 text-carbongray-900;\n\t}\n</style>\n"
  },
  {
    "path": "src/routes/about/+page.svelte",
    "content": ""
  },
  {
    "path": "src/routes/api/backlinks/+server.ts",
    "content": "import type { RequestHandler } from '@sveltejs/kit';\nimport PocketBase from 'pocketbase';\nimport {\n\tPOCKETBASE_ADMIN_PASSWORD,\n\tPOCKETBASE_ADMIN_EMAIL,\n\tPOCKETBASE_URL,\n\tAPI_KEY\n} from '$env/static/private';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nconst pb = await getAuthenticatedPocketBase();\n\nexport const GET: RequestHandler = async ({ url, request }) => {\n\ttry {\n\t\tconst mdbaseCollection = pb.collection('mdbase');\n\t\tconst documentUrl = url.searchParams.get('url');\n\n\t\tif (!documentUrl) {\n\t\t\treturn new Response(JSON.stringify({ message: 'URL parameter is required' }), {\n\t\t\t\tstatus: 400\n\t\t\t});\n\t\t}\n\n\t\tconst documents = await mdbaseCollection.getList(1, 1, {\n\t\t\tfilter: `url=\"${documentUrl}\"`,\n\t\t\texpand: 'backlinks'\n\t\t});\n\n\t\tif (documents.items.length === 0) {\n\t\t\treturn new Response(JSON.stringify({ message: 'Document not found' }), { status: 404 });\n\t\t}\n\n\t\tconst document = documents.items[0];\n\n\t\tconst backLinks = (document.expand?.backlinks || []).map((link) => ({\n\t\t\tid: link.id,\n\t\t\ttitle: link.title,\n\t\t\turl: link.url\n\t\t}));\n\n\t\treturn new Response(JSON.stringify({ backLinks }), {\n\t\t\tstatus: 200,\n\t\t\theaders: { 'Content-Type': 'application/json' }\n\t\t});\n\t} catch (error: any) {\n\t\tconsole.error('Error in backlinks API:', error);\n\t\treturn new Response(\n\t\t\tJSON.stringify({\n\t\t\t\tmessage: 'Failed to retrieve backlinks',\n\t\t\t\terror: error.message || 'Unknown error',\n\t\t\t\tdetails: error.data ? JSON.stringify(error.data) : 'No additional details'\n\t\t\t}),\n\t\t\t{ status: 500 }\n\t\t);\n\t}\n};\n\nexport const OPTIONS: RequestHandler = async () => {\n\treturn new Response(null, {\n\t\tstatus: 204,\n\t\theaders: {\n\t\t\t'Access-Control-Allow-Origin': '*',\n\t\t\t'Access-Control-Allow-Methods': 'GET, OPTIONS',\n\t\t\t'Access-Control-Allow-Headers': 'Content-Type, X-API-Key'\n\t\t}\n\t});\n};\n"
  },
  {
    "path": "src/routes/api/graph/+server.ts",
    "content": "import { json } from '@sveltejs/kit';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\nimport type { RequestHandler } from './$types';\n\nexport const GET: RequestHandler = async ({ url, request }) => {\n\tconst fileUrl = url.searchParams.get('url');\n\tconsole.log(fileUrl);\n\n\tif (!fileUrl) {\n\t\treturn json({ error: 'Missing URL parameter' }, { status: 400 });\n\t}\n\n\ttry {\n\t\tconst pb = await getAuthenticatedPocketBase();\n\t\tconst currentPage = await pb.collection('mdbase').getFirstListItem(`url=\"${fileUrl}\"`);\n\t\tconst graphData = await computeGraphData(currentPage);\n\t\treturn json(graphData);\n\t} catch (error) {\n\t\tconsole.error('Error computing graph:', error);\n\t\treturn json({ error: error }, { status: 500 });\n\t}\n};\n\nasync function computeGraphData(currentPage) {\n\tconst pb = await getAuthenticatedPocketBase();\n\tconst relatedPages = await pb.collection('mdbase').getList(1, 50, {\n\t\tfilter: `id ?~ \"${currentPage.backlinks}\" || id ?~ \"${currentPage.links}\"`\n\t});\n\n\tconst nodes = [\n\t\t{ id: currentPage.id, label: currentPage.title, color: '#ff0000' }, // Current page (red)\n\t\t...relatedPages.items.map((p) => ({ id: p.id, label: p.title, color: '#00ff00' })) // Related pages (green)\n\t];\n\n\tconst edges = [\n\t\t...currentPage.links.map((link) => ({ from: currentPage.id, to: link })),\n\t\t...currentPage.backlinks.map((backlink) => ({ from: backlink, to: currentPage.id }))\n\t];\n\n\treturn { nodes, edges };\n}\n"
  },
  {
    "path": "src/routes/api/hello/+server.ts",
    "content": "// src/routes/api/hello/+server.ts\nimport type { RequestHandler } from './$types';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nexport const GET: RequestHandler = async ({ request }) => {\n\tconst pb = await getAuthenticatedPocketBase();\n\n\t// Ensure the server is authenticated\n\tif (!pb.authStore.isValid) {\n\t\treturn new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 });\n\t}\n\n\t// Example of accessing PocketBase data\n\tconst users = await pb.collection('users').getFullList();\n\n\treturn new Response(JSON.stringify({ data: users }), { status: 200 });\n};\n"
  },
  {
    "path": "src/routes/api/img/[...path]/+server.ts",
    "content": "import { error } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\nimport PocketBase from 'pocketbase';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nexport const GET: RequestHandler = async ({ params }) => {\n\ttry {\n\t\tconst pb = await getAuthenticatedPocketBase();\n\t\tconst imagePath = params.path;\n\t\tconsole.log('Requested image path:', imagePath);\n\n\t\tconst record = await pb.collection('attachments').getFirstListItem(`url=\"${imagePath}\"`);\n\t\tconsole.log('Found record:', record);\n\n\t\tif (!record) {\n\t\t\tthrow error(404, 'Image not found');\n\t\t}\n\n\t\tconst fileUrl = pb.files.getUrl(record, record.file);\n\t\tconsole.log('File URL:', fileUrl);\n\n\t\tconst fileResponse = await fetch(fileUrl);\n\n\t\tif (!fileResponse.ok) {\n\t\t\tthrow error(500, 'Failed to fetch the image file');\n\t\t}\n\n\t\tconst contentType = fileResponse.headers.get('content-type') || getContentType(imagePath);\n\t\tconsole.log('Content Type:', contentType);\n\n\t\t// Get the filename from the record or use a default\n\t\tconst filename = record.filename || 'image';\n\n\t\treturn new Response(fileResponse.body, {\n\t\t\tstatus: 200,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': contentType,\n\t\t\t\t'Content-Disposition': `attachment; filename=\"${filename}\"`,\n\t\t\t\t'Cache-Control': 'public, max-age=3600'\n\t\t\t}\n\t\t});\n\t} catch (err) {\n\t\tconsole.error('Error serving image:', err);\n\t\tthrow error(500, 'Internal server error');\n\t}\n};\n\nfunction getContentType(filename: string): string {\n\tconst ext = filename.split('.').pop()?.toLowerCase();\n\tswitch (ext) {\n\t\tcase 'webp':\n\t\t\treturn 'image/webp';\n\t\tcase 'jpg':\n\t\tcase 'jpeg':\n\t\t\treturn 'image/jpeg';\n\t\tcase 'png':\n\t\t\treturn 'image/png';\n\t\tcase 'gif':\n\t\t\treturn 'image/gif';\n\t\tcase 'svg':\n\t\t\treturn 'image/svg+xml';\n\t\tdefault:\n\t\t\treturn 'application/octet-stream';\n\t}\n}\n"
  },
  {
    "path": "src/routes/api/links/+server.ts",
    "content": "import type { RequestHandler } from '@sveltejs/kit';\nimport PocketBase from 'pocketbase';\nimport {\n\tPOCKETBASE_ADMIN_PASSWORD,\n\tPOCKETBASE_ADMIN_EMAIL,\n\tPOCKETBASE_URL,\n\tAPI_KEY\n} from '$env/static/private';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nconst pb = getAuthenticatedPocketBase();\n\nexport const GET: RequestHandler = async ({ url, request }) => {\n\ttry {\n\t\tconst mdbaseCollection = pb.collection('mdbase');\n\t\tconst documentUrl = url.searchParams.get('url');\n\n\t\tif (!documentUrl) {\n\t\t\treturn new Response(JSON.stringify({ message: 'URL parameter is required' }), {\n\t\t\t\tstatus: 400\n\t\t\t});\n\t\t}\n\n\t\tconst documents = await mdbaseCollection.getList(1, 1, {\n\t\t\tfilter: `url=\"${documentUrl}\"`,\n\t\t\texpand: 'links,backlinks'\n\t\t});\n\n\t\tif (documents.items.length === 0) {\n\t\t\treturn new Response(JSON.stringify({ message: 'Document not found' }), { status: 404 });\n\t\t}\n\n\t\tconst document = documents.items[0];\n\n\t\tconst forwardLinks = (document.expand?.links || []).map((link) => ({\n\t\t\tid: link.id,\n\t\t\ttitle: link.title,\n\t\t\turl: link.url\n\t\t}));\n\n\t\tconst backLinks = (document.expand?.backlinks || []).map((link) => ({\n\t\t\tid: link.id,\n\t\t\ttitle: link.title,\n\t\t\turl: link.url\n\t\t}));\n\n\t\treturn new Response(JSON.stringify({ forwardLinks, backLinks }), {\n\t\t\tstatus: 200,\n\t\t\theaders: { 'Content-Type': 'application/json' }\n\t\t});\n\t} catch (error: any) {\n\t\tconsole.error('Error in links API:', error);\n\t\treturn new Response(\n\t\t\tJSON.stringify({\n\t\t\t\tmessage: 'Failed to retrieve links',\n\t\t\t\terror: error.message || 'Unknown error',\n\t\t\t\tdetails: error.data ? JSON.stringify(error.data) : 'No additional details'\n\t\t\t}),\n\t\t\t{ status: 500 }\n\t\t);\n\t}\n};\n\nexport const OPTIONS: RequestHandler = async () => {\n\treturn new Response(null, {\n\t\tstatus: 204,\n\t\theaders: {\n\t\t\t'Access-Control-Allow-Origin': '*',\n\t\t\t'Access-Control-Allow-Methods': 'GET, OPTIONS',\n\t\t\t'Access-Control-Allow-Headers': 'Content-Type, X-API-Key'\n\t\t}\n\t});\n};\n"
  },
  {
    "path": "src/routes/api/ls/+server.ts",
    "content": "import { json } from \"@sveltejs/kit\";\nimport type { RequestHandler } from \"./$types\";\nimport { getAuthenticatedPocketBase } from \"$lib/server/auth\";\n\ninterface FileNode {\n  id: string;\n  title: string;\n  name: string;\n  url: string;\n  children: FileNode[];\n}\n\nfunction buildFileTree(records: any[]): FileNode[] {\n  const tree: FileNode[] = [];\n\n  records.forEach((record) => {\n    const pathParts = record.url.split(\"/\").filter(Boolean); // Split the URL into parts\n    let currentLevel = tree;\n\n    pathParts.forEach((part: string, index: number) => {\n      let existingNode = currentLevel.find((node) => node.name === part);\n\n      // If the node doesn't exist, create a new one\n      if (!existingNode) {\n        existingNode = {\n          id: \"\", // Only set if it's a file (at the last level)\n          title: \"\",\n          name: part, // The name of the folder or file\n          url: \"\", // Only set if it's a file (at the last level)\n          children: [], // This will hold the children (for folders)\n        };\n        currentLevel.push(existingNode);\n      }\n\n      // If it's the last part of the path (a file), assign file properties\n      if (index === pathParts.length - 1) {\n        existingNode.id = record.id;\n        existingNode.title = record.title;\n        existingNode.url = record.url;\n      }\n\n      // Move to the next level in the tree\n      currentLevel = existingNode.children;\n    });\n  });\n\n  return tree;\n}\n\n\nexport const GET: RequestHandler = async () => {\n  try {\n    const pb = await getAuthenticatedPocketBase();\n    const pageSize = 200; // Adjust this value based on your needs\n    let page = 1;\n    let allRecords: any[] = [];\n\n    while (true) {\n      const result = await pb.collection(\"mdbase\").getList(page, pageSize, {\n        sort: \"url\",\n      });\n\n      allRecords = allRecords.concat(result.items);\n\n      if (!result.items.length || result.items.length < pageSize) {\n        break;\n      }\n\n      page++;\n    }\n\n    const fileTree = buildFileTree(allRecords);\n    return json(fileTree);\n  } catch (error) {\n    console.error(\"Error fetching records:\", error);\n    return json({ error: \"Failed to fetch file tree\" }, { status: 500 });\n  }\n};\n"
  },
  {
    "path": "src/routes/api/search/+server.ts",
    "content": "// api/search/+server.ts\nimport { json } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\nimport uFuzzy from '@leeoniya/ufuzzy';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nfunction extractSnippet(content: string, query: string, snippetLength: number = 150) {\n\tconst lowerContent = content.toLowerCase();\n\tconst lowerQuery = query.toLowerCase();\n\tconst index = lowerContent.indexOf(lowerQuery);\n\n\tif (index === -1) return content.slice(0, snippetLength);\n\n\tconst start = Math.max(0, index - snippetLength / 2);\n\tconst end = Math.min(content.length, index + query.length + snippetLength / 2);\n\n\tlet snippet = content.slice(start, end);\n\n\tif (start > 0) snippet = '...' + snippet;\n\tif (end < content.length) snippet = snippet + '...';\n\n\treturn snippet;\n}\n\nexport const GET: RequestHandler = async ({ url }) => {\n\tconst query = url.searchParams.get('query');\n\tif (!query) {\n\t\treturn json({ error: 'Query parameter is required' }, { status: 400 });\n\t}\n\n\ttry {\n\t\t/* await authenticateAdmin(); */\n\t\tconst pb = await getAuthenticatedPocketBase();\n\n\t\tconst pbResults = await pb.collection('mdbase').getList(1, 1000, {\n\t\t\tfields: 'id,title,content,url'\n\t\t});\n\t\tconsole.log('searched');\n\n\t\tconst haystack = pbResults.items.map((item) => item.title + ' ' + item.content);\n\t\tconst uf = new uFuzzy();\n\n\t\tlet idxs = uf.filter(haystack, query);\n\n\t\tif (idxs != null && idxs.length > 0) {\n\t\t\tlet info = uf.info(idxs, haystack, query);\n\t\t\tlet order = uf.sort(info, haystack, query);\n\n\t\t\tconst results = order.map((i) => {\n\t\t\t\tconst item = pbResults.items[info.idx[i]];\n\t\t\t\treturn {\n\t\t\t\t\ttitle: item.title,\n\t\t\t\t\turl: `${item.url}`,\n\t\t\t\t\tsnippet: extractSnippet(item.content, query)\n\t\t\t\t};\n\t\t\t});\n\n\t\t\treturn json(results);\n\t\t} else {\n\t\t\treturn json([]);\n\t\t}\n\t} catch (error) {\n\t\tconsole.error('Search error:', error);\n\t\treturn json({ error: 'An error occurred during search' }, { status: 500 });\n\t}\n};\n"
  },
  {
    "path": "src/routes/api/tags/+server.ts",
    "content": "// src/routes/api/hello/+server.ts\nimport { json } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nexport const GET: RequestHandler = async ({ request }) => {\n\tconst pb = await getAuthenticatedPocketBase();\n\n\tconst records = await pb.collection('tags').getFullList();\n\tconst tags = records.map((tag) => {\n\t\treturn {\n\t\t\tname: tag.tag\n\t\t};\n\t});\n\n\treturn json(tags);\n};\n"
  },
  {
    "path": "src/routes/api/upload/+server.ts",
    "content": "import type { RequestHandler } from \"@sveltejs/kit\";\nimport PocketBase from \"pocketbase\";\nimport path from \"path\";\nimport { compile } from \"mdsvex\";\nimport { getAuthenticatedPocketBase } from \"$lib/server/auth\";\n\nimport { addFrontmatterToMarkdown } from \"$lib/md\"; // Updated to accept fileContent and url\nimport { visit } from \"unist-util-visit\";\nimport remarkFootnotes from \"remark-footnotes\";\nimport remarkTags from \"$lib/remark-plugins/remarkTags\";\nimport remarkHighlight from \"$lib/remark-plugins/highlightSyn\";\n/* import rehypeMermaid from 'rehype-mermaid'; */\nimport remarkMermaid from \"$lib/remark-plugins/mermaidDiag\";\nimport remarkLogImages from \"$lib/remark-plugins/imgRel\";\n/* import rehypeKatexSvelte from 'rehype-katex-svelte'; */\nimport rehypeKatex from \"rehype-katex\";\nimport remarkMath from \"remark-math\";\nimport rehypeCallouts from \"rehype-callouts\";\nimport rehypeAutolinkHeadings from \"rehype-autolink-headings\";\nimport wikiLink from \"remark-wiki-link\";\nimport obsidianImagePlugin from \"$lib/remark-plugins/obsidianImage\";\nimport rehypeWrapLiWithP from \"$lib/rehype_plugins/wrapWithP\"; \n\nimport {\n  POCKETBASE_ADMIN_PASSWORD,\n  POCKETBASE_ADMIN_EMAIL,\n  POCKETBASE_URL,\n  API_KEY, // Add this line to import the API_KEY\n} from \"$env/static/private\";\n\nfunction verifyApiKey(request: Request): boolean {\n  const apiKey = request.headers.get(\"X-API-Key\");\n  return apiKey === API_KEY;\n}\n\n/**\n * Middleware to check API key before processing the request\n */\nasync function apiKeyMiddleware(\n  request: Request,\n  handler: (req: Request) => Promise,\n): Promise {\n  if (!verifyApiKey(request)) {\n    return new Response(JSON.stringify({ message: \"Unauthorized\" }), {\n      status: 401,\n      headers: { \"Content-Type\": \"application/json\" },\n    });\n  }\n  return handler(request);\n}\n\n// Define interfaces for frontmatter and compiled markdown\ninterface Frontmatter {\n  title?: string;\n  [key: string]: any;\n}\n\ninterface CompiledMD {\n  code: string;\n  data?: {\n    fm?: Frontmatter;\n  };\n}\n\n// Initialize PocketBase\nconst pb = await getAuthenticatedPocketBase();\n\n/**\n * Custom WikiLink transformer for Markdown processing.\n */\nfunction customWikiLink() {\n  return function transformer(tree: any, file: any) {\n    // Ensure file metadata exists\n    if (!file || !file.data || !file.data.fm || !file.data.fm.mdpath) {\n      throw new Error(\"File metadata with url is missing.\");\n    }\n\n    const url = file.data.fm.mdpath; // e.g., '/writing/f2/test'\n\n    visit(tree, \"wikiLink\", (node: any) => {\n      // Extract the link part before any pipe (e.g., [[link|alias]])\n      const rawLink = node.value.trim().split(\"|\")[0].trim(); // e.g., '../f1/test'\n\n      // Resolve the absolute path based on the current file's directory\n      const folder = path.dirname(url.split(\".\")[0]);\n\n      const absPath = path.join(folder, rawLink); // e.g., 'mdpath/f1/test'\n\n      node.data.permalink = `/${absPath}.md`;\n      node.data.hProperties.href = `/${absPath}.md`;\n    });\n  };\n}\n\nfunction unescapeHtml(html: string): string {\n  return html\n    .replace(/&lt;/g, \"<\")\n    .replace(/&gt;/g, \">\")\n    .replace(/&amp;/g, \"&\")\n    .replace(/&quot;/g, '\"')\n    .replace(/&#39;/g, \"'\");\n}\n\nfunction processContent(content: string): string {\n  // Adjusting the regex to correctly match the pattern {@html ...} and removing the surrounding spaces.\n  const regex = /{@html\\s+([\\s\\S]*?)\\s*}/g;\n  const stage1 = content.replace(regex, (match, p1) => {\n    // Removing {@html } and unescaping the inner HTML\n    return unescapeHtml(p1);\n  });\n\n  const backticksRemoved = stage1.replace(\n    /`(<code[\\s\\S]*?<\\/code>)`/g,\n    (match, p1) => {\n      return p1; // Remove the backticks around <code>...</code>\n    },\n  );\n\n  return unescapeHtml(backticksRemoved);\n}\n/**\n * Compile Markdown content using mdsvex with various plugins.\n */\nasync function compileMarkdown(fileContent: string, url: string): Promise {\n  // Add or update frontmatter with the provided URL\n  const processedContent = addFrontmatterToMarkdown(fileContent, url);\n\n  const compiled: CompiledMD = await compile(processedContent, {\n    extensions: [\".svx\"],\n    smartypants: {\n      dashes: \"oldschool\",\n    },\n    remarkPlugins: [\n      remarkMath,\n      remarkFootnotes,\n      [\n        wikiLink,\n        {\n          hrefTemplate: (permalink: string) => `/${permalink}.md`,\n        },\n      ],\n      [customWikiLink],\n      obsidianImagePlugin,\n      remarkMermaid,\n      remarkHighlight,\n      remarkLogImages,\n      remarkTags,\n    ],\n    rehypePlugins: [rehypeKatex, rehypeCallouts, rehypeAutolinkHeadings, rehypeWrapLiWithP],\n  });\n\n  const frontmatter: Frontmatter = compiled.data?.fm || {};\n  const fileName: string = path.parse(fileContent).name; // Adjusted as fdpath is now url\n  const title: string = frontmatter.title || fileName;\n\n  // const node = { code: processContent(compiled.code), data: compiled.data };\n  const node = {code: processedContent, data: processedContent};\n\n  // Use the provided URL\n  return { compiled: node, title, url };\n}\n\nasync function ensureMdbaseCollection() {\n  try {\n    // Check if the collection exists\n    const collection = await pb.collections.getOne(\"mdbase\");\n    console.log(\"Mdbase collection already exists\");\n\n    const linksFieldExists = collection.schema.some(\n      (field) => field.name === \"links\",\n    );\n    const backlinksFieldExists = collection.schema.some(\n      (field) => field.name === \"backlinks\",\n    );\n    const tagFieldExists = collection.schema.some(\n      (field) => field.name === \"tags\",\n    );\n\n    if (!linksFieldExists) {\n      console.log(\"Adding links field to mdbase collection...\");\n      await pb.collections.update(\"mdbase\", {\n        schema: [\n          ...collection.schema,\n          {\n            name: \"links\",\n            type: \"relation\",\n            required: false,\n            options: {\n              collectionId: collection.id,\n              cascadeDelete: false,\n              maxSelect: null,\n              displayFields: [\"title\"],\n            },\n          },\n        ],\n      });\n      console.log(\"Links field added successfully\");\n    } else {\n      console.log(\"Links field already exists\");\n    }\n\n    if (!backlinksFieldExists) {\n      console.log(\"Adding links field to mdbase collection...\");\n      await pb.collections.update(\"mdbase\", {\n        schema: [\n          ...collection.schema,\n          {\n            name: \"backlinks\",\n            type: \"relation\",\n            required: false,\n            options: {\n              collectionId: collection.id,\n              cascadeDelete: false,\n              maxSelect: null,\n              displayFields: [\"title\"],\n            },\n          },\n        ],\n      });\n      console.log(\"Backlinks field added successfully\");\n    } else {\n      console.log(\"BackLinks field already exists\");\n    }\n\n    if (!tagFieldExists) {\n      const tagcol = await pb.collections.getOne(\"tags\");\n      console.log(\"Adding links field to mdbase collection...\");\n      await pb.collections.update(\"mdbase\", {\n        schema: [\n          ...collection.schema,\n          {\n            name: \"tags\",\n            type: \"relation\",\n            required: false,\n            options: {\n              collectionId: tagcol.id,\n              cascadeDelete: false,\n              maxSelect: null,\n              displayFields: [\"title\"],\n            },\n          },\n        ],\n      });\n      console.log(\"Tags field added successfully\");\n    } else {\n      console.log(\"Tags field already exists\");\n    }\n\n    // Check if the index exists\n    const indexExists = collection.indexes.some((index) =>\n      index.includes(\"CREATE INDEX `idx_url` ON `mdbase` (`url`)\"),\n    );\n\n    if (!indexExists) {\n      console.log(\"Creating index on url field...\");\n      await pb.collections.update(\"mdbase\", {\n        indexes: [\n          ...collection.indexes,\n          \"CREATE INDEX `idx_url` ON `mdbase` (`url`)\",\n        ],\n      });\n      console.log(\"Index created successfully\");\n    } else {\n      console.log(\"Index on url field already exists\");\n    }\n  } catch (error) {\n    if (error.status === 404) {\n      console.log(\"Mdbase collection does not exist. Creating...\");\n      try {\n        await pb.collections.create({\n          name: \"mdbase\",\n          type: \"base\",\n          schema: [\n            { name: \"title\", type: \"text\" },\n            { name: \"content\", type: \"text\" },\n            { name: \"url\", type: \"text\" },\n            {\n              name: \"mdfile\",\n              type: \"file\",\n              required: true,\n              options: {\n                maxSelect: 1,\n                maxSize: 5242880, // 5MB max size\n              },\n            },\n            {\n              name: \"frontmatter\",\n              type: \"json\",\n              options: {\n                maxSize: 5242880,\n              },\n            },\n          ],\n          indexes: [\"CREATE INDEX `idx_url` ON `mdbase` (`url`)\"],\n        });\n        console.log(\n          \"Mdbase collection created successfully with index on url field\",\n        );\n      } catch (createError) {\n        console.error(\"Failed to create mdbase collection:\", createError);\n        throw createError;\n      }\n    } else {\n      console.error(\"Error checking mdbase collection:\", error);\n      throw error;\n    }\n  }\n}\n\nfunction extractWikiLinks(htmlContent: string): string[] {\n  // const regex = /<a[^>]+href=\"([^\"]+\\.md)\"[^>]*>/g;\n  // const matches = htmlContent.matchAll(regex);\n  // return Array.from(matches, (m) => m[1]);\n  return []\n}\n\nasync function updateLinks(recordId: string, content: string) {\n  const mdbaseCollection = pb.collection(\"mdbase\");\n\n  // Extract wiki links from the content\n  const wikiLinks = extractWikiLinks(content);\n  console.log(\"WIKI =====>\", wikiLinks);\n\n  // Get the current record\n  const currentRecord = await mdbaseCollection.getOne(recordId, {\n    expand: \"links,backlinks\",\n  });\n  const oldLinks = currentRecord.links || [];\n\n  // Get IDs of linked documents\n  const newLinkedRecordIds = [];\n  for (const link of wikiLinks) {\n    const linkedRecords = await mdbaseCollection.getList(1, 1, {\n      filter: `url=\"${link.startsWith(\"/\") ? link.slice(1) : link}\"`,\n    });\n\n    if (linkedRecords.items.length > 0) {\n      newLinkedRecordIds.push(linkedRecords.items[0].id);\n    }\n  }\n\n  // Update the current record's links\n  await mdbaseCollection.update(recordId, {\n    links: newLinkedRecordIds,\n  });\n\n  // Update backlinks\n  const linksToAdd = newLinkedRecordIds.filter((id) => !oldLinks.includes(id));\n  const linksToRemove = oldLinks.filter(\n    (id) => !newLinkedRecordIds.includes(id),\n  );\n\n  for (const id of linksToAdd) {\n    await mdbaseCollection.update(id, {\n      \"backlinks+\": recordId,\n    });\n  }\n\n  for (const id of linksToRemove) {\n    await mdbaseCollection.update(id, {\n      \"backlinks-\": recordId,\n    });\n  }\n}\n\nasync function ensureAttachmentsCollection() {\n  try {\n    // Check if the collection exists\n    await pb.collections.getOne(\"attachments\");\n    console.log(\"Attachments collection already exists\");\n  } catch (error) {\n    if (error.status === 404) {\n      console.log(\"Attachments collection does not exist. Creating...\");\n      try {\n        await pb.collections.create({\n          name: \"attachments\",\n          type: \"base\",\n          schema: [\n            {\n              name: \"file\",\n              type: \"file\",\n              required: true,\n              options: {\n                maxSelect: 1,\n                maxSize: 524288000,\n              },\n            },\n            {\n              name: \"url\",\n              type: \"text\",\n              required: true,\n            },\n          ],\n        });\n        console.log(\"Attachments collection created successfully\");\n      } catch (createError) {\n        console.error(\"Failed to create attachments collection:\", createError);\n        throw createError;\n      }\n    } else {\n      console.error(\"Error checking attachments collection:\", error);\n      throw error;\n    }\n  }\n}\n\nasync function ensureTagsCollection() {\n  try {\n    // Check if the collection exists\n    await pb.collections.getOne(\"tags\");\n    console.log(\"Tags collection already exists\");\n  } catch (error) {\n    if (error.status === 404) {\n      console.log(\"Tags collection does not exist. Creating...\");\n      const collection = await pb.collections.getOne(\"mdbase\");\n      try {\n        await pb.collections.create({\n          name: \"tags\",\n          type: \"base\",\n          schema: [\n            {\n              name: \"tag\",\n              type: \"text\",\n              required: true,\n            },\n\n            {\n              name: \"links\",\n              type: \"relation\",\n              required: false,\n              options: {\n                collectionId: collection.id,\n                cascadeDelete: false,\n                maxSelect: null,\n                displayFields: [\"title\"],\n              },\n            },\n          ],\n        });\n        console.log(\"tags collection created successfully\");\n      } catch (createError) {\n        console.error(\"Failed to create tags collection:\", createError);\n        throw createError;\n      }\n    } else {\n      console.error(\"Error checking tags collection:\", error);\n      throw error;\n    }\n  }\n}\n\ninterface Tag {\n  id: string;\n  name: string;\n  links: string[];\n}\n\nasync function parseTagsAndUpdatePocketBase(\n  compiledMarkdown: string,\n  frontmatter: Frontmatter,\n  noteId: string,\n  pb: PocketBase,\n) {\n  const processedTags = new Set<string>();\n  const noteTags: string[] = [];\n\n  // Function to process a single tag\n  async function processTag(tagName: string) {\n    if (processedTags.has(tagName)) return;\n    processedTags.add(tagName);\n\n    try {\n      let tag: Tag;\n      try {\n        tag = await pb.collection(\"tags\").getFirstListItem(`tag=\"${tagName}\"`);\n        // If the tag exists, update it\n        if (!tag.links.includes(noteId)) {\n          await pb.collection(\"tags\").update<Tag>(tag.id, {\n            links: [...tag.links, noteId],\n          });\n          console.log(\n            `Updated existing tag ${tagName} with new link to note ${noteId}`,\n          );\n        } else {\n          console.log(`Tag ${tagName} already linked to note ${noteId}`);\n        }\n      } catch (error) {\n        // If the tag doesn't exist, create it\n        tag = await pb.collection(\"tags\").create<Tag>({\n          tag: tagName,\n          links: [noteId],\n        });\n        console.log(`Created new tag: ${tagName}`);\n      }\n      noteTags.push(tag.id);\n    } catch (error) {\n      console.error(`Error processing tag \"${tagName}\":`, error);\n    }\n  }\n\n  // Process tags from compiled markdown\n  const tagRegex = /<span class=\"tag\">([^<]+)<\\/span>/g;\n  let match;\n  while ((match = tagRegex.exec(compiledMarkdown)) !== null) {\n    const tagName = match[1];\n    await processTag(tagName);\n  }\n\n  // Process tags from frontmatter\n  if (frontmatter.tags && Array.isArray(frontmatter.tags)) {\n    for (const tag of frontmatter.tags) {\n      await processTag(tag);\n    }\n  }\n\n  // Update the note in mdbase collection with the tags\n  try {\n    const note = await pb.collection(\"mdbase\").getOne(noteId);\n    await pb.collection(\"mdbase\").update(noteId, {\n      tags: noteTags,\n    });\n    console.log(`Updated note ${noteId} with tags: ${noteTags.join(\", \")}`);\n  } catch (error) {\n    console.error(`Error updating note ${noteId} with tags:`, error);\n  }\n}\n\nexport const POST: RequestHandler = async ({ request }) => {\n  return apiKeyMiddleware(request, async () => {\n    try {\n      await ensureAttachmentsCollection();\n      await ensureMdbaseCollection();\n      await ensureTagsCollection();\n\n      const formData = await request.formData();\n      const file = formData.get(\"file\") as File | null;\n      const url = formData.get(\"url\") as string | null;\n\n      if (!file) {\n        return new Response(JSON.stringify({ message: \"No file uploaded\" }), {\n          status: 400,\n        });\n      }\n\n      const fileName = file.name;\n      const fileExtension = path.extname(fileName).toLowerCase();\n\n      if (fileExtension === \".md\") {\n        if (!url) {\n          return new Response(\n            JSON.stringify({ message: \"URL is required for Markdown files\" }),\n            {\n              status: 400,\n            },\n          );\n        }\n\n        const fileContent = await file.text();\n        const {\n          compiled,\n          title,\n          url: providedUrl,\n        } = await compileMarkdown(fileContent, url);\n        const frontmatter = compiled.data?.fm || {};\n        console.log(\"front\", frontmatter);\n\n        const mdbaseCollection = pb.collection(\"mdbase\");\n        const existingRecords = await mdbaseCollection.getList(1, 1, {\n          filter: `url=\"${providedUrl}\"`,\n        });\n\n        let record;\n        if (existingRecords.items.length > 0) {\n          const existingRecord = existingRecords.items[0];\n          record = await mdbaseCollection.update(existingRecord.id, {\n            title,\n            frontmatter,\n            content: compiled.code,\n            url: providedUrl,\n            mdfile: file,\n          });\n        } else {\n          record = await mdbaseCollection.create({\n            title,\n            frontmatter,\n            content: compiled.code,\n            url: providedUrl,\n            mdfile: file,\n          });\n        }\n\n        // Update links and backlinks\n        await updateLinks(record.id, compiled.code);\n        console.log(\"Updated bi-directional links\");\n        await parseTagsAndUpdatePocketBase(\n          compiled.code,\n          frontmatter,\n          record.id,\n          pb,\n        );\n        console.log(\"Updated  tags\");\n\n        return new Response(\n          JSON.stringify({\n            message: \"Markdown file uploaded successfully\",\n            record,\n          }),\n          { status: 200 },\n        );\n      } else if (\n        [\".png\", \".jpg\", \".svg\", \".jpeg\", \".gif\", \".webp\"].includes(\n          fileExtension,\n        )\n      ) {\n        console.log(\"Processing image file...\");\n        try {\n          const attachmentsCollection = pb.collection(\"attachments\");\n\n          // Check if a record with the given URL already exists\n          const existingRecords = await attachmentsCollection.getList(1, 1, {\n            filter: `url=\"${url}\"`,\n          });\n\n          let attachmentRecord;\n          if (existingRecords.items.length > 0) {\n            // Update existing record\n            const existingRecord = existingRecords.items[0];\n            console.log(\"Updating existing attachment record...\");\n            attachmentRecord = await attachmentsCollection.update(\n              existingRecord.id,\n              {\n                file: file,\n                url: url,\n              },\n            );\n          } else {\n            // Create new record\n            console.log(\"Creating new attachment record...\");\n            attachmentRecord = await attachmentsCollection.create({\n              file: file,\n              url: url,\n            });\n          }\n\n          const fileUrl = pb.getFileUrl(\n            attachmentRecord,\n            attachmentRecord.file,\n          );\n          console.log(\"Generated file URL:\", fileUrl);\n\n          return new Response(\n            JSON.stringify({\n              message:\n                existingRecords.items.length > 0\n                  ? \"Image updated successfully\"\n                  : \"Image uploaded successfully\",\n              record: attachmentRecord,\n              url: fileUrl,\n            }),\n            { status: 200 },\n          );\n        } catch (imageError: any) {\n          console.error(\"Error during image upload/update:\", imageError);\n          return new Response(\n            JSON.stringify({\n              message: \"Image upload/update failed\",\n              error:\n                imageError.message ||\n                \"Unknown error during image upload/update\",\n              details: imageError.data\n                ? JSON.stringify(imageError.data)\n                : \"No additional details\",\n            }),\n            { status: 500 },\n          );\n        }\n      } else {\n        return new Response(\n          JSON.stringify({ message: \"Unsupported file type\" }),\n          { status: 400 },\n        );\n      }\n    } catch (error: any) {\n      console.error(\"Error in upload API:\", error);\n      return new Response(\n        JSON.stringify({\n          message: \"File upload failed\",\n          error: error.message || \"Unknown error\",\n          details: error.data\n            ? JSON.stringify(error.data)\n            : \"No additional details\",\n        }),\n        { status: 500 },\n      );\n    }\n  });\n};\n\nexport const DELETE: RequestHandler = async ({ request }) => {\n  return apiKeyMiddleware(request, async () => {\n    try {\n      const { url } = await request.json();\n\n      if (!url) {\n        return new Response(JSON.stringify({ message: \"URL is required\" }), {\n          status: 400,\n        });\n      }\n\n      const mdbaseCollection = pb.collection(\"mdbase\");\n      const attachmentsCollection = pb.collection(\"attachments\");\n\n      let deletedCount = 0;\n\n      // Try to delete from mdbase collection\n      try {\n        const mdbaseRecords = await mdbaseCollection.getList(1, 1, {\n          filter: `url=\"${url}\"`,\n        });\n        if (mdbaseRecords.items.length > 0) {\n          await mdbaseCollection.delete(mdbaseRecords.items[0].id);\n          console.log(`Deleted ${url} from mdbase collection`);\n          deletedCount++;\n        }\n      } catch (error) {\n        console.error(`Error deleting ${url} from mdbase collection:`, error);\n      }\n\n      // Try to delete from attachments collection\n      try {\n        const attachmentRecords = await attachmentsCollection.getList(1, 1, {\n          filter: `url=\"${url}\"`,\n        });\n        if (attachmentRecords.items.length > 0) {\n          await attachmentsCollection.delete(attachmentRecords.items[0].id);\n          console.log(`Deleted ${url} from attachments collection`);\n          deletedCount++;\n        }\n      } catch (error) {\n        console.error(\n          `Error deleting ${url} from attachments collection:`,\n          error,\n        );\n      }\n\n      if (deletedCount > 0) {\n        return new Response(\n          JSON.stringify({ message: \"File deleted successfully\" }),\n          {\n            status: 200,\n          },\n        );\n      } else {\n        return new Response(JSON.stringify({ message: \"File not found\" }), {\n          status: 404,\n        });\n      }\n    } catch (error: any) {\n      console.error(\"Error in delete API:\", error);\n      return new Response(\n        JSON.stringify({\n          message: \"File deletion failed\",\n          error: error.message || \"Unknown error\",\n          details: error.data\n            ? JSON.stringify(error.data)\n            : \"No additional details\",\n        }),\n        { status: 500 },\n      );\n    }\n  });\n};\n\nexport const GET: RequestHandler = async ({ request }) => {\n  return apiKeyMiddleware(request, async () => {\n    try {\n      await ensureAttachmentsCollection();\n      await ensureMdbaseCollection();\n      await ensureTagsCollection();\n\n      const mdbaseCollection = pb.collection(\"mdbase\");\n      const attachmentsCollection = pb.collection(\"attachments\");\n\n      const mdbaseRecords = await mdbaseCollection.getFullList({\n        fields: \"url\",\n      });\n      const attachmentRecords = await attachmentsCollection.getFullList({\n        fields: \"url\",\n      });\n\n      const allUrls = [\n        ...mdbaseRecords.map((record) => record.url),\n        ...attachmentRecords.map((record) => record.url),\n      ];\n\n      return new Response(JSON.stringify(allUrls), {\n        status: 200,\n        headers: { \"Content-Type\": \"application/json\" },\n      });\n    } catch (error: any) {\n      console.error(\"Error in list API:\", error);\n      return new Response(\n        JSON.stringify({\n          message: \"Failed to list files\",\n          error: error.message || \"Unknown error\",\n          details: error.data\n            ? JSON.stringify(error.data)\n            : \"No additional details\",\n        }),\n        {\n          status: 500,\n          headers: { \"Content-Type\": \"application/json\" },\n        },\n      );\n    }\n  });\n};\n\n// Handle OPTIONS requests for CORS preflight\nexport const OPTIONS: RequestHandler = async () => {\n  return new Response(null, {\n    status: 204,\n    headers: {\n      \"Access-Control-Allow-Origin\": \"*\",\n      \"Access-Control-Allow-Methods\": \"POST, DELETE, OPTIONS, GET\",\n      \"Access-Control-Allow-Headers\": \"Content-Type\",\n    },\n  });\n};\n"
  },
  {
    "path": "src/routes/login/+page.server.ts",
    "content": "import type { PageServerLoad, Actions } from './$types.js';\nimport { fail, redirect } from '@sveltejs/kit';\nimport { superValidate } from 'sveltekit-superforms';\nimport { zod } from 'sveltekit-superforms/adapters';\nimport { formSchema } from '$lib/components/schema';\n\nexport const load: PageServerLoad = async () => {\n\treturn {\n\t\tform: await superValidate(zod(formSchema))\n\t};\n};\n\nexport const actions: Actions = {\n\tdefault: async (event) => {\n\t\tconst data = await superValidate(event, zod(formSchema));\n\t\tif (!data.valid) {\n\t\t\treturn fail(400, {\n\t\t\t\tdata\n\t\t\t});\n\t\t}\n\t\tconsole.log(data);\n\t\tconst email = data.data.username;\n\t\tconst password = data.data.password;\n\t\tconsole.log('Login action called');\n\t\tif (!email || !password) {\n\t\t\treturn fail(400, { emailRequired: !email, passwordRequired: !password });\n\t\t}\n\t\ttry {\n\t\t\tconst authData = await event.locals.pb.collection('users').authWithPassword(email, password);\n\t\t\tconsole.log('Logged in successfully. Auth state:', event.locals.pb.authStore.isValid);\n\n\t\t\t// Ensure the auth data is saved to the auth store\n\t\t\tevent.locals.pb.authStore.save(authData.token, authData.record);\n\n\t\t\t// Set the auth cookie\n\t\t\tconst cookieOptions = {\n\t\t\t\thttpOnly: true,\n\t\t\t\tsecure: process.env.NODE_ENV === 'production',\n\t\t\t\tsameSite: 'lax',\n\t\t\t\tpath: '/',\n\t\t\t\tmaxAge: 60 * 60 * 24 * 30 // 30 days\n\t\t\t};\n\t\t\tconst cookie = event.locals.pb.authStore.exportToCookie(cookieOptions);\n\n\t\t\tconsole.log('Auth state before redirect:', event.locals.pb.authStore.isValid);\n\t\t\tconsole.log('Attempting to redirect to /dashboard');\n\n\t\t\t// Use throw redirect instead of return\n\t\t\t// throw redirect(303, '/login/success');\n\t\t} catch (error) {\n\t\t\tconsole.error('Login error:', error);\n\t\t\tconst errorObj = error as ClientResponseError;\n\t\t\treturn fail(500, { form: data });\n\t\t}\n\n\t\tthrow redirect(303, '/login/success');\n\t\t/* return {\n\t\t\tform\n\t\t}; */\n\t}\n};\n"
  },
  {
    "path": "src/routes/login/+page.svelte",
    "content": "<script>\n\timport LoginForm from '$lib/components/LoginForm.svelte';\n\texport let data;\n\t$: formData = data?.form;\n</script>\n\n<LoginForm bind:data={formData} />\n"
  },
  {
    "path": "src/routes/login/success/+page.server.ts",
    "content": "import type { PageServerLoad } from './$types';\nimport { redirect } from '@sveltejs/kit';\n\nexport const load: PageServerLoad = async ({ locals }) => {\n\tconsole.log('Dashboard load function. Auth state:', locals.pb.authStore.isValid);\n\n\tif (!locals.pb.authStore.isValid) {\n\t\tconsole.log('User not authenticated, redirecting to login');\n\t\tthrow redirect(303, '/login');\n\t}\n\n\t// You can fetch additional data for the dashboard here\n\treturn {\n\t\tuser: locals.pb.authStore.model\n\t};\n};\n"
  },
  {
    "path": "src/routes/login/success/+page.svelte",
    "content": "<!-- src/routes/dashboard/+page.svelte -->\n<script lang=\"ts\">\n\texport let data;\n</script>\n\n<h1>Welcome to your dashboard, {data.user?.email}</h1>\n\n<div>Logged in Successfully !!</div>\n"
  },
  {
    "path": "src/routes/publications/+page.svelte",
    "content": "<script>\n\timport Award from '$lib/components/award.svelte';\n\texport const pubs = [\n\t\t{\n\t\t\ttitle:\n\t\t\t\t'A Neuro-Symbolic Architecture for Efficient Classification using Hyperdimensional Computing',\n\t\t\tauth: 'Rishikanth Chandrasekaran, Tajana Rosing',\n\t\t\tvenue: 'ISLPED',\n\t\t\tdate: 2024\n\t\t},\n\t\t{\n\t\t\ttitle: 'Federated Hyperdimensional Computing',\n\t\t\tauth: 'Kazim Ergun, Rishikanth Chandrasekaran, Tajana Rosing',\n\t\t\tvenue: 'ACM TIoT (under review)',\n\t\t\tdate: 2023\n\t\t},\n\t\t{\n\t\t\ttitle: 'Multi-label classification with Hyperdimensional Representations',\n\t\t\tauth: 'Rishikanth Chandrasekaran, F Asgareinjad, J Morris, T Rosing',\n\t\t\tvenue: 'IEEE Access Journal',\n\t\t\tdate: 2023\n\t\t},\n\t\t{\n\t\t\ttitle: 'Fhdnn: Communication efficient and robust federated learning for aiot networks',\n\t\t\tauth: 'R Chandrasekaran, K Ergun, J Lee, D Nanjunda, J Kang, T Rosing',\n\t\t\tvenue: 'Proceedings of the 59th ACM/IEEE Design Automation Conference',\n\t\t\tdate: 2022\n\t\t},\n\t\t{\n\t\t\ttitle:\n\t\t\t\t'Hdnn-pim: Efficient in memory design of hyperdimensional computing with feature extraction',\n\t\t\tauth: 'A Dutta, S Gupta, B Khaleghi, R Chandrasekaran, W Xu, T Rosing',\n\t\t\tvenue: 'Proceedings of the Great Lakes Symposium on VLSI',\n\t\t\tdate: 2022\n\t\t},\n\t\t{\n\t\t\ttitle: 'A drone-based system for intelligent and autonomous homes',\n\t\t\tauth: 'S Xia, R Chandrasekaran, Y Liu, C Yang, TS Rosing, X Jiang',\n\t\t\tvenue: 'Proceedings of the 19th ACM Conference on Embedded Networked Sensor Systems',\n\t\t\tdate: 2021,\n\t\t\taward: 'Best Demo'\n\t\t},\n\t\t{\n\t\t\ttitle: 'Efficient Sparse Processing for Smart Home Applications',\n\t\t\tauth: 'Rishikanth Chandrasekaran, Yunhui Guo, Anthony Thomas, Masimiliano Menarini, Michael Ostertag, Tajana Rosing',\n\t\t\tvenue: 'ACM Conference on Embedded Network Sensor Systems (SenSys)',\n\t\t\tdate: 2019\n\t\t},\n\t\t{\n\t\t\ttitle:\n\t\t\t\t'A Scalable System for Apportionment and Tracking of Energy Footprints in Commercial Buildings',\n\t\t\tauth: 'Peter Wei, Xiaoqi Chen, Jordan Vega, Stephen Xia, Rishikanth Chandrasekaran, Xiaofan Jiang',\n\t\t\tvenue: 'ACM Transaction on Sensor Networks (TSON)',\n\t\t\tdate: 2018\n\t\t},\n\t\t{\n\t\t\ttitle: 'PAWS: A Wearable Acoustic System for Pedestrian Safety',\n\t\t\tauth: 'Daniel de Godoy, Bashima Islam, Stephen Xia, Md Tamzeed Islam, Rishikanth Chandrasekaran, Yen-Chun Chen, Shahriar Nirjon, Peter R Kinget, Xiaofan Jiang',\n\t\t\tvenue: 'Internet-of-Things Design and Implementation (IoTDI)',\n\t\t\tdate: 2018\n\t\t},\n\t\t{\n\t\t\ttitle: 'Paws: A wearable acoustic system for pedestrian safety.',\n\t\t\tvenue:\n\t\t\t\t'IEEE/ACM Third International Conference on Internet-of-Things Design and Implementation (IoTDI)',\n\t\t\tdate: 2018,\n\t\t\tauth: 'Daniel de Godoy, Bashima Islam, Stephen Xia, Md Tamzeed Islam, Rishikanth Chandrasekaran, Yen-Chun Chen, Shahriar Nirjon, Peter R Kinget, Xiaofan Jiang'\n\t\t},\n\t\t{\n\t\t\ttitle:\n\t\t\t\t'ePrints a real-time and scalable system for fair apportionment and tracking of personal energy footprints in commercial buildings',\n\t\t\tvenue:\n\t\t\t\t'ACM International Conference on Systems for Energy-Efficient Built Environments (BuildSys)',\n\t\t\tauth: 'P Wei, X Chen, J Vega, S Xia, R Chandrasekaran, X Jiang',\n\t\t\tdate: 2017,\n\t\t\taward: 'Best Paper Runner Up'\n\t\t},\n\t\t{\n\t\t\ttitle: 'Adaptive and Personalized Energy Saving Suggestions for Occupants in Smart Buildings',\n\t\t\tvenue:\n\t\t\t\t'ACM International Conference on Systems for Energy-Efficient Built Environments (BuildSys)',\n\t\t\tauth: 'P Wei, X Chen, R Chandrasekaran, F Song, X Jiang',\n\t\t\tdate: 2016,\n\t\t\taward: 'Best Poster'\n\t\t},\n\t\t{\n\t\t\ttitle:\n\t\t\t\t'SEUS: A Wearable Multi-Channel Acoustic Headset Platform to Improve Pedestrian Safety',\n\t\t\tvenue: 'ACM Conference on Embedded Network Sensor Systems (SenSys)',\n\t\t\tauth: 'R Chandrasekaran, D de Godoy, S Xia, MT Islam, B Islam, S Nirjon, P Kinget, X Jiang',\n\t\t\tdate: 2016\n\t\t},\n\t\t{\n\t\t\ttitle: 'Personal energy footprint in shared building environment',\n\t\t\tvenue: 'International Conference on Information Processing in Sensor Networks (IPSN)',\n\t\t\tauth: 'P Wei, X Chen, R Chandrasekaran, F Song, X Jiang',\n\t\t\tdate: 2016\n\t\t},\n\t\t{\n\t\t\ttitle:\n\t\t\t\t'Low-cost intelligent gesture recognition engine for audio-vocally impaired individuals',\n\t\t\tvenue: 'Global Humanitarian Technology Conference (GHTC)',\n\t\t\tauth: 'C Rishikanth, H Sekar, G Rajagopal, R Rajesh, V Vijayaraghavan',\n\t\t\tdate: 2014\n\t\t}\n\t];\n</script>\n\n<div class=\"flex w-[756px] flex-col justify-start gap-8\">\n\t{#each pubs as pub (pub.title)}\n\t\t<div class=\"flex flex-col\">\n\t\t\t<div class=\"flex gap-2 text-lg text-gray-50\">\n\t\t\t\t{pub.title}\n\n\t\t\t\t{#if pub.award}\n\t\t\t\t\t<div class=\"flex flex-shrink-0 items-center gap-1\">\n\t\t\t\t\t\t<Award size=\"20\" starColor=\"#0f62fe\" />\n\t\t\t\t\t\t<div class=\"text-sm\">{pub.award}</div>\n\t\t\t\t\t</div>\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t\t<div class=\"text-sm\">{pub.auth}</div>\n\t\t\t<div class=\"text-xs italic text-blue-500\">{pub.venue}</div>\n\t\t</div>\n\t{/each}\n</div>\n"
  },
  {
    "path": "src/routes/tags/[tag]/+page.server.ts",
    "content": "import PocketBase from 'pocketbase';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\nimport type { PageServerLoad } from './$types';\n\nexport const load: PageServerLoad = async ({ params }) => {\n\ttry {\n\t\tconst pb = await getAuthenticatedPocketBase();\n\t\tconst record = await pb.collection('tags').getFirstListItem(`tag=\"${params.tag}\"`, {\n\t\t\texpand: 'links'\n\t\t});\n\t\tconsole.log('TAG =====>', record);\n\n\t\t// Extract the expanded 'links' data\n\t\tconst posts =\n\t\t\trecord.expand?.links?.map((link: any) => ({\n\t\t\t\tid: link.id,\n\t\t\t\ttitle: link.title,\n\t\t\t\turl: link.url\n\t\t\t})) || [];\n\n\t\treturn {\n\t\t\ttag: record.tag,\n\t\t\tposts: posts,\n\t\t\terror: null\n\t\t};\n\t} catch (error) {\n\t\tconsole.error('Error fetching tag data:', error);\n\t\treturn {\n\t\t\ttag: params.tag,\n\t\t\tposts: [],\n\t\t\terror: error instanceof Error ? error.message : 'An unknown error occurred'\n\t\t};\n\t}\n};\n"
  },
  {
    "path": "src/routes/tags/[tag]/+page.svelte",
    "content": "<script lang=\"ts\">\n\texport let data;\n</script>\n\n<div class=\"p-2 text-6xl font-bold lg:pt-6\">\n\tTag: {data.tag}\n</div>\n\n<div class=\"my-8 p-2\">\n\t<div class=\"text-2xl\">Posts tagged with {data.tag}</div>\n\t<div class=\"items-left mt-4 flex flex-col gap-3\">\n\t\t{#each data.posts as post}\n\t\t\t<div class=\"text-lg\"><a href=\"/{post.url}\">{post.title}</a></div>\n\t\t{/each}\n\t</div>\n</div>\n"
  },
  {
    "path": "src/writing/+page.server.ts",
    "content": "import PocketBase from 'pocketbase';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nconst pb = await getAuthenticatedPocketBase();\n\nexport async function load({ params }) {\n\ttry {\n\t\tconst mdbase = await pb.collections.getOne('mdbase');\n\n\t\tconst records = await pb.collection('mdbase').getList(1, 10, { sort: '-created' });\n\t\tconst posts = Object.values(records.items).map((item) => ({\n\t\t\ttitle: item.title,\n\t\t\tid: item.id,\n\t\t\tdate: item.created,\n\t\t\turl: item.url\n\t\t}));\n\t\treturn { posts };\n\t} catch (error) {\n\t\treturn { posts: [], err: error };\n\t}\n}\n"
  },
  {
    "path": "src/writing/+page.svelte",
    "content": "<script>\n\texport let data;\n\n\tfunction formatDate(dateString) {\n\t\tconst date = new Date(dateString);\n\t\t// Format the date as DD MMM YYYY\n\t\treturn date.toLocaleDateString('en-GB', {\n\t\t\tday: '2-digit',\n\t\t\tmonth: 'short',\n\t\t\tyear: 'numeric'\n\t\t});\n\t}\n</script>\n\n<div class=\"mb-6 text-4xl font-bold\">Latest</div>\n<div class=\"flex flex-col items-start justify-center gap-4\">\n\t{#if data && data.posts.length > 0}\n\t\t{#each data.posts as p, id (id)}\n\t\t\t<div class=\"flex flex-col items-start justify-center gap-0.5\">\n\t\t\t\t<div class=\"text-xl\"><a href=\"/writing/{p.url}\">{p.title}</a></div>\n\t\t\t\t<div class=\"text-sm text-gray-400\">{formatDate(p.date)}</div>\n\t\t\t</div>\n\t\t{/each}\n\t{:else}\n\t\t<div class=\"text-lg\">Please upload markdown files to begin</div>\n\t{/if}\n</div>\n"
  },
  {
    "path": "src/writing/[...dir]/+page.server.ts",
    "content": ""
  },
  {
    "path": "src/writing/[...dir]/+page.svelte",
    "content": "<div>This is a directory</div>\n"
  },
  {
    "path": "src/writing/[...post].md/+page.server.ts",
    "content": "import { json } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\nimport PocketBase from 'pocketbase';\nimport { promises as fs } from 'fs';\nimport { getAuthenticatedPocketBase } from '$lib/server/auth';\n\nconst pb = await getAuthenticatedPocketBase();\n\nasync function getBacklinks(url) {\n\tconst mdbaseCollection = pb.collection('mdbase');\n\tconst documentUrl = url;\n\ttry {\n\t\tif (!documentUrl) {\n\t\t\treturn new Response(JSON.stringify({ message: 'URL parameter is required' }), {\n\t\t\t\tstatus: 400\n\t\t\t});\n\t\t}\n\n\t\tconst documents = await mdbaseCollection.getList(1, 1, {\n\t\t\tfilter: `url=\"${documentUrl}\"`,\n\t\t\texpand: 'backlinks'\n\t\t});\n\n\t\tif (documents.items.length === 0) {\n\t\t\treturn new Response(JSON.stringify({ message: 'Document not found' }), { status: 404 });\n\t\t}\n\n\t\tconst document = documents.items[0];\n\n\t\tconst backLinks = (document.expand?.backlinks || []).map((link) => ({\n\t\t\tid: link.id,\n\t\t\ttitle: link.title,\n\t\t\turl: link.url\n\t\t}));\n\n\t\treturn backLinks;\n\t} catch (error: any) {\n\t\tconsole.error('Error in backlinks API:', error);\n\t\treturn {};\n\t}\n}\n\nasync function computeGraphData(fileUrl) {\n\tconst currentPage = await pb.collection('mdbase').getFirstListItem(`url=\"${fileUrl}\"`);\n\tconst relatedPages = await pb.collection('mdbase').getList(1, 50, {\n\t\tfilter: `id ?~ \"${currentPage.backlinks}\" || id ?~ \"${currentPage.links}\"`\n\t});\n\n\t// Use a Set to store unique node IDs\n\tconst uniqueNodeIds = new Set([currentPage.id]);\n\n\t// Create nodes array with current page\n\tconst nodes = [{ id: currentPage.id, label: currentPage.title, color: '#ff0000' }];\n\n\t// Add related pages to nodes array, avoiding duplicates\n\trelatedPages.items.forEach((p) => {\n\t\tif (!uniqueNodeIds.has(p.id)) {\n\t\t\tuniqueNodeIds.add(p.id);\n\t\t\tnodes.push({ id: p.id, label: p.title, color: '#00ff00' });\n\t\t}\n\t});\n\n\t// Create edges array\n\tconst edges = [\n\t\t...currentPage.links.map((link) => ({ from: currentPage.id, to: link })),\n\t\t...currentPage.backlinks.map((backlink) => ({ from: backlink, to: currentPage.id }))\n\t];\n\n\treturn { nodes, edges };\n}\n// Main load function\nexport async function load({ params, fetch, locals }) {\n\ttry {\n\t\t// Step 1: Authenticate\n\t\t/* console.log(pb); */\n\t\tconsole.log(params.post);\n\t\tconst post = await pb.collection('mdbase').getFirstListItem(`url=\"${params.post}.md\"`);\n\t\tconst backlinks = await getBacklinks(`${params.post}.md`);\n\t\tconst graphData = await computeGraphData(`${params.post}.md`);\n\n\t\treturn { post, title: post.title, backlinks, graphData };\n\t} catch (error) {\n\t\tconsole.error(`Failed to fetch post: ${error}`);\n\t\treturn { message: `Failed to fetch post: ${error}` };\n\t}\n}\n"
  },
  {
    "path": "src/writing/[...post].md/+page.svelte",
    "content": "<script lang=\"ts\">\n\timport MDsvexRenderer from '$lib/components/MDsvexRenderer.svelte';\n\timport MDGraph from '$lib/components/MDGraph.svelte';\n\n\texport let data: { content: string };\n\t$: content = data.post?.content;\n\t$: graphData = data.graphData;\n</script>\n\n<div class=\"mb-12 mt-6 text-wrap text-6xl md:w-[700px] md:text-8xl\">\n\t{data.title}\n</div>\n<MDsvexRenderer bind:content />\n<div>\n\t{#if data?.backlinks?.length > 0}\n\t\t<div class=\"mb-2 mt-12 text-2xl font-light\">BACKLINKS</div>\n\t{/if}\n\t{#each data?.backlinks || [] as bl (bl.id)}\n\t\t<div><a href={`/writing/${bl.url.trim()}`} class=\"text-large\">{bl.title}</a></div>\n\t{/each}\n</div>\n"
  },
  {
    "path": "start.sh",
    "content": "#!/bin/sh\n\n# Ensure the environment variables are correctly set\necho \"Creating admin with email: ${POCKETBASE_ADMIN_EMAIL}\"\necho \"PocketBase URL: ${POCKETBASE_URL}\"\n\n# Start PocketBase in the background\n/pb/pocketbase serve --http=0.0.0.0:8080 --dir /app/db &\n\n# Wait for PocketBase to start (adjust the sleep time if necessary)\nsleep 5\n\n# Create the admin user using environment variables\n/pb/pocketbase admin create \"${POCKETBASE_ADMIN_EMAIL}\" \"${POCKETBASE_ADMIN_PASSWORD}\" --dir /app/db\n\nsleep 2\n\n# Build the SvelteKit app (requires PocketBase to be running)\nnpm run build\n\n# # Optional: Log environment variables for debugging\n# echo \"PocketBase URL: ${POCKETBASE_URL}\"\n# echo \"API Key: ${API_KEY}\"\n# echo \"Title: ${TITLE}\"\n"
  },
  {
    "path": "start_services.sh",
    "content": "#!/bin/sh\n\n# Ensure the environment variables are correctly set\necho \"Creating admin with email: ${POCKETBASE_ADMIN_EMAIL}\"\necho \"PocketBase URL: ${POCKETBASE_URL}\"\n\n# Start PocketBase in the background\n/pb/pocketbase serve --http=0.0.0.0:8080 --dir /app/db &\n\n# Wait for PocketBase to start (adjust the sleep time if necessary)\nsleep 5\n\n# Create the admin user using environment variables\n/pb/pocketbase admin create \"${POCKETBASE_ADMIN_EMAIL}\" \"${POCKETBASE_ADMIN_PASSWORD}\" --dir /app/db\n\nsleep 2\n\nnpm run build\n\n# Build the SvelteKit app (requires PocketBase to be running)\nnode build\n\n# # Optional: Log environment variables for debugging\n# echo \"PocketBase URL: ${POCKETBASE_URL}\"\n# echo \"API Key: ${API_KEY}\"\n# echo \"Title: ${TITLE}\"\n"
  },
  {
    "path": "static/fonts.css",
    "content": "@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Bold.ttf') format('truetype');\n\tfont-weight: bold;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-BoldItalic.ttf') format('truetype');\n\tfont-weight: bold;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-ExtraLight.ttf') format('truetype');\n\tfont-weight: 200;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-ExtraLightItalic.ttf') format('truetype');\n\tfont-weight: 200;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Italic.ttf') format('truetype');\n\tfont-weight: normal;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Light.ttf') format('truetype');\n\tfont-weight: 300;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-LightItalic.ttf') format('truetype');\n\tfont-weight: 300;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Medium.ttf') format('truetype');\n\tfont-weight: 500;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-MediumItalic.ttf') format('truetype');\n\tfont-weight: 500;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Regular.ttf') format('truetype');\n\tfont-weight: normal;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-SemiBold.ttf') format('truetype');\n\tfont-weight: 600;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-SemiBoldItalic.ttf') format('truetype');\n\tfont-weight: 600;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Text.ttf') format('truetype');\n\tfont-weight: 400;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-TextItalic.ttf') format('truetype');\n\tfont-weight: 400;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-Thin.ttf') format('truetype');\n\tfont-weight: 100;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Sans';\n\tsrc: url('/fonts/IBMPlexSans-ThinItalic.ttf') format('truetype');\n\tfont-weight: 100;\n\tfont-style: italic;\n}\n\n@font-face {\n\tfont-family: 'Megrim';\n\tsrc: url('/fonts/Megrim-Regular.ttf') format('truetype');\n\tfont-weight: normal;\n\tfont-style: normal;\n}\n\n/* ---- IBM Plex Mono ---- */\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 700;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-Bold.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 700;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-BoldItalic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 200;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-ExtraLight.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 200;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-ExtraLightItalic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 400;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-Italic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 300;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-Light.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 300;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-LightItalic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 500;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-Medium.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 500;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-MediumItalic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 400;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-Regular.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 600;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-SemiBold.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 600;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-SemiBoldItalic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 400;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-Text.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 400;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-TextItalic.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 100;\n\tfont-style: normal;\n\tsrc: url('/fonts/IBMPlexMono-Thin.ttf') format('truetype');\n}\n\n@font-face {\n\tfont-family: 'IBM Plex Mono';\n\tfont-weight: 100;\n\tfont-style: italic;\n\tsrc: url('/fonts/IBMPlexMono-ThinItalic.ttf') format('truetype');\n}\n\n/* Lombok */\n@font-face {\n\tfont-family: 'Lombok';\n\tfont-weight: 400;\n\tfont-style: normal;\n\tsrc: url('/fonts/Lombok.otf') format('opentype');\n}\n"
  },
  {
    "path": "supervisord.conf",
    "content": "[supervisord]\nnodaemon=true\n\n[program:pocketbase]\ncommand=/pb/pocketbase serve --http=0.0.0.0:8090 --dev --dir /app/db\nstdout_logfile=/dev/stdout\nstdout_logfile_maxbytes=0\nstderr_logfile=/dev/stderr\nstderr_logfile_maxbytes=0\n\n[program:node]\ncommand=/usr/local/bin/node build\nstdout_logfile=/dev/stdout\nstdout_logfile_maxbytes=0\nstderr_logfile=/dev/stderr\nstderr_logfile_maxbytes=0\n"
  },
  {
    "path": "svelte.config.js",
    "content": "/* import adapter from '@sveltejs/adapter-auto'; */\nimport adapter from '@sveltejs/adapter-node';\n\nimport { vitePreprocess } from '@sveltejs/vite-plugin-svelte';\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n\t// Consult https://kit.svelte.dev/docs/integrations#preprocessors\n\t// for more information about preprocessors\n\tpreprocess: vitePreprocess(),\n\n\tkit: {\n\t\t// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.\n\t\t// If your environment is not supported, or you settled on a specific environment, switch out the adapter.\n\t\t// See https://kit.svelte.dev/docs/adapters for more information about adapters.\n\t\tadapter: adapter(),\n\t\tcsrf: {\n      checkOrigin: false,\n    }\n\t}\n};\n\nexport default config;\n"
  },
  {
    "path": "tailwind.config.ts",
    "content": "import { fontFamily } from 'tailwindcss/defaultTheme';\nimport type { Config } from 'tailwindcss';\n\nconst config: Config = {\n\tdarkMode: ['class'],\n\tcontent: ['./src/**/*.{html,js,svelte,ts}'],\n\tsafelist: ['dark'],\n\ttheme: {\n\t\tfontFamily: {\n\t\t\tsans: ['IBM Plex Sans'],\n\t\t\tbody: ['IBM Plex Sans'],\n\t\t\tdisplay: ['IBM Plex Sans'],\n\t\t\tserif: ['IBM Plex Serif'],\n\t\t\tmono: ['IBM Plex Mono']\n\t\t},\n\t\tcontainer: {\n\t\t\tcenter: true,\n\t\t\tpadding: '2rem',\n\t\t\tscreens: {\n\t\t\t\t'2xl': '1400px'\n\t\t\t}\n\t\t},\n\t\textend: {\n\t\t\tcolors: {\n\t\t\t\tcarbongray: {\n\t\t\t\t\t50: '#f4f4f4',\n\t\t\t\t\t100: '#e0e0e0',\n\t\t\t\t\t200: '#c6c6c6',\n\t\t\t\t\t300: '#a8a8a8',\n\t\t\t\t\t400: '#8d8d8d',\n\t\t\t\t\t500: '#6f6f6f',\n\t\t\t\t\t600: '#525252',\n\t\t\t\t\t700: '#262626',\n\t\t\t\t\t800: '#161616',\n\t\t\t\t\t900: '#000000'\n\t\t\t\t},\n\t\t\t\tcarbonblue: {\n\t\t\t\t\t50: '#ecf5ff',\n\t\t\t\t\t100: '#d0e2ff',\n\t\t\t\t\t200: '#a6c8ff',\n\t\t\t\t\t300: '#77a9fe',\n\t\t\t\t\t400: '#4589ff',\n\t\t\t\t\t500: '#0e61fe',\n\t\t\t\t\t600: '#0043ce',\n\t\t\t\t\t700: '#012d9c',\n\t\t\t\t\t800: '#001d6c',\n\t\t\t\t\t900: '#001141'\n\t\t\t\t},\n\t\t\t\tcarbonborder: {\n\t\t\t\t\t300: '#393939',\n\t\t\t\t\t200: '#525252',\n\t\t\t\t\t100: '#6f6f6f'\n\t\t\t\t},\n\t\t\t\tborder: 'hsl(var(--border) / <alpha-value>)',\n\t\t\t\tinput: 'hsl(var(--input) / <alpha-value>)',\n\t\t\t\tring: 'hsl(var(--ring) / <alpha-value>)',\n\t\t\t\tbackground: 'hsl(var(--background) / <alpha-value>)',\n\t\t\t\tforeground: 'hsl(var(--foreground) / <alpha-value>)',\n\t\t\t\tprimary: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--primary) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--primary-foreground) / <alpha-value>)'\n\t\t\t\t},\n\t\t\t\tsecondary: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--secondary) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--secondary-foreground) / <alpha-value>)'\n\t\t\t\t},\n\t\t\t\tdestructive: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--destructive) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--destructive-foreground) / <alpha-value>)'\n\t\t\t\t},\n\t\t\t\tmuted: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--muted) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--muted-foreground) / <alpha-value>)'\n\t\t\t\t},\n\t\t\t\taccent: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--accent) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--accent-foreground) / <alpha-value>)'\n\t\t\t\t},\n\t\t\t\tpopover: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--popover) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--popover-foreground) / <alpha-value>)'\n\t\t\t\t},\n\t\t\t\tcard: {\n\t\t\t\t\tDEFAULT: 'hsl(var(--card) / <alpha-value>)',\n\t\t\t\t\tforeground: 'hsl(var(--card-foreground) / <alpha-value>)'\n\t\t\t\t}\n\t\t\t},\n\t\t\tborderRadius: {\n\t\t\t\tlg: 'var(--radius)',\n\t\t\t\tmd: 'calc(var(--radius) - 2px)',\n\t\t\t\tsm: 'calc(var(--radius) - 4px)'\n\t\t\t},\n\t\t\tfontFamily: {\n\t\t\t\tsans: [...fontFamily.sans]\n\t\t\t}\n\t\t}\n\t}\n};\n\nexport default config;\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n\t\"extends\": \"./.svelte-kit/tsconfig.json\",\n\t\"compilerOptions\": {\n\t\t\"allowJs\": true,\n\t\t\"checkJs\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"sourceMap\": true,\n\t\t\"strict\": true,\n\t\t\"moduleResolution\": \"bundler\"\n\t}\n\t// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias\n\t// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files\n\t//\n\t// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes\n\t// from the referenced tsconfig.json - TypeScript does not merge them in\n}\n"
  },
  {
    "path": "vite.config.ts",
    "content": "import { sveltekit } from '@sveltejs/kit/vite';\nimport { defineConfig } from 'vite';\n\nexport default defineConfig({\n\tplugins: [sveltekit()]\n});\n"
  }
]