[
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\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# UV\n#   Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.\n#   This is especially recommended for binary packages to ensure reproducibility, and is more\n#   commonly ignored for libraries.\n#uv.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\n# Ruff stuff:\n.ruff_cache/\n\n# PyPI configuration file\n.pypirc\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 Oleh Kostromin\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": "MANIFEST.in",
    "content": "include LICENSE\ninclude README.md\ninclude pyproject.toml\n\n# Include all files from the kernelspec and labextension directories\n# Use the correct relative paths\nrecursive-include juvio/kernelspec *\nrecursive-include juvio/labextension *\n\n# Exclude unnecessary files\nglobal-exclude *.py[cod]\nglobal-exclude __pycache__\nglobal-exclude *.so\nglobal-exclude .git*\nglobal-exclude .ipynb_checkpoints\nglobal-exclude node_modules"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <img alt=\"logo\" src=\"https://gist.githubusercontent.com/OKUA1/d6e65e883546021ea774857878fd0537/raw/4de2ea217e25d9ff7b3d2a73899e85665ed7d94c/juvio_logo.svg\" height = \"250\">\n</div>\n\n\n# **Juvio**: reproducible, dependency-aware, and Git-friendly Jupyter Notebooks.\n\n## 🚀 What It Does\n\n- 💡 **Inline Dependency Management**\n\n  Install packages right from the notebook:\n\n  ```python\n  %juvio install numpy pandas\n  ```\n\n  Dependencies are saved directly in the notebook as metadata (PEP 723-style), like:\n  ```python\n  # /// script\n  # requires-python = \"==3.10.17\"\n  # dependencies = [\n  # \"numpy==2.2.5\",\n  # \"pandas==2.2.3\"\n  # ]\n  # ///\n  ```\n\n- ⚙️ **Automatic Environment Setup**\n\n    When the notebook is opened, Juvio installs the dependencies automatically in an ephemeral virtual environment (using `uv`), ensuring that the notebook runs with the correct versions of the packages and Python.\n\n- 📁 **Git-Friendly Format**\n\n    Notebooks are converted on the fly to a script-style format using `# %%` markers, making diffs and version control painless:\n    ```python\n    # %%\n    %juvio install numpy\n    # %%\n    import numpy as np\n    # %%\n    arr = np.array([1, 2, 3])\n    print(arr)\n    # %%\n    ```\n\n## 🧑‍💻 How to Use\n\n> [!WARNING]\n> This project is currently in **early beta**. It may contain bugs and is subject to change.\n> Please [open an issue](../../issues) to report problems or suggest improvements.\n\n\n**1. Install Juvio:**\n\n```bash\npip install juvio\njupyter labextension enable juvio-frontend\n```\n\n**2. Make sure you have uv installed:**\n\nhttps://docs.astral.sh/uv/getting-started/installation/\n\n**3. Start JupyterLab and create a Juvio Notebook.**\n \n**4. Install necessary packages in the notebook and run your code**\n\n```python\n  %juvio install ...\n  ```\nDependencies are tracked, environments are reproducible, and your notebook stays Git-clean ✨\n\n**Known issue:** If you experience the error \"Notebook does not appear to be JSON\", try to lauch the jupyterlab with an additional argument:\n\n```bash\njupyter lab --ServerApp.jpserver_extensions=\"{'juvio': True}\"\n```\n\n## Why Use Juvio?\n\n- No additional lock or requirements files are needed\n- Guaranteed reproducibility\n- Cleaner Git diffs\n\n## Powered By\n\n- `uv` – ultra-fast Python package management\n- `PEP 723` – Python inline dependency standards\n- `jupytext`-like format for easy version control\n\n\n"
  },
  {
    "path": "juvio/__init__.py",
    "content": "from .server_extension import _load_jupyter_server_extension\n\ndef _jupyter_server_extension_points():\n    return [{\"module\": \"juvio.server_extension\"}]\n\n# def _jupyter_labextension_paths():\n#     return [\n#         {\n#             \"src\": \"labextension\",\n#             \"dest\": \"juvio\",\n#         }\n#     ]"
  },
  {
    "path": "juvio/content_manager.py",
    "content": "import nbformat\nimport inspect\nimport aiofiles\nfrom juvio.converter import JuvioConverter\nfrom juvio.shared import FileLock, AsyncFileLock\nimport os\n\n\ndef _is_juvio_file(path):\n    return path.endswith(\".juvio\")\n\n\ndef juvio_get_sync(self, path, content=True, type=None, format=None, **kwargs):\n    api_path = path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    full_path = self._get_os_path(api_path)\n\n    if not _is_juvio_file(api_path):\n        return self._original_get(\n            api_path, content=content, type=type, format=format, **kwargs\n        )\n\n    model = self._original_get(api_path, content=False, **kwargs)\n\n    if not content:\n        model[\"type\"] = \"notebook\"\n        return model\n\n    try:\n        with FileLock(full_path):\n            with open(full_path, \"r\", encoding=\"utf-8\") as f:\n                text = f.read()\n        nb = JuvioConverter.convert_script_to_notebook(text)\n        nbformat.validate(nb)\n        model.update(type=\"notebook\", format=\"json\", content=nb)\n        return model\n    except Exception as e:\n        from tornado.web import HTTPError\n\n        raise HTTPError(500, f\"Error while reading {api_path}: {e}\")\n\n\ndef juvio_save_sync(self, model, path, **kwargs):\n    from datetime import datetime\n\n    def format_timestamp(timestamp):\n        return datetime.utcfromtimestamp(timestamp).strftime(\"%Y-%m-%dT%H:%M:%S.%fZ\")\n\n    model_type = model.get(\"type\")\n    model_content = model.get(\"content\")\n\n    api_path = path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    full_path = self._get_os_path(api_path)\n    os.makedirs(os.path.dirname(full_path), exist_ok=True)\n\n    if not _is_juvio_file(api_path) or model_type != \"notebook\":\n        return self._original_save(model, api_path, **kwargs)\n\n    try:\n        with FileLock(full_path):\n            pass  # ensures lock before opening\n\n        if model_content is not None:\n            if isinstance(model_content, dict):\n                nb = nbformat.from_dict(model_content)\n            else:\n                nb = model_content\n\n            nbformat.validate(nb)\n            with open(full_path, \"r\", encoding=\"utf-8\") as f:\n                existing = f.read()\n\n            metadata = JuvioConverter._generate_metadata(\n                **JuvioConverter.extract_metadata(existing)\n            )\n            text = JuvioConverter.convert_notebook_to_script(nb, metadata)\n\n            with open(full_path, \"w\", encoding=\"utf-8\") as f:\n                f.write(text)\n        else:\n            with open(full_path, \"w\", encoding=\"utf-8\") as f:\n                f.write(JuvioConverter.create())\n\n        stat = os.stat(full_path)\n\n        return {\n            \"name\": os.path.basename(api_path),\n            \"path\": api_path,\n            \"type\": \"notebook\",\n            \"format\": None,\n            \"content\": None,\n            \"created\": format_timestamp(stat.st_ctime),\n            \"last_modified\": format_timestamp(stat.st_mtime),\n            \"writable\": True,\n            \"mimetype\": None,\n        }\n    except Exception as e:\n        from tornado.web import HTTPError\n\n        raise HTTPError(500, f\"Error while saving {api_path}: {e}\")\n\n\nasync def juvio_get_async(self, path, content=True, type=None, format=None, **kwargs):\n    api_path = path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    full_path = self._get_os_path(api_path)\n\n    if not _is_juvio_file(api_path):\n        return await self._original_get(\n            api_path, content=content, type=type, format=format, **kwargs\n        )\n\n    model = await self._original_get(api_path, content=False, **kwargs)\n\n    if not content:\n        model[\"type\"] = \"notebook\"\n        return model\n\n    try:\n        async with AsyncFileLock(full_path):\n            pass  # ensures lock before opening\n\n        async with aiofiles.open(full_path, \"r\", encoding=\"utf-8\") as f:\n            text = await f.read()\n\n        nb = JuvioConverter.convert_script_to_notebook(text)\n        nbformat.validate(nb)\n        model.update(type=\"notebook\", format=\"json\", content=nb)\n        return model\n    except Exception as e:\n        from tornado.web import HTTPError\n\n        raise HTTPError(500, f\"Error while reading {api_path}: {e}\")\n\n\nasync def juvio_save_async(self, model, path, **kwargs):\n    from datetime import datetime\n\n    def format_timestamp(timestamp):\n        return datetime.utcfromtimestamp(timestamp).strftime(\"%Y-%m-%dT%H:%M:%S.%fZ\")\n\n    model_type = model.get(\"type\")\n    model_content = model.get(\"content\")\n\n    api_path = path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    full_path = self._get_os_path(api_path)\n    os.makedirs(os.path.dirname(full_path), exist_ok=True)\n\n    if not _is_juvio_file(api_path) or model_type != \"notebook\":\n        return await self._original_save(model, api_path, **kwargs)\n\n    try:\n        async with AsyncFileLock(full_path):\n            pass  # ensures lock before opening\n\n        if model_content is not None:\n            if isinstance(model_content, dict):\n                nb = nbformat.from_dict(model_content)\n            else:\n                nb = model_content\n\n            nbformat.validate(nb)\n            async with aiofiles.open(full_path, \"r\", encoding=\"utf-8\") as f:\n                existing = await f.read()\n\n            metadata = JuvioConverter._generate_metadata(\n                **JuvioConverter.extract_metadata(existing)\n            )\n            text = JuvioConverter.convert_notebook_to_script(nb, metadata)\n\n            async with aiofiles.open(full_path, \"w\", encoding=\"utf-8\") as f:\n                await f.write(text)\n        else:\n            async with aiofiles.open(full_path, \"w\", encoding=\"utf-8\") as f:\n                await f.write(JuvioConverter.create())\n\n        stat = os.stat(full_path)\n\n        return {\n            \"name\": os.path.basename(api_path),\n            \"path\": api_path,\n            \"type\": \"notebook\",\n            \"format\": None,\n            \"content\": None,\n            \"created\": format_timestamp(stat.st_ctime),\n            \"last_modified\": format_timestamp(stat.st_mtime),\n            \"writable\": True,\n            \"mimetype\": None,\n        }\n    except Exception as e:\n        from tornado.web import HTTPError\n\n        raise HTTPError(500, f\"Error while saving {api_path}: {e}\")\n\n\ndef juvio_rename_file(self, old_path, new_path):\n    old_api_path = old_path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    new_api_path = new_path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    full_path = self._get_os_path(new_api_path)\n\n    result = self._original_rename_file(old_api_path, new_api_path)\n\n    if _is_juvio_file(new_api_path):\n        with FileLock(full_path):\n            if os.path.getsize(full_path) == 0:\n                with open(full_path, \"w\", encoding=\"utf-8\") as f:\n                    f.write(JuvioConverter.create())\n\n    return result\n\n\nasync def rename_file(self, old_path, new_path):\n    old_api_path = old_path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    new_api_path = new_path.replace(\"\\\\\", \"/\").lstrip(\"/\")\n    full_path = self._get_os_path(new_api_path)\n\n    result = await self._original_rename_file(old_api_path, new_api_path)\n\n    async with AsyncFileLock(full_path):\n        if os.path.getsize(full_path) == 0:\n            async with aiofiles.open(full_path, \"w\", encoding=\"utf-8\") as f:\n                await f.write(JuvioConverter.create())\n\n    return result\n\n\ndef create_juvio_contents_manager_class(base_manager_class):\n    is_async = inspect.iscoroutinefunction(base_manager_class.get)\n    if is_async:\n\n        class AsyncJuvioContentsManager(base_manager_class):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                self._original_get = super().get\n                self._original_save = super().save\n                self._original_rename_file = super().rename_file\n\n            async def get(self, path, content=True, type=None, format=None, **kwargs):\n                return await juvio_get_async(\n                    self, path, content=content, type=type, format=format, **kwargs\n                )\n\n            async def save(self, model, path, **kwargs):\n                return await juvio_save_async(self, model, path, **kwargs)\n\n            async def rename_file(self, old_path, new_path):\n                return await rename_file(self, old_path, new_path)\n\n        return AsyncJuvioContentsManager\n\n    else:\n\n        class JuvioContentsManager(base_manager_class):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                self._original_get = super().get\n                self._original_save = super().save\n                self._original_rename_file = super().rename_file\n\n            def get(self, path, content=True, type=None, format=None, **kwargs):\n                return juvio_get_sync(\n                    self, path, content=content, type=type, format=format, **kwargs\n                )\n\n            def save(self, model, path, **kwargs):\n                return juvio_save_sync(self, model, path, **kwargs)\n\n            def rename_file(self, old_path, new_path):\n                return juvio_rename_file(self, old_path, new_path)\n\n        return JuvioContentsManager\n"
  },
  {
    "path": "juvio/converter.py",
    "content": "import sys\nimport nbformat\n\nif sys.version_info >= (3, 11):\n    import tomllib\nelse:\n    raise ImportError(\"Python 3.11+ is required for tomllib support.\")\n\n\nclass JuvioConverter:\n    @staticmethod\n    def _parse_metadata(metadata_lines):\n        toml_text = \"\\n\".join(metadata_lines)\n        data = tomllib.loads(toml_text)\n\n        if \"requires-python\" in data:\n            version = data[\"requires-python\"]\n            # Clean up operators like >=, ~=, etc.\n            for prefix in (\">=\", \"~=\", \"==\", \">\", \"<=\", \"<\"):\n                if version.startswith(prefix):\n                    version = version[len(prefix) :].strip()\n                    break\n            data[\"python_version\"] = version\n            data.pop(\"requires-python\", None)\n        return data\n\n    @staticmethod\n    def _generate_metadata(python_version, dependencies):\n        lines = []\n        lines.append(f'requires-python = \"=={python_version}\"')\n        if dependencies:\n            lines.append(\"dependencies = [\")\n            for dep in dependencies:\n                lines.append(f'  \"{dep}\",')\n            lines.append(\"]\")\n        else:\n            lines.append(\"dependencies = []\")\n        return lines\n\n    @staticmethod\n    def convert_script_to_notebook(text: str):\n        nb = nbformat.v4.new_notebook()\n        nb.metadata.kernelspec = {\n            \"name\": \"juvio\",\n            \"language\": \"python\",\n            \"display_name\": \"Juvio\",\n        }\n        nb.metadata.language_info = {\n            \"name\": \"python\",\n            \"version\": \"3.10\",\n            \"mimetype\": \"text/x-python\",\n            \"codemirror_mode\": {\"name\": \"ipython\", \"version\": 3},\n            \"pygments_lexer\": \"ipython3\",\n            \"nbconvert_exporter\": \"python\",\n            \"file_extension\": \".py\",\n        }\n\n        lines = text.splitlines()\n        metadata_lines = []\n        body_lines = []\n        in_metadata = False\n\n        for line in lines:\n            if line.startswith(\"# ///\"):\n                in_metadata = not in_metadata\n            elif in_metadata:\n                if line.strip().startswith(\"#\"):\n                    metadata_lines.append(line.strip(\"# \").rstrip())\n            else:\n                body_lines.append(line)\n\n        lines = body_lines\n        cells = []\n        cell_source = []\n        cell_type = \"code\"\n\n        for line in lines:\n            if line.startswith(\"# %%\"):\n                if cell_source:\n                    if cell_type == \"markdown\":\n                        cleaned = [\n                            l[2:] if l.startswith(\"# \") else l.lstrip(\"#\")\n                            for l in cell_source\n                        ]\n                        cells.append(nbformat.v4.new_markdown_cell(\"\\n\".join(cleaned)))\n                    else:\n                        cells.append(nbformat.v4.new_code_cell(\"\\n\".join(cell_source)))\n                    cell_source = []\n\n                if \"markdown\" in line.lower():\n                    cell_type = \"markdown\"\n                else:\n                    cell_type = \"code\"\n            else:\n                cell_source.append(line)\n\n        if cell_source:\n            if cell_type == \"markdown\":\n                cleaned = [\n                    l[2:] if l.startswith(\"# \") else l.lstrip(\"#\") for l in cell_source\n                ]\n                cells.append(nbformat.v4.new_markdown_cell(\"\\n\".join(cleaned)))\n            else:\n                cells.append(nbformat.v4.new_code_cell(\"\\n\".join(cell_source)))\n\n        if not cells:\n            cells.append(nbformat.v4.new_code_cell(\"\"))\n\n        nb.cells = cells\n        return nb\n\n    @staticmethod\n    def convert_notebook_to_script(nb, dep_metadata: list[str] | None = None):\n        dep_metadata = dep_metadata or []\n        lines = []\n\n        if dep_metadata:\n            lines.append(\"# /// script\")\n            for line in dep_metadata:\n                lines.append(\"# \" + line)\n            lines.append(\"# ///\")\n\n        for cell in nb.cells:\n            if cell.cell_type == \"code\":\n                lines.append(\"# %%\")\n                lines.append(cell.source.rstrip() if cell.source else \"\")\n            elif cell.cell_type == \"markdown\":\n                lines.append(\"# %% markdown\")\n                if cell.source:\n                    for line in cell.source.rstrip().splitlines():\n                        lines.append(\n                            \"# \" + line if not line.startswith(\"#\") else \"# \" + line\n                        )\n                else:\n                    lines.append(\"\")\n\n        return \"\\n\".join(lines) + \"\\n\"\n\n    @staticmethod\n    def create(python_version=\"3.10\", dependencies=None):\n        if dependencies is None:\n            dependencies = []\n\n        lines = []\n        lines.append(\"# /// script\")\n        metadata_block = JuvioConverter._generate_metadata(python_version, dependencies)\n        for line in metadata_block:\n            lines.append(\"# \" + line)\n        lines.append(\"# ///\")\n        lines.append(\"# %%\")\n        lines.append(\"\")\n        return \"\\n\".join(lines) + \"\\n\"\n\n    @staticmethod\n    def extract_metadata(text: str):\n        lines = text.splitlines()\n        metadata_lines = []\n        in_metadata = False\n\n        for line in lines:\n            if line.startswith(\"# ///\"):\n                in_metadata = not in_metadata\n            elif in_metadata:\n                if line.strip().startswith(\"#\"):\n                    metadata_lines.append(line.strip(\"# \").rstrip())\n\n        if metadata_lines:\n            return JuvioConverter._parse_metadata(metadata_lines)\n        else:\n            return {\n                \"python_version\": f\"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}\",\n                \"dependencies\": [],\n            }\n"
  },
  {
    "path": "juvio/kernel_launcher.py",
    "content": "import os\nimport sys\nimport platform\nfrom juvio.converter import JuvioConverter\n\n\ncurrent_path = os.path.dirname(os.path.abspath(__file__))\n\nshared_path = os.path.join(current_path, \"shared.py\")\n\n\ndef cmd(\n    python_version=None,\n    deps=None,\n):\n    head = [\n        \"uv\",\n        \"run\",\n        \"--no-project\",\n        \"--isolated\",\n        \"--exact\",\n        \"--with\",\n        \"ipykernel\",\n    ]\n\n    head.append(\"--python\")\n    if python_version is not None:\n        head.append(python_version)\n    else:\n        head.append(f\"{sys.version_info.major}.{sys.version_info.minor}\")\n    if deps is not None and len(deps) > 0:\n        for dep in deps:\n            head.append(\"--with\")\n            head.append(f\"{dep}\")\n    else:\n        head.append(\"-n\")\n\n    head.extend([\"--\", \"python\", shared_path])\n\n    head += sys.argv[1:]\n    return head\n\n\ndef main():\n    notebook_path = os.environ.get(\"JPY_SESSION_NAME\", None)\n\n    if notebook_path is None:\n        print(\"No notebook path found in environment variable JPY_SESSION_NAME.\")\n        sys.exit(1)\n\n    with open(notebook_path, \"r\", encoding=\"utf-8\") as f:\n        text = f.read()\n\n    metadata = JuvioConverter.extract_metadata(text)\n\n    if platform.system() == \"Windows\":\n        import subprocess\n\n        proc = subprocess.Popen(\n            cmd(\n                python_version=metadata.get(\"python_version\", None),\n                deps=metadata.get(\"dependencies\", []),\n            )\n        )\n        proc.wait()\n        sys.exit(proc.returncode)\n    else:\n        os.execvp(\n            \"uv\",\n            cmd(\n                python_version=metadata.get(\"python_version\", None),\n                deps=metadata.get(\"dependencies\", []),\n            ),\n        )\n"
  },
  {
    "path": "juvio/kernelspec/kernel.json",
    "content": "{\n    \"argv\": [\n      \"juvio-kernel-launcher\",\n      \"-f\",\n      \"{connection_file}\"\n    ],\n    \"name\": \"juvio\",\n    \"display_name\": \"Juvio\",\n    \"language\": \"python\"\n  }\n  "
  },
  {
    "path": "juvio/labextension/package.json",
    "content": "{\n  \"name\": \"juvio_frontend\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Juvio jupyterlab extension.\",\n  \"keywords\": [\n    \"jupyter\",\n    \"jupyterlab\",\n    \"jupyterlab-extension\"\n  ],\n  \"homepage\": \"-\",\n  \"bugs\": {\n    \"url\": \"-/issues\"\n  },\n  \"license\": \"BSD-3-Clause\",\n  \"author\": {\n    \"name\": \"Oleh Kostromin\",\n    \"email\": \"oleh@beastbyte.ai\"\n  },\n  \"files\": [\n    \"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}\",\n    \"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}\"\n  ],\n  \"main\": \"lib/index.js\",\n  \"types\": \"lib/index.d.ts\",\n  \"style\": \"style/index.css\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"-.git\"\n  },\n  \"workspaces\": [\n    \"ui-tests\"\n  ],\n  \"scripts\": {\n    \"build\": \"jlpm build:lib && jlpm build:labextension:dev\",\n    \"build:prod\": \"jlpm clean && jlpm build:lib:prod && jlpm build:labextension\",\n    \"build:labextension\": \"jupyter labextension build .\",\n    \"build:labextension:dev\": \"jupyter labextension build --development True .\",\n    \"build:lib\": \"tsc --sourceMap\",\n    \"build:lib:prod\": \"tsc\",\n    \"clean\": \"jlpm clean:lib\",\n    \"clean:lib\": \"rimraf lib tsconfig.tsbuildinfo\",\n    \"clean:lintcache\": \"rimraf .eslintcache .stylelintcache\",\n    \"clean:labextension\": \"rimraf juvio_frontend/labextension juvio_frontend/_version.py\",\n    \"clean:all\": \"jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache\",\n    \"eslint\": \"jlpm eslint:check --fix\",\n    \"eslint:check\": \"eslint . --cache --ext .ts,.tsx\",\n    \"install:extension\": \"jlpm build\",\n    \"lint\": \"jlpm stylelint && jlpm prettier && jlpm eslint\",\n    \"lint:check\": \"jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check\",\n    \"prettier\": \"jlpm prettier:base --write --list-different\",\n    \"prettier:base\": \"prettier \\\"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\\\"\",\n    \"prettier:check\": \"jlpm prettier:base --check\",\n    \"stylelint\": \"jlpm stylelint:check --fix\",\n    \"stylelint:check\": \"stylelint --cache \\\"style/**/*.css\\\"\",\n    \"test\": \"jest --coverage\",\n    \"watch\": \"run-p watch:src watch:labextension\",\n    \"watch:src\": \"tsc -w --sourceMap\",\n    \"watch:labextension\": \"jupyter labextension watch .\"\n  },\n  \"dependencies\": {\n    \"@jupyterlab/application\": \"^4.0.0\",\n    \"@jupyterlab/launcher\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@jupyterlab/builder\": \"^4.0.0\",\n    \"@jupyterlab/testutils\": \"^4.0.0\",\n    \"@types/jest\": \"^29.2.0\",\n    \"@types/json-schema\": \"^7.0.11\",\n    \"@types/react\": \"^18.0.26\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.55.0\",\n    \"@typescript-eslint/parser\": \"^5.55.0\",\n    \"css-loader\": \"^6.7.1\",\n    \"eslint\": \"^8.36.0\",\n    \"eslint-config-prettier\": \"^8.7.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"jest\": \"^29.2.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^2.8.7\",\n    \"rimraf\": \"^4.4.1\",\n    \"source-map-loader\": \"^1.0.2\",\n    \"style-loader\": \"^3.3.1\",\n    \"stylelint\": \"^14.9.1\",\n    \"stylelint-config-prettier\": \"^9.0.4\",\n    \"stylelint-config-recommended\": \"^8.0.0\",\n    \"stylelint-config-standard\": \"^26.0.0\",\n    \"stylelint-prettier\": \"^2.0.0\",\n    \"typescript\": \"^5.0\",\n    \"yjs\": \"^13.5.0\"\n  },\n  \"sideEffects\": [\n    \"style/*.css\",\n    \"style/index.js\"\n  ],\n  \"styleModule\": \"style/index.js\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"jupyterlab\": {\n    \"extension\": true,\n    \"outputDir\": \"build/labextension\",\n    \"_build\": {\n      \"load\": \"static/remoteEntry.cb75fcc929fa00484f36.js\",\n      \"extension\": \"./extension\",\n      \"style\": \"./style\"\n    }\n  },\n  \"eslintIgnore\": [\n    \"node_modules\",\n    \"dist\",\n    \"coverage\",\n    \"**/*.d.ts\",\n    \"tests\",\n    \"**/__tests__\",\n    \"ui-tests\"\n  ],\n  \"eslintConfig\": {\n    \"extends\": [\n      \"eslint:recommended\",\n      \"plugin:@typescript-eslint/eslint-recommended\",\n      \"plugin:@typescript-eslint/recommended\",\n      \"plugin:prettier/recommended\"\n    ],\n    \"parser\": \"@typescript-eslint/parser\",\n    \"parserOptions\": {\n      \"project\": \"tsconfig.json\",\n      \"sourceType\": \"module\"\n    },\n    \"plugins\": [\n      \"@typescript-eslint\"\n    ],\n    \"rules\": {\n      \"@typescript-eslint/naming-convention\": [\n        \"error\",\n        {\n          \"selector\": \"interface\",\n          \"format\": [\n            \"PascalCase\"\n          ],\n          \"custom\": {\n            \"regex\": \"^I[A-Z]\",\n            \"match\": true\n          }\n        }\n      ],\n      \"@typescript-eslint/no-unused-vars\": [\n        \"warn\",\n        {\n          \"args\": \"none\"\n        }\n      ],\n      \"@typescript-eslint/no-explicit-any\": \"off\",\n      \"@typescript-eslint/no-namespace\": \"off\",\n      \"@typescript-eslint/no-use-before-define\": \"off\",\n      \"@typescript-eslint/quotes\": [\n        \"error\",\n        \"single\",\n        {\n          \"avoidEscape\": true,\n          \"allowTemplateLiterals\": false\n        }\n      ],\n      \"curly\": [\n        \"error\",\n        \"all\"\n      ],\n      \"eqeqeq\": \"error\",\n      \"prefer-arrow-callback\": \"error\"\n    }\n  },\n  \"prettier\": {\n    \"singleQuote\": true,\n    \"trailingComma\": \"none\",\n    \"arrowParens\": \"avoid\",\n    \"endOfLine\": \"auto\"\n  },\n  \"stylelint\": {\n    \"extends\": [\n      \"stylelint-config-recommended\",\n      \"stylelint-config-standard\",\n      \"stylelint-prettier/recommended\"\n    ],\n    \"rules\": {\n      \"property-no-vendor-prefix\": null,\n      \"selector-no-vendor-prefix\": null,\n      \"value-no-vendor-prefix\": null\n    }\n  }\n}\n"
  },
  {
    "path": "juvio/labextension/static/509.8b21d569ebbb8f039bdd.js",
    "content": "\"use strict\";(self.webpackChunkjuvio_frontend=self.webpackChunkjuvio_frontend||[]).push([[509],{509:(e,o,t)=>{t.r(o),t.d(o,{default:()=>n});const n={id:\"juvio_frontend:plugin\",description:\"Juvio JupyterLab extension\",autoStart:!0,requires:[t(625).ILauncher],activate:(e,o)=>{console.log(\"JupyterLab extension juvio_frontend is activated!\"),e.docRegistry.addFileType({name:\"juvio\",displayName:\"Juvio Notebook\",extensions:[\".juvio\"],contentType:\"notebook\",fileFormat:\"json\"}),e.docRegistry.addFileType({name:\"notebook\",extensions:[\".juvio\"]});const{commands:t}=e;t.addCommand(\"create-juvio-file\",{label:\"Juvio Notebook\",execute:()=>t.execute(\"docmanager:new-untitled\",{type:\"file\",ext:\".juvio\",kernelName:\"juvio\"})}),o.add({category:\"Notebook\",rank:1,command:\"create-juvio-file\",kernelIconUrl:\"https://iili.io/3WtCOjs.th.png\"})}}}}]);"
  },
  {
    "path": "juvio/labextension/static/728.864a8d7732d3e14d284a.js",
    "content": "\"use strict\";(self.webpackChunkjuvio_frontend=self.webpackChunkjuvio_frontend||[]).push([[728],{56:(e,t,n)=>{e.exports=function(e){var t=n.nc;t&&e.setAttribute(\"nonce\",t)}},72:e=>{var t=[];function n(e){for(var n=-1,r=0;r<t.length;r++)if(t[r].identifier===e){n=r;break}return n}function r(e,r){for(var a={},i=[],s=0;s<e.length;s++){var c=e[s],u=r.base?c[0]+r.base:c[0],l=a[u]||0,p=\"\".concat(u,\" \").concat(l);a[u]=l+1;var f=n(p),d={css:c[1],media:c[2],sourceMap:c[3],supports:c[4],layer:c[5]};if(-1!==f)t[f].references++,t[f].updater(d);else{var v=o(d,r);r.byIndex=s,t.splice(s,0,{identifier:p,updater:v,references:1})}i.push(p)}return i}function o(e,t){var n=t.domAPI(t);return n.update(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap&&t.supports===e.supports&&t.layer===e.layer)return;n.update(e=t)}else n.remove()}}e.exports=function(e,o){var a=r(e=e||[],o=o||{});return function(e){e=e||[];for(var i=0;i<a.length;i++){var s=n(a[i]);t[s].references--}for(var c=r(e,o),u=0;u<a.length;u++){var l=n(a[u]);0===t[l].references&&(t[l].updater(),t.splice(l,1))}a=c}}},113:e=>{e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}},314:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=\"\",r=void 0!==t[5];return t[4]&&(n+=\"@supports (\".concat(t[4],\") {\")),t[2]&&(n+=\"@media \".concat(t[2],\" {\")),r&&(n+=\"@layer\".concat(t[5].length>0?\" \".concat(t[5]):\"\",\" {\")),n+=e(t),r&&(n+=\"}\"),t[2]&&(n+=\"}\"),t[4]&&(n+=\"}\"),n})).join(\"\")},t.i=function(e,n,r,o,a){\"string\"==typeof e&&(e=[[null,e,void 0]]);var i={};if(r)for(var s=0;s<this.length;s++){var c=this[s][0];null!=c&&(i[c]=!0)}for(var u=0;u<e.length;u++){var l=[].concat(e[u]);r&&i[l[0]]||(void 0!==a&&(void 0===l[5]||(l[1]=\"@layer\".concat(l[5].length>0?\" \".concat(l[5]):\"\",\" {\").concat(l[1],\"}\")),l[5]=a),n&&(l[2]?(l[1]=\"@media \".concat(l[2],\" {\").concat(l[1],\"}\"),l[2]=n):l[2]=n),o&&(l[4]?(l[1]=\"@supports (\".concat(l[4],\") {\").concat(l[1],\"}\"),l[4]=o):l[4]=\"\".concat(o)),t.push(l))}},t}},475:(e,t,n)=>{n.d(t,{A:()=>s});var r=n(601),o=n.n(r),a=n(314),i=n.n(a)()(o());i.push([e.id,\"/*\\n    See the JupyterLab Developer Guide for useful CSS Patterns:\\n\\n    https://jupyterlab.readthedocs.io/en/stable/developer/css.html\\n*/\\n\",\"\"]);const s=i},540:e=>{e.exports=function(e){var t=document.createElement(\"style\");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},601:e=>{e.exports=function(e){return e[1]}},659:e=>{var t={};e.exports=function(e,n){var r=function(e){if(void 0===t[e]){var n=document.querySelector(e);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}t[e]=n}return t[e]}(e);if(!r)throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");r.appendChild(n)}},728:(e,t,n)=>{var r=n(72),o=n.n(r),a=n(825),i=n.n(a),s=n(659),c=n.n(s),u=n(56),l=n.n(u),p=n(540),f=n.n(p),d=n(113),v=n.n(d),h=n(475),m={};m.styleTagTransform=v(),m.setAttributes=l(),m.insert=c().bind(null,\"head\"),m.domAPI=i(),m.insertStyleElement=f(),o()(h.A,m),h.A&&h.A.locals&&h.A.locals},825:e=>{e.exports=function(e){if(\"undefined\"==typeof document)return{update:function(){},remove:function(){}};var t=e.insertStyleElement(e);return{update:function(n){!function(e,t,n){var r=\"\";n.supports&&(r+=\"@supports (\".concat(n.supports,\") {\")),n.media&&(r+=\"@media \".concat(n.media,\" {\"));var o=void 0!==n.layer;o&&(r+=\"@layer\".concat(n.layer.length>0?\" \".concat(n.layer):\"\",\" {\")),r+=n.css,o&&(r+=\"}\"),n.media&&(r+=\"}\"),n.supports&&(r+=\"}\");var a=n.sourceMap;a&&\"undefined\"!=typeof btoa&&(r+=\"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a)))),\" */\")),t.styleTagTransform(r,e,t.options)}(t,e,n)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}}}]);"
  },
  {
    "path": "juvio/labextension/static/remoteEntry.cb75fcc929fa00484f36.js",
    "content": "var _JUPYTERLAB;(()=>{\"use strict\";var e,r,t,n,o,i,a,u,f,l,s,d,p,c,h,v,g,b,m,y={678:(e,r,t)=>{var n={\"./index\":()=>t.e(509).then((()=>()=>t(509))),\"./extension\":()=>t.e(509).then((()=>()=>t(509))),\"./style\":()=>t.e(728).then((()=>()=>t(728)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module \"'+e+'\" does not exist in container.')})),t.R=void 0,r),i=(e,r)=>{if(t.S){var n=\"default\",o=t.S[n];if(o&&o!==e)throw new Error(\"Container initialization failed as it has already been initialized with a different share scope\");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>i})}},w={};function S(e){var r=w[e];if(void 0!==r)return r.exports;var t=w[e]={id:e,exports:{}};return y[e](t,t.exports,S),t.exports}S.m=y,S.c=w,S.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return S.d(r,{a:r}),r},S.d=(e,r)=>{for(var t in r)S.o(r,t)&&!S.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},S.f={},S.e=e=>Promise.all(Object.keys(S.f).reduce(((r,t)=>(S.f[t](e,r),r)),[])),S.u=e=>e+\".\"+{509:\"8b21d569ebbb8f039bdd\",728:\"864a8d7732d3e14d284a\"}[e]+\".js?v=\"+{509:\"8b21d569ebbb8f039bdd\",728:\"864a8d7732d3e14d284a\"}[e],S.g=function(){if(\"object\"==typeof globalThis)return globalThis;try{return this||new Function(\"return this\")()}catch(e){if(\"object\"==typeof window)return window}}(),S.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},r=\"juvio_frontend:\",S.l=(t,n,o,i)=>{if(e[t])e[t].push(n);else{var a,u;if(void 0!==o)for(var f=document.getElementsByTagName(\"script\"),l=0;l<f.length;l++){var s=f[l];if(s.getAttribute(\"src\")==t||s.getAttribute(\"data-webpack\")==r+o){a=s;break}}a||(u=!0,(a=document.createElement(\"script\")).charset=\"utf-8\",a.timeout=120,S.nc&&a.setAttribute(\"nonce\",S.nc),a.setAttribute(\"data-webpack\",r+o),a.src=t),e[t]=[n];var d=(r,n)=>{a.onerror=a.onload=null,clearTimeout(p);var o=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),o&&o.forEach((e=>e(n))),r)return r(n)},p=setTimeout(d.bind(null,void 0,{type:\"timeout\",target:a}),12e4);a.onerror=d.bind(null,a.onerror),a.onload=d.bind(null,a.onload),u&&document.head.appendChild(a)}},S.r=e=>{\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},(()=>{S.S={};var e={},r={};S.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];S.o(S.S,t)||(S.S[t]={});var i=S.S[t],a=\"juvio_frontend\",u=[];return\"default\"===t&&((e,r,t,n)=>{var o=i[e]=i[e]||{},u=o[r];(!u||!u.loaded&&(1!=!u.eager?n:a>u.from))&&(o[r]={get:()=>S.e(509).then((()=>()=>S(509))),from:a,eager:!1})})(\"juvio_frontend\",\"0.1.0\"),e[t]=u.length?Promise.all(u).then((()=>e[t]=1)):1}}})(),(()=>{var e;S.g.importScripts&&(e=S.g.location+\"\");var r=S.g.document;if(!e&&r&&(r.currentScript&&\"SCRIPT\"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName(\"script\");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error(\"Automatic publicPath is not supported in this browser\");e=e.replace(/^blob:/,\"\").replace(/#.*$/,\"\").replace(/\\?.*$/,\"\").replace(/\\/[^\\/]+$/,\"/\"),S.p=e})(),t=e=>{var r=e=>e.split(\".\").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},n=(e,r)=>{e=t(e),r=t(r);for(var n=0;;){if(n>=e.length)return n<r.length&&\"u\"!=(typeof r[n])[0];var o=e[n],i=(typeof o)[0];if(n>=r.length)return\"u\"==i;var a=r[n],u=(typeof a)[0];if(i!=u)return\"o\"==i&&\"n\"==u||\"s\"==u||\"u\"==i;if(\"o\"!=i&&\"u\"!=i&&o!=a)return o<a;n++}},o=e=>{var r=e[0],t=\"\";if(1===e.length)return\"*\";if(r+.5){t+=0==r?\">=\":-1==r?\"<\":1==r?\"^\":2==r?\"~\":r>0?\"=\":\"!=\";for(var n=1,i=1;i<e.length;i++)n--,t+=\"u\"==(typeof(u=e[i]))[0]?\"-\":(n>0?\".\":\"\")+(n=2,u);return t}var a=[];for(i=1;i<e.length;i++){var u=e[i];a.push(0===u?\"not(\"+f()+\")\":1===u?\"(\"+f()+\" || \"+f()+\")\":2===u?a.pop()+\" \"+a.pop():o(u))}return f();function f(){return a.pop().replace(/^\\((.+)\\)$/,\"$1\")}},i=(e,r)=>{if(0 in e){r=t(r);var n=e[0],o=n<0;o&&(n=-n-1);for(var a=0,u=1,f=!0;;u++,a++){var l,s,d=u<e.length?(typeof e[u])[0]:\"\";if(a>=r.length||\"o\"==(s=(typeof(l=r[a]))[0]))return!f||(\"u\"==d?u>n&&!o:\"\"==d!=o);if(\"u\"==s){if(!f||\"u\"!=d)return!1}else if(f)if(d==s)if(u<=n){if(l!=e[u])return!1}else{if(o?l>e[u]:l<e[u])return!1;l!=e[u]&&(f=!1)}else if(\"s\"!=d&&\"n\"!=d){if(o||u<=n)return!1;f=!1,u--}else{if(u<=n||s<d!=o)return!1;f=!1}else\"s\"!=d&&\"n\"!=d&&(f=!1,u--)}}var p=[],c=p.pop.bind(p);for(a=1;a<e.length;a++){var h=e[a];p.push(1==h?c()|c():2==h?c()&c():h?i(h,r):!c())}return!!c()},a=(e,r)=>e&&S.o(e,r),u=e=>(e.loaded=1,e.get()),f=e=>Object.keys(e).reduce(((r,t)=>(e[t].eager&&(r[t]=e[t]),r)),{}),l=(e,r,t)=>{var o=t?f(e[r]):e[r];return Object.keys(o).reduce(((e,r)=>!e||!o[e].loaded&&n(e,r)?r:e),0)},s=(e,r,t,n)=>\"Unsatisfied version \"+t+\" from \"+(t&&e[r][t].from)+\" of shared singleton module \"+r+\" (required \"+o(n)+\")\",d=e=>{throw new Error(e)},p=e=>{\"undefined\"!=typeof console&&console.warn&&console.warn(e)},c=(e,r,t)=>t?t():((e,r)=>d(\"Shared module \"+r+\" doesn't exist in shared scope \"+e))(e,r),h=(e=>function(r,t,n,o,i){var a=S.I(r);return a&&a.then&&!n?a.then(e.bind(e,r,S.S[r],t,!1,o,i)):e(r,S.S[r],t,n,o,i)})(((e,r,t,n,o,f)=>{if(!a(r,t))return c(e,t,f);var d=l(r,t,n);return i(o,d)||p(s(r,t,d,o)),u(r[t][d])})),v={},g={625:()=>h(\"default\",\"@jupyterlab/launcher\",!1,[1,4,4,1])},b={509:[625]},m={},S.f.consumes=(e,r)=>{S.o(b,e)&&b[e].forEach((e=>{if(S.o(v,e))return r.push(v[e]);if(!m[e]){var t=r=>{v[e]=0,S.m[e]=t=>{delete S.c[e],t.exports=r()}};m[e]=!0;var n=r=>{delete v[e],S.m[e]=t=>{throw delete S.c[e],r}};try{var o=g[e]();o.then?r.push(v[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}}))},(()=>{var e={561:0};S.f.j=(r,t)=>{var n=S.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var i=S.p+S.u(r),a=new Error;S.l(i,(t=>{if(S.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&(\"load\"===t.type?\"missing\":t.type),i=t&&t.target&&t.target.src;a.message=\"Loading chunk \"+r+\" failed.\\n(\"+o+\": \"+i+\")\",a.name=\"ChunkLoadError\",a.type=o,a.request=i,n[1](a)}}),\"chunk-\"+r,r)}};var r=(r,t)=>{var n,o,[i,a,u]=t,f=0;if(i.some((r=>0!==e[r]))){for(n in a)S.o(a,n)&&(S.m[n]=a[n]);u&&u(S)}for(r&&r(t);f<i.length;f++)o=i[f],S.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=self.webpackChunkjuvio_frontend=self.webpackChunkjuvio_frontend||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),S.nc=void 0;var j=S(678);(_JUPYTERLAB=void 0===_JUPYTERLAB?{}:_JUPYTERLAB).juvio_frontend=j})();"
  },
  {
    "path": "juvio/labextension/static/style.js",
    "content": "/* This is a generated file of CSS imports */\n/* It was generated by @jupyterlab/builder in Build.ensureAssets() */\n\nimport 'juvio_frontend/style/index.js';\n"
  },
  {
    "path": "juvio/labextension/static/third-party-licenses.json",
    "content": "{\n  \"packages\": [\n    {\n      \"name\": \"css-loader\",\n      \"versionInfo\": \"6.11.0\",\n      \"licenseId\": \"MIT\",\n      \"extractedText\": \"Copyright JS Foundation and other contributors\\n\\nPermission is hereby granted, free of charge, to any person obtaining\\na copy of this software and associated documentation files (the\\n'Software'), to deal in the Software without restriction, including\\nwithout limitation the rights to use, copy, modify, merge, publish,\\ndistribute, sublicense, and/or sell copies of the Software, and to\\npermit persons to whom the Software is furnished to do so, subject to\\nthe following conditions:\\n\\nThe above copyright notice and this permission notice shall be\\nincluded in all copies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n\"\n    },\n    {\n      \"name\": \"style-loader\",\n      \"versionInfo\": \"3.3.4\",\n      \"licenseId\": \"MIT\",\n      \"extractedText\": \"Copyright JS Foundation and other contributors\\n\\nPermission is hereby granted, free of charge, to any person obtaining\\na copy of this software and associated documentation files (the\\n'Software'), to deal in the Software without restriction, including\\nwithout limitation the rights to use, copy, modify, merge, publish,\\ndistribute, sublicense, and/or sell copies of the Software, and to\\npermit persons to whom the Software is furnished to do so, subject to\\nthe following conditions:\\n\\nThe above copyright notice and this permission notice shall be\\nincluded in all copies or substantial portions of the Software.\\n\\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\\n\"\n    }\n  ]\n}"
  },
  {
    "path": "juvio/server_extension.py",
    "content": "def _load_jupyter_server_extension(app):\n    app.log.info(\"[juvio] Loading Juvio\")\n    from juvio.content_manager import create_juvio_contents_manager_class\n\n    original_contents_manager = app.contents_manager_class\n\n    new_cm_class = create_juvio_contents_manager_class(original_contents_manager)\n    app.contents_manager_class = new_cm_class\n    app.contents_manager = new_cm_class(parent=app, log=app.log)\n    app.session_manager.contents_manager = app.contents_manager\n    app.web_app.settings[\"contents_manager\"] = app.contents_manager\n"
  },
  {
    "path": "juvio/shared.py",
    "content": "from ipykernel import kernelapp as app_module\nimport time\nimport platform\nimport asyncio\nimport hashlib\nimport os\nimport tempfile\n\n\nif platform.system() == \"Windows\":\n    import msvcrt\nelse:\n    import fcntl\n\n\nclass FileLock:\n    def __init__(self, filepath, timeout=10, delay=0.05):\n        self.filepath = filepath\n        self.timeout = timeout\n        self.delay = delay\n\n        if platform.system() == \"Windows\":\n            abs_path = os.path.abspath(filepath)\n            h = hashlib.sha256(abs_path.encode(\"utf-8\")).hexdigest()[:16]\n            lock_filename = f\"{os.path.basename(filepath)}.{h}.lock\"\n            self.lockfile_path = os.path.join(tempfile.gettempdir(), lock_filename)\n        else:\n            self.lockfile_path = filepath\n\n        self.file = None\n\n    def acquire(self):\n        start_time = time.time()\n        if platform.system() == \"Windows\":\n            while True:\n                try:\n                    self.file = os.open(\n                        self.lockfile_path, os.O_CREAT | os.O_EXCL | os.O_RDWR\n                    )\n                    break  # acquired lock\n                except FileExistsError:\n                    if (time.time() - start_time) >= self.timeout:\n                        raise TimeoutError(\n                            f\"Timeout acquiring lock for {self.filepath}\"\n                        )\n                    time.sleep(self.delay)\n        else:\n            self.file = open(self.lockfile_path, \"a+\")\n            while True:\n                try:\n                    import fcntl\n\n                    fcntl.flock(self.file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)\n                    break\n                except BlockingIOError:\n                    if (time.time() - start_time) >= self.timeout:\n                        self.file.close()\n                        raise TimeoutError(\n                            f\"Timeout acquiring lock for {self.filepath}\"\n                        )\n                    time.sleep(self.delay)\n\n    def release(self):\n        if self.file:\n            if platform.system() == \"Windows\":\n                os.close(self.file)\n                os.unlink(self.lockfile_path)\n            else:\n                import fcntl\n\n                fcntl.flock(self.file.fileno(), fcntl.LOCK_UN)\n                self.file.close()\n\n    def __enter__(self):\n        self.acquire()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.release()\n\n\nclass AsyncFileLock:\n    def __init__(self, filepath, timeout=10, delay=0.05):\n        self.filepath = filepath\n        self.timeout = timeout\n        self.delay = delay\n\n        if platform.system() == \"Windows\":\n            abs_path = os.path.abspath(filepath)\n            h = hashlib.sha256(abs_path.encode(\"utf-8\")).hexdigest()[:16]\n            lock_filename = f\"{os.path.basename(filepath)}.{h}.lock\"\n            self.lockfile_path = os.path.join(tempfile.gettempdir(), lock_filename)\n        else:\n            self.lockfile_path = filepath\n\n        self.file = None\n\n    async def acquire(self):\n        start_time = time.time()\n\n        if platform.system() == \"Windows\":\n            while True:\n                try:\n                    self.file = os.open(\n                        self.lockfile_path, os.O_CREAT | os.O_EXCL | os.O_RDWR\n                    )\n                    break\n                except FileExistsError:\n                    if (time.time() - start_time) >= self.timeout:\n                        raise TimeoutError(\n                            f\"Timeout acquiring lock for {self.filepath}\"\n                        )\n                    await asyncio.sleep(self.delay)\n        else:\n            self.file = open(self.lockfile_path, \"a+\")\n            while True:\n                try:\n                    fcntl.flock(self.file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)\n                    break\n                except BlockingIOError:\n                    if (time.time() - start_time) >= self.timeout:\n                        self.file.close()\n                        raise TimeoutError(\n                            f\"Timeout acquiring lock for {self.filepath}\"\n                        )\n                    await asyncio.sleep(self.delay)\n\n    async def release(self):\n        if self.file:\n            if platform.system() == \"Windows\":\n                os.close(self.file)\n                os.unlink(self.lockfile_path)\n            else:\n                fcntl.flock(self.file.fileno(), fcntl.LOCK_UN)\n                self.file.close()\n\n    async def __aenter__(self):\n        await self.acquire()\n        return self\n\n    async def __aexit__(self, exc_type, exc_value, traceback):\n        await self.release()\n\n\ndef load_ipython_extension(ip):\n    def juvio(line):\n        import subprocess, os, sys\n\n        command = [\"uv\", \"pip\"] + line.split()\n        try:\n            subprocess.check_call(command)\n        except subprocess.CalledProcessError as e:\n            print(f\"Error: {e}\")\n        original_no_color = os.environ.get(\"NO_COLOR\", \"1\")\n        os.environ[\"NO_COLOR\"] = \"1\"\n        try:\n            result = subprocess.run(\n                [\"uv\", \"pip\", \"freeze\", \"--no-color\"],\n                stdout=subprocess.PIPE,\n                stderr=subprocess.PIPE,\n                encoding=\"utf-8\",\n                text=True,\n                check=True,\n            )\n            deps = result.stdout.split(\"\\n\")\n        except subprocess.CalledProcessError as e:\n            print(f\"Error: {e}\")\n            print(\"Stdout:\", e.stdout)\n            print(\"Stderr:\", e.stderr)\n        finally:\n            os.environ[\"NO_COLOR\"] = original_no_color\n\n        notebook_path = os.environ.get(\"JPY_SESSION_NAME\", None)\n        if notebook_path is None:\n            raise ValueError(\n                \"No notebook path found in environment variable JPY_SESSION_NAME.\"\n            )\n        with FileLock(notebook_path):\n            with open(notebook_path, \"r+\", encoding=\"utf-8\") as f:\n                content = f.read()\n                blocks = content.split(\"# ///\")\n                cells = blocks[2:]\n                pyver = f\"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}\"\n                lines = [\n                    \"# /// script\",\n                    f'# requires-python = \"=={pyver}\"',\n                    \"# dependencies = [\",\n                ]\n                for d in deps:\n                    if len(d) > 0:\n                        lines.append(f'#   \"{d}\",')\n                lines.append(\"# ]\\n\")\n                f.seek(0)\n                metadata = \"\\n\".join(lines)\n                new = \"# ///\".join([metadata] + cells)\n                f.write(new)\n                f.truncate()\n\n    ip.register_magic_function(juvio, \"line\")\n\n\ndef start():\n    kernel_app = app_module.IPKernelApp.instance()\n    kernel_app.initialize()\n    load_ipython_extension(kernel_app.shell)\n    kernel_app.start()\n\n\nif __name__ == \"__main__\":\n    start()\n"
  },
  {
    "path": "juvio_frontend/.gitignore",
    "content": "*.bundle.*\nlib/\nnode_modules/\n*.log\n.eslintcache\n.stylelintcache\n*.egg-info/\n.ipynb_checkpoints\n*.tsbuildinfo\njuvio_frontend/labextension\n# Version file is handled by hatchling\njuvio_frontend/_version.py\n\n# Integration tests\nui-tests/test-results/\nui-tests/playwright-report/\n\n# Created by https://www.gitignore.io/api/python\n# Edit at https://www.gitignore.io/?templates=python\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\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/\ncoverage.xml\n*.cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# Mr Developer\n.mr.developer.cfg\n.project\n.pydevproject\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# End of https://www.gitignore.io/api/python\n\n# OSX files\n.DS_Store\n\n# Yarn cache\n.yarn/\n"
  },
  {
    "path": "juvio_frontend/.prettierignore",
    "content": "node_modules\n**/node_modules\n**/lib\n**/package.json\n!/package.json\njuvio_frontend\n"
  },
  {
    "path": "juvio_frontend/.yarnrc.yml",
    "content": "enableImmutableInstalls: false\n\nnodeLinker: node-modules\n"
  },
  {
    "path": "juvio_frontend/CHANGELOG.md",
    "content": "# Changelog\n\n<!-- <START NEW CHANGELOG ENTRY> -->\n\n<!-- <END NEW CHANGELOG ENTRY> -->\n"
  },
  {
    "path": "juvio_frontend/LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2025, Oleh Kostromin\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "juvio_frontend/README.md",
    "content": "# juvio_frontend\n\n[![Github Actions Status](-/workflows/Build/badge.svg)](-/actions/workflows/build.yml)\nJuvio jupyterlab extension.\n\n## Requirements\n\n- JupyterLab >= 4.0.0\n\n## Install\n\nTo install the extension, execute:\n\n```bash\npip install juvio_frontend\n```\n\n## Uninstall\n\nTo remove the extension, execute:\n\n```bash\npip uninstall juvio_frontend\n```\n\n## Contributing\n\n### Development install\n\nNote: You will need NodeJS to build the extension package.\n\nThe `jlpm` command is JupyterLab's pinned version of\n[yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use\n`yarn` or `npm` in lieu of `jlpm` below.\n\n```bash\n# Clone the repo to your local environment\n# Change directory to the juvio_frontend directory\n# Install package in development mode\npip install -e \".\"\n# Link your development version of the extension with JupyterLab\njupyter labextension develop . --overwrite\n# Rebuild extension Typescript source after making changes\njlpm build\n```\n\nYou can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the extension.\n\n```bash\n# Watch the source directory in one terminal, automatically rebuilding when needed\njlpm watch\n# Run JupyterLab in another terminal\njupyter lab\n```\n\nWith the watch command running, every saved change will immediately be built locally and available in your running JupyterLab. Refresh JupyterLab to load the change in your browser (you may need to wait several seconds for the extension to be rebuilt).\n\nBy default, the `jlpm build` command generates the source maps for this extension to make it easier to debug using the browser dev tools. To also generate source maps for the JupyterLab core extensions, you can run the following command:\n\n```bash\njupyter lab build --minimize=False\n```\n\n### Development uninstall\n\n```bash\npip uninstall juvio_frontend\n```\n\nIn development mode, you will also need to remove the symlink created by `jupyter labextension develop`\ncommand. To find its location, you can run `jupyter labextension list` to figure out where the `labextensions`\nfolder is located. Then you can remove the symlink named `juvio_frontend` within that folder.\n\n### Testing the extension\n\n#### Frontend tests\n\nThis extension is using [Jest](https://jestjs.io/) for JavaScript code testing.\n\nTo execute them, execute:\n\n```sh\njlpm\njlpm test\n```\n\n#### Integration tests\n\nThis extension uses [Playwright](https://playwright.dev/docs/intro/) for the integration tests (aka user level tests).\nMore precisely, the JupyterLab helper [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) is used to handle testing the extension in JupyterLab.\n\nMore information are provided within the [ui-tests](./ui-tests/README.md) README.\n\n### Packaging the extension\n\nSee [RELEASE](RELEASE.md)\n"
  },
  {
    "path": "juvio_frontend/RELEASE.md",
    "content": "# Making a new release of juvio_frontend\n\nThe extension can be published to `PyPI` and `npm` manually or using the [Jupyter Releaser](https://github.com/jupyter-server/jupyter_releaser).\n\n## Manual release\n\n### Python package\n\nThis extension can be distributed as Python packages. All of the Python\npackaging instructions are in the `pyproject.toml` file to wrap your extension in a\nPython package. Before generating a package, you first need to install some tools:\n\n```bash\npip install build twine hatch\n```\n\nBump the version using `hatch`. By default this will create a tag.\nSee the docs on [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version#semver) for details.\n\n```bash\nhatch version <new-version>\n```\n\nMake sure to clean up all the development files before building the package:\n\n```bash\njlpm clean:all\n```\n\nYou could also clean up the local git repository:\n\n```bash\ngit clean -dfX\n```\n\nTo create a Python source package (`.tar.gz`) and the binary package (`.whl`) in the `dist/` directory, do:\n\n```bash\npython -m build\n```\n\n> `python setup.py sdist bdist_wheel` is deprecated and will not work for this package.\n\nThen to upload the package to PyPI, do:\n\n```bash\ntwine upload dist/*\n```\n\n### NPM package\n\nTo publish the frontend part of the extension as a NPM package, do:\n\n```bash\nnpm login\nnpm publish --access public\n```\n\n## Automated releases with the Jupyter Releaser\n\nThe extension repository should already be compatible with the Jupyter Releaser.\n\nCheck out the [workflow documentation](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html) for more information.\n\nHere is a summary of the steps to cut a new release:\n\n- Add `ADMIN_GITHUB_TOKEN`, `PYPI_TOKEN` and `NPM_TOKEN` to the [Github Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets) in the repository\n- Go to the Actions panel\n- Run the \"Step 1: Prep Release\" workflow\n- Check the draft changelog\n- Run the \"Step 2: Publish Release\" workflow\n\n## Publishing to `conda-forge`\n\nIf the package is not on conda forge yet, check the documentation to learn how to add it: https://conda-forge.org/docs/maintainer/adding_pkgs.html\n\nOtherwise a bot should pick up the new version publish to PyPI, and open a new PR on the feedstock repository automatically.\n"
  },
  {
    "path": "juvio_frontend/babel.config.js",
    "content": "module.exports = require('@jupyterlab/testutils/lib/babel.config');\n"
  },
  {
    "path": "juvio_frontend/install.json",
    "content": "{\n  \"packageManager\": \"python\",\n  \"packageName\": \"juvio_frontend\",\n  \"uninstallInstructions\": \"Use your Python package manager (pip, conda, etc.) to uninstall the package juvio_frontend\"\n}"
  },
  {
    "path": "juvio_frontend/jest.config.js",
    "content": "const jestJupyterLab = require('@jupyterlab/testutils/lib/jest-config');\n\nconst esModules = [\n  '@codemirror',\n  '@jupyter/ydoc',\n  '@jupyterlab/',\n  'lib0',\n  'nanoid',\n  'vscode-ws-jsonrpc',\n  'y-protocols',\n  'y-websocket',\n  'yjs'\n].join('|');\n\nconst baseConfig = jestJupyterLab(__dirname);\n\nmodule.exports = {\n  ...baseConfig,\n  automock: false,\n  collectCoverageFrom: [\n    'src/**/*.{ts,tsx}',\n    '!src/**/*.d.ts',\n    '!src/**/.ipynb_checkpoints/*'\n  ],\n  coverageReporters: ['lcov', 'text'],\n  testRegex: 'src/.*/.*.spec.ts[x]?$',\n  transformIgnorePatterns: [`/node_modules/(?!${esModules}).+`]\n};\n"
  },
  {
    "path": "juvio_frontend/package.json",
    "content": "{\n  \"name\": \"juvio_frontend\",\n  \"version\": \"0.1.0\",\n  \"description\": \"Juvio jupyterlab extension.\",\n  \"keywords\": [\n    \"jupyter\",\n    \"jupyterlab\",\n    \"jupyterlab-extension\"\n  ],\n  \"homepage\": \"-\",\n  \"bugs\": {\n    \"url\": \"-/issues\"\n  },\n  \"license\": \"BSD-3-Clause\",\n  \"author\": {\n    \"name\": \"Oleh Kostromin\",\n    \"email\": \"oleh@beastbyte.ai\"\n  },\n  \"files\": [\n    \"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}\",\n    \"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}\"\n  ],\n  \"main\": \"lib/index.js\",\n  \"types\": \"lib/index.d.ts\",\n  \"style\": \"style/index.css\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"-.git\"\n  },\n  \"workspaces\": [\n    \"ui-tests\"\n  ],\n  \"scripts\": {\n    \"build\": \"jlpm build:lib && jlpm build:labextension:dev\",\n    \"build:prod\": \"jlpm clean && jlpm build:lib:prod && jlpm build:labextension\",\n    \"build:labextension\": \"jupyter labextension build .\",\n    \"build:labextension:dev\": \"jupyter labextension build --development True .\",\n    \"build:lib\": \"tsc --sourceMap\",\n    \"build:lib:prod\": \"tsc\",\n    \"clean\": \"jlpm clean:lib\",\n    \"clean:lib\": \"rimraf lib tsconfig.tsbuildinfo\",\n    \"clean:lintcache\": \"rimraf .eslintcache .stylelintcache\",\n    \"clean:labextension\": \"rimraf juvio_frontend/labextension juvio_frontend/_version.py\",\n    \"clean:all\": \"jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache\",\n    \"eslint\": \"jlpm eslint:check --fix\",\n    \"eslint:check\": \"eslint . --cache --ext .ts,.tsx\",\n    \"install:extension\": \"jlpm build\",\n    \"lint\": \"jlpm stylelint && jlpm prettier && jlpm eslint\",\n    \"lint:check\": \"jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check\",\n    \"prettier\": \"jlpm prettier:base --write --list-different\",\n    \"prettier:base\": \"prettier \\\"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\\\"\",\n    \"prettier:check\": \"jlpm prettier:base --check\",\n    \"stylelint\": \"jlpm stylelint:check --fix\",\n    \"stylelint:check\": \"stylelint --cache \\\"style/**/*.css\\\"\",\n    \"test\": \"jest --coverage\",\n    \"watch\": \"run-p watch:src watch:labextension\",\n    \"watch:src\": \"tsc -w --sourceMap\",\n    \"watch:labextension\": \"jupyter labextension watch .\"\n  },\n  \"dependencies\": {\n    \"@jupyterlab/application\": \"^4.0.0\",\n    \"@jupyterlab/launcher\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@jupyterlab/builder\": \"^4.0.0\",\n    \"@jupyterlab/testutils\": \"^4.0.0\",\n    \"@types/jest\": \"^29.2.0\",\n    \"@types/json-schema\": \"^7.0.11\",\n    \"@types/react\": \"^18.0.26\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.55.0\",\n    \"@typescript-eslint/parser\": \"^5.55.0\",\n    \"css-loader\": \"^6.7.1\",\n    \"eslint\": \"^8.36.0\",\n    \"eslint-config-prettier\": \"^8.7.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"jest\": \"^29.2.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"prettier\": \"^2.8.7\",\n    \"rimraf\": \"^4.4.1\",\n    \"source-map-loader\": \"^1.0.2\",\n    \"style-loader\": \"^3.3.1\",\n    \"stylelint\": \"^14.9.1\",\n    \"stylelint-config-prettier\": \"^9.0.4\",\n    \"stylelint-config-recommended\": \"^8.0.0\",\n    \"stylelint-config-standard\": \"^26.0.0\",\n    \"stylelint-prettier\": \"^2.0.0\",\n    \"typescript\": \"^5.0\",\n    \"yjs\": \"^13.5.0\"\n  },\n  \"sideEffects\": [\n    \"style/*.css\",\n    \"style/index.js\"\n  ],\n  \"styleModule\": \"style/index.js\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"jupyterlab\": {\n    \"extension\": true,\n    \"outputDir\": \"build/labextension\"\n  },\n  \"eslintIgnore\": [\n    \"node_modules\",\n    \"dist\",\n    \"coverage\",\n    \"**/*.d.ts\",\n    \"tests\",\n    \"**/__tests__\",\n    \"ui-tests\"\n  ],\n  \"eslintConfig\": {\n    \"extends\": [\n      \"eslint:recommended\",\n      \"plugin:@typescript-eslint/eslint-recommended\",\n      \"plugin:@typescript-eslint/recommended\",\n      \"plugin:prettier/recommended\"\n    ],\n    \"parser\": \"@typescript-eslint/parser\",\n    \"parserOptions\": {\n      \"project\": \"tsconfig.json\",\n      \"sourceType\": \"module\"\n    },\n    \"plugins\": [\n      \"@typescript-eslint\"\n    ],\n    \"rules\": {\n      \"@typescript-eslint/naming-convention\": [\n        \"error\",\n        {\n          \"selector\": \"interface\",\n          \"format\": [\n            \"PascalCase\"\n          ],\n          \"custom\": {\n            \"regex\": \"^I[A-Z]\",\n            \"match\": true\n          }\n        }\n      ],\n      \"@typescript-eslint/no-unused-vars\": [\n        \"warn\",\n        {\n          \"args\": \"none\"\n        }\n      ],\n      \"@typescript-eslint/no-explicit-any\": \"off\",\n      \"@typescript-eslint/no-namespace\": \"off\",\n      \"@typescript-eslint/no-use-before-define\": \"off\",\n      \"@typescript-eslint/quotes\": [\n        \"error\",\n        \"single\",\n        {\n          \"avoidEscape\": true,\n          \"allowTemplateLiterals\": false\n        }\n      ],\n      \"curly\": [\n        \"error\",\n        \"all\"\n      ],\n      \"eqeqeq\": \"error\",\n      \"prefer-arrow-callback\": \"error\"\n    }\n  },\n  \"prettier\": {\n    \"singleQuote\": true,\n    \"trailingComma\": \"none\",\n    \"arrowParens\": \"avoid\",\n    \"endOfLine\": \"auto\"\n  },\n  \"stylelint\": {\n    \"extends\": [\n      \"stylelint-config-recommended\",\n      \"stylelint-config-standard\",\n      \"stylelint-prettier/recommended\"\n    ],\n    \"rules\": {\n      \"property-no-vendor-prefix\": null,\n      \"selector-no-vendor-prefix\": null,\n      \"value-no-vendor-prefix\": null\n    }\n  }\n}\n"
  },
  {
    "path": "juvio_frontend/pyproject.toml",
    "content": "[build-system]\nrequires = [\"hatchling>=1.5.0\", \"jupyterlab>=4.0.0,<5\", \"hatch-nodejs-version\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"juvio_frontend\"\nreadme = \"README.md\"\nlicense = { file = \"LICENSE\" }\nrequires-python = \">=3.8\"\nclassifiers = [\n    \"Framework :: Jupyter\",\n    \"Framework :: Jupyter :: JupyterLab\",\n    \"Framework :: Jupyter :: JupyterLab :: 4\",\n    \"Framework :: Jupyter :: JupyterLab :: Extensions\",\n    \"Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt\",\n    \"License :: OSI Approved :: BSD License\",\n    \"Programming Language :: Python\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3.8\",\n    \"Programming Language :: Python :: 3.9\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n]\ndependencies = [\n]\ndynamic = [\"version\", \"description\", \"authors\", \"urls\", \"keywords\"]\n\n[tool.hatch.version]\nsource = \"nodejs\"\n\n[tool.hatch.metadata.hooks.nodejs]\nfields = [\"description\", \"authors\", \"urls\"]\n\n[tool.hatch.build.targets.sdist]\nartifacts = [\"juvio_frontend/labextension\"]\nexclude = [\".github\", \"binder\"]\n\n[tool.hatch.build.targets.wheel.shared-data]\n\"juvio_frontend/labextension\" = \"share/jupyter/labextensions/juvio_frontend\"\n\"install.json\" = \"share/jupyter/labextensions/juvio_frontend/install.json\"\n\n[tool.hatch.build.hooks.version]\npath = \"juvio_frontend/_version.py\"\n\n[tool.hatch.build.hooks.jupyter-builder]\ndependencies = [\"hatch-jupyter-builder>=0.5\"]\nbuild-function = \"hatch_jupyter_builder.npm_builder\"\nensured-targets = [\n    \"juvio_frontend/labextension/static/style.js\",\n    \"juvio_frontend/labextension/package.json\",\n]\nskip-if-exists = [\"juvio_frontend/labextension/static/style.js\"]\n\n[tool.hatch.build.hooks.jupyter-builder.build-kwargs]\nbuild_cmd = \"build:prod\"\nnpm = [\"jlpm\"]\n\n[tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs]\nbuild_cmd = \"install:extension\"\nnpm = [\"jlpm\"]\nsource_dir = \"src\"\nbuild_dir = \"juvio_frontend/labextension\"\n\n[tool.jupyter-releaser.options]\nversion_cmd = \"hatch version\"\n\n[tool.jupyter-releaser.hooks]\nbefore-build-npm = [\n    \"python -m pip install 'jupyterlab>=4.0.0,<5'\",\n    \"jlpm\",\n    \"jlpm build:prod\"\n]\nbefore-build-python = [\"jlpm clean:all\"]\n\n[tool.check-wheel-contents]\nignore = [\"W002\"]\n"
  },
  {
    "path": "juvio_frontend/setup.py",
    "content": "__import__('setuptools').setup()\n"
  },
  {
    "path": "juvio_frontend/src/__tests__/juvio_frontend.spec.ts",
    "content": "/**\n * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests\n */\n\ndescribe('juvio_frontend', () => {\n  it('should be tested', () => {\n    expect(1 + 1).toEqual(2);\n  });\n});\n"
  },
  {
    "path": "juvio_frontend/src/index.ts",
    "content": "import {\n  JupyterFrontEnd,\n  JupyterFrontEndPlugin\n} from '@jupyterlab/application';\n\nimport { ILauncher } from '@jupyterlab/launcher';\n\n\nconst plugin: JupyterFrontEndPlugin<void> = {\n  id: 'juvio_frontend:plugin',\n  description: 'Juvio JupyterLab extension',\n  autoStart: true,\n  requires: [ILauncher],\n  activate: (\n    app: JupyterFrontEnd,\n    launcher: ILauncher\n  ) => {\n    console.log('JupyterLab extension juvio_frontend is activated!');\n\n    app.docRegistry.addFileType({\n      name: 'juvio',\n      displayName: 'Juvio Notebook',\n      extensions: ['.juvio'],\n      contentType: 'notebook',\n      fileFormat: 'json',\n    });\n\n    app.docRegistry.addFileType({\n      name: 'notebook',\n      extensions: ['.juvio']\n    });\n\n    const { commands } = app;\n    commands.addCommand('create-juvio-file', {\n      label: 'Juvio Notebook',\n      execute: () => {\n        return commands.execute('docmanager:new-untitled', {\n          type: 'file',\n          ext: '.juvio',\n          kernelName: \"juvio\",\n        });\n      }\n    });\n\n    launcher.add({\n      category: 'Notebook',\n      rank: 1,\n      command: 'create-juvio-file',\n      kernelIconUrl: 'https://gist.githubusercontent.com/OKUA1/d6e65e883546021ea774857878fd0537/raw/4de2ea217e25d9ff7b3d2a73899e85665ed7d94c/juvio_logo.svg',\n    });\n  }\n};\n\nexport default plugin;\n"
  },
  {
    "path": "juvio_frontend/style/base.css",
    "content": "/*\n    See the JupyterLab Developer Guide for useful CSS Patterns:\n\n    https://jupyterlab.readthedocs.io/en/stable/developer/css.html\n*/\n"
  },
  {
    "path": "juvio_frontend/style/index.css",
    "content": "@import 'base.css';\n"
  },
  {
    "path": "juvio_frontend/style/index.js",
    "content": "import './base.css';\n"
  },
  {
    "path": "juvio_frontend/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowSyntheticDefaultImports\": true,\n    \"composite\": true,\n    \"declaration\": true,\n    \"esModuleInterop\": true,\n    \"incremental\": true,\n    \"jsx\": \"react\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"noEmitOnError\": true,\n    \"noImplicitAny\": true,\n    \"noUnusedLocals\": true,\n    \"preserveWatchOutput\": true,\n    \"resolveJsonModule\": true,\n    \"outDir\": \"lib\",\n    \"rootDir\": \"src\",\n    \"strict\": true,\n    \"strictNullChecks\": true,\n    \"target\": \"ES2018\",\n    \"types\": [\"jest\"],\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\", \"ESNext.Intl\"],\n  },\n  \"include\": [\"src/*\"]\n}\n\n"
  },
  {
    "path": "juvio_frontend/tsconfig.test.json",
    "content": "{\n  \"extends\": \"./tsconfig\"\n}\n"
  },
  {
    "path": "juvio_frontend/ui-tests/README.md",
    "content": "# Integration Testing\n\nThis folder contains the integration tests of the extension.\n\nThey are defined using [Playwright](https://playwright.dev/docs/intro) test runner\nand [Galata](https://github.com/jupyterlab/jupyterlab/tree/master/galata) helper.\n\nThe Playwright configuration is defined in [playwright.config.js](./playwright.config.js).\n\nThe JupyterLab server configuration to use for the integration test is defined\nin [jupyter_server_test_config.py](./jupyter_server_test_config.py).\n\nThe default configuration will produce video for failing tests and an HTML report.\n\n> There is a new experimental UI mode that you may fall in love with; see [that video](https://www.youtube.com/watch?v=jF0yA-JLQW0).\n\n## Run the tests\n\n> All commands are assumed to be executed from the root directory\n\nTo run the tests, you need to:\n\n1. Compile the extension:\n\n```sh\njlpm install\njlpm build:prod\n```\n\n> Check the extension is installed in JupyterLab.\n\n2. Install test dependencies (needed only once):\n\n```sh\ncd ./ui-tests\njlpm install\njlpm playwright install\ncd ..\n```\n\n3. Execute the [Playwright](https://playwright.dev/docs/intro) tests:\n\n```sh\ncd ./ui-tests\njlpm playwright test\n```\n\nTest results will be shown in the terminal. In case of any test failures, the test report\nwill be opened in your browser at the end of the tests execution; see\n[Playwright documentation](https://playwright.dev/docs/test-reporters#html-reporter)\nfor configuring that behavior.\n\n## Update the tests snapshots\n\n> All commands are assumed to be executed from the root directory\n\nIf you are comparing snapshots to validate your tests, you may need to update\nthe reference snapshots stored in the repository. To do that, you need to:\n\n1. Compile the extension:\n\n```sh\njlpm install\njlpm build:prod\n```\n\n> Check the extension is installed in JupyterLab.\n\n2. Install test dependencies (needed only once):\n\n```sh\ncd ./ui-tests\njlpm install\njlpm playwright install\ncd ..\n```\n\n3. Execute the [Playwright](https://playwright.dev/docs/intro) command:\n\n```sh\ncd ./ui-tests\njlpm playwright test -u\n```\n\n> Some discrepancy may occurs between the snapshots generated on your computer and\n> the one generated on the CI. To ease updating the snapshots on a PR, you can\n> type `please update playwright snapshots` to trigger the update by a bot on the CI.\n> Once the bot has computed new snapshots, it will commit them to the PR branch.\n\n## Create tests\n\n> All commands are assumed to be executed from the root directory\n\nTo create tests, the easiest way is to use the code generator tool of playwright:\n\n1. Compile the extension:\n\n```sh\njlpm install\njlpm build:prod\n```\n\n> Check the extension is installed in JupyterLab.\n\n2. Install test dependencies (needed only once):\n\n```sh\ncd ./ui-tests\njlpm install\njlpm playwright install\ncd ..\n```\n\n3. Start the server:\n\n```sh\ncd ./ui-tests\njlpm start\n```\n\n4. Execute the [Playwright code generator](https://playwright.dev/docs/codegen) in **another terminal**:\n\n```sh\ncd ./ui-tests\njlpm playwright codegen localhost:8888\n```\n\n## Debug tests\n\n> All commands are assumed to be executed from the root directory\n\nTo debug tests, a good way is to use the inspector tool of playwright:\n\n1. Compile the extension:\n\n```sh\njlpm install\njlpm build:prod\n```\n\n> Check the extension is installed in JupyterLab.\n\n2. Install test dependencies (needed only once):\n\n```sh\ncd ./ui-tests\njlpm install\njlpm playwright install\ncd ..\n```\n\n3. Execute the Playwright tests in [debug mode](https://playwright.dev/docs/debug):\n\n```sh\ncd ./ui-tests\njlpm playwright test --debug\n```\n\n## Upgrade Playwright and the browsers\n\nTo update the web browser versions, you must update the package `@playwright/test`:\n\n```sh\ncd ./ui-tests\njlpm up \"@playwright/test\"\njlpm playwright install\n```\n"
  },
  {
    "path": "juvio_frontend/ui-tests/jupyter_server_test_config.py",
    "content": "\"\"\"Server configuration for integration tests.\n\n!! Never use this configuration in production because it\nopens the server to the world and provide access to JupyterLab\nJavaScript objects through the global window variable.\n\"\"\"\nfrom jupyterlab.galata import configure_jupyter_server\n\nconfigure_jupyter_server(c)\n\n# Uncomment to set server log level to debug level\n# c.ServerApp.log_level = \"DEBUG\"\n"
  },
  {
    "path": "juvio_frontend/ui-tests/package.json",
    "content": "{\n  \"name\": \"juvio_frontend-ui-tests\",\n  \"version\": \"1.0.0\",\n  \"description\": \"JupyterLab juvio_frontend Integration Tests\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"jupyter lab --config jupyter_server_test_config.py\",\n    \"test\": \"jlpm playwright test\",\n    \"test:update\": \"jlpm playwright test --update-snapshots\"\n  },\n  \"devDependencies\": {\n    \"@jupyterlab/galata\": \"^5.0.0\",\n    \"@playwright/test\": \"^1.32.0\"\n  }\n}\n"
  },
  {
    "path": "juvio_frontend/ui-tests/playwright.config.js",
    "content": "/**\n * Configuration for Playwright using default from @jupyterlab/galata\n */\nconst baseConfig = require('@jupyterlab/galata/lib/playwright-config');\n\nmodule.exports = {\n  ...baseConfig,\n  webServer: {\n    command: 'jlpm start',\n    url: 'http://localhost:8888/lab',\n    timeout: 120 * 1000,\n    reuseExistingServer: !process.env.CI\n  }\n};\n"
  },
  {
    "path": "juvio_frontend/ui-tests/tests/juvio_frontend.spec.ts",
    "content": "import { expect, test } from '@jupyterlab/galata';\n\n/**\n * Don't load JupyterLab webpage before running the tests.\n * This is required to ensure we capture all log messages.\n */\ntest.use({ autoGoto: false });\n\ntest('should emit an activation console message', async ({ page }) => {\n  const logs: string[] = [];\n\n  page.on('console', message => {\n    logs.push(message.text());\n  });\n\n  await page.goto();\n\n  expect(\n    logs.filter(s => s === 'JupyterLab extension juvio_frontend is activated!')\n  ).toHaveLength(1);\n});\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools>=64\", \"wheel\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname = \"juvio\"\nversion = \"0.1.0b2\"\ndescription = \"Jupyter kernel using uv and per-notebook dependencies\"\nauthors = [{ name = \"Oleh Kostromin\", email = \"oleh@beastbyte.ai\" }]\nlicense = { text = \"MIT\" }\nrequires-python = \">=3.11\"\ndependencies = [\n    \"notebook>=7.0,<8.0\",\n    \"jupyterlab>=4.0,<5.0\",\n    \"ipykernel>=6.25,<8.0\",\n    \"aiofiles>=23.0.0\"\n]\n\n\n[project.scripts]\njuvio-kernel-launcher = \"juvio.kernel_launcher:main\"\n\n[project.entry-points.\"jupyter_server.extensions\"]\njuvio = \"juvio:_load_jupyter_server_extension\"\n\n[tool.setuptools]\ninclude-package-data = true\n\n[tool.setuptools.packages.find]\nwhere = [\".\"]\ninclude = [\"juvio\"]\n\n[tool.setuptools.data-files]\n\"share/jupyter/labextensions/juvio_frontend\" = [\n    \"juvio/labextension/package.json\"\n]\n\"share/jupyter/labextensions/juvio_frontend/static\" = [\n    \"juvio/labextension/static/509.8b21d569ebbb8f039bdd.js\",\n    \"juvio/labextension/static/728.864a8d7732d3e14d284a.js\",\n    \"juvio/labextension/static/remoteEntry.cb75fcc929fa00484f36.js\",\n    \"juvio/labextension/static/third-party-licenses.json\"\n]\n\n\"share/jupyter/kernels/juvio\" = [\n    \"juvio/kernelspec/kernel.json\",\n]"
  }
]