├── .yarnrc.yml ├── style ├── index.js ├── index.css ├── base.css └── js.svg ├── setup.py ├── src ├── svg.d.ts ├── logLevelSwitcher.tsx └── index.ts ├── .prettierignore ├── install.json ├── .github └── workflows │ ├── binder-on-pr.yml │ ├── enforce-label.yml │ ├── check-release.yml │ ├── deploy.yml │ ├── prep-release.yml │ ├── publish-release.yml │ └── build.yml ├── .copier-answers.yml ├── binder ├── environment.yml └── postBuild ├── tsconfig.json ├── jupyterlab_js_logs └── __init__.py ├── LICENSE ├── .gitignore ├── pyproject.toml ├── RELEASE.md ├── README.md ├── CHANGELOG.md └── package.json /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /style/index.js: -------------------------------------------------------------------------------- 1 | import './base.css'; 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | __import__("setuptools").setup() 2 | -------------------------------------------------------------------------------- /style/index.css: -------------------------------------------------------------------------------- 1 | @import url('base.css'); 2 | -------------------------------------------------------------------------------- /src/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | const value: string; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/node_modules 3 | **/lib 4 | **/package.json 5 | !/package.json 6 | jupyterlab_js_logs 7 | -------------------------------------------------------------------------------- /style/base.css: -------------------------------------------------------------------------------- 1 | /* 2 | See the JupyterLab Developer Guide for useful CSS Patterns: 3 | 4 | https://jupyterlab.readthedocs.io/en/stable/developer/css.html 5 | */ 6 | -------------------------------------------------------------------------------- /install.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageManager": "python", 3 | "packageName": "jupyterlab_js_logs", 4 | "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package jupyterlab-js-logs" 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/binder-on-pr.yml: -------------------------------------------------------------------------------- 1 | name: Binder Badge 2 | on: 3 | pull_request_target: 4 | types: [opened] 5 | 6 | jobs: 7 | binder: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | pull-requests: write 11 | steps: 12 | - uses: jupyterlab/maintainer-tools/.github/actions/binder-link@v1 13 | with: 14 | github_token: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.github/workflows/enforce-label.yml: -------------------------------------------------------------------------------- 1 | name: Enforce PR label 2 | 3 | on: 4 | pull_request: 5 | types: [labeled, unlabeled, opened, edited, synchronize] 6 | jobs: 7 | enforce-label: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | pull-requests: write 11 | steps: 12 | - name: enforce-triage-label 13 | uses: jupyterlab/maintainer-tools/.github/actions/enforce-label@v1 14 | -------------------------------------------------------------------------------- /.copier-answers.yml: -------------------------------------------------------------------------------- 1 | # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY 2 | _commit: v4.3.5 3 | _src_path: https://github.com/jupyterlab/extension-template 4 | author_email: '' 5 | author_name: jupyterlab-js-logs contributors 6 | has_binder: true 7 | has_settings: false 8 | kind: frontend 9 | labextension_name: jupyterlab-js-logs 10 | project_short_description: JupyterLab extension to show the js logs from the browser 11 | dev tools console 12 | python_name: jupyterlab_js_logs 13 | repository: https://github.com/jupyterlab-contrib/jupyterlab-js-logs.git 14 | test: false 15 | 16 | -------------------------------------------------------------------------------- /binder/environment.yml: -------------------------------------------------------------------------------- 1 | # a mybinder.org-ready environment for demoing jupyterlab_js_logs 2 | # this environment may also be used locally on Linux/MacOS/Windows, e.g. 3 | # 4 | # conda env update --file binder/environment.yml 5 | # conda activate jupyterlab-js-logs-demo 6 | # 7 | name: jupyterlab-js-logs-demo 8 | 9 | channels: 10 | - conda-forge 11 | 12 | dependencies: 13 | # runtime dependencies 14 | - python >=3.10,<3.11.0a0 15 | - jupyterlab >=4.0.0,<5 16 | # labextension build dependencies 17 | - nodejs >=18,<19 18 | - pip 19 | - wheel 20 | # additional packages for demos 21 | # - ipywidgets 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "composite": true, 5 | "declaration": true, 6 | "esModuleInterop": true, 7 | "incremental": true, 8 | "jsx": "react", 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmitOnError": true, 12 | "noImplicitAny": true, 13 | "noUnusedLocals": true, 14 | "preserveWatchOutput": true, 15 | "resolveJsonModule": true, 16 | "outDir": "lib", 17 | "rootDir": "src", 18 | "strict": true, 19 | "strictNullChecks": true, 20 | "target": "ES2018" 21 | }, 22 | "include": ["src/*"] 23 | } 24 | -------------------------------------------------------------------------------- /jupyterlab_js_logs/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from ._version import __version__ 3 | except ImportError: 4 | # Fallback when using the package in dev mode without installing 5 | # in editable mode with pip. It is highly recommended to install 6 | # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs 7 | import warnings 8 | warnings.warn("Importing 'jupyterlab_js_logs' outside a proper installation.") 9 | __version__ = "dev" 10 | 11 | 12 | def _jupyter_labextension_paths(): 13 | return [{ 14 | "src": "labextension", 15 | "dest": "jupyterlab-js-logs" 16 | }] 17 | -------------------------------------------------------------------------------- /style/js.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/workflows/check-release.yml: -------------------------------------------------------------------------------- 1 | name: Check Release 2 | on: 3 | push: 4 | branches: ["main"] 5 | pull_request: 6 | branches: ["*"] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | check_release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | - name: Base Setup 19 | uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 20 | - name: Check Release 21 | uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2 22 | with: 23 | 24 | token: ${{ secrets.GITHUB_TOKEN }} 25 | 26 | - name: Upload Distributions 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: jupyterlab_js_logs-releaser-dist-${{ github.run_number }} 30 | path: .jupyter_releaser_checkout/dist 31 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | - name: Setup Python 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: '3.11' 21 | - name: Install the dependencies 22 | run: | 23 | python -m pip install jupyterlite-core jupyterlite-pyodide-kernel . 24 | - name: Build the JupyterLite site 25 | run: | 26 | jupyter lite build --output-dir dist 27 | - name: Upload artifact 28 | uses: actions/upload-pages-artifact@v3 29 | with: 30 | path: ./dist 31 | 32 | deploy: 33 | needs: build 34 | if: github.ref == 'refs/heads/main' 35 | permissions: 36 | pages: write 37 | id-token: write 38 | 39 | environment: 40 | name: github-pages 41 | url: ${{ steps.deployment.outputs.page_url }} 42 | 43 | runs-on: ubuntu-latest 44 | steps: 45 | - name: Deploy to GitHub Pages 46 | id: deployment 47 | uses: actions/deploy-pages@v4 48 | -------------------------------------------------------------------------------- /binder/postBuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ perform a development install of jupyterlab_js_logs 3 | 4 | On Binder, this will run _after_ the environment has been fully created from 5 | the environment.yml in this directory. 6 | 7 | This script should also run locally on Linux/MacOS/Windows: 8 | 9 | python3 binder/postBuild 10 | """ 11 | import subprocess 12 | import sys 13 | from pathlib import Path 14 | 15 | 16 | ROOT = Path.cwd() 17 | 18 | def _(*args, **kwargs): 19 | """ Run a command, echoing the args 20 | 21 | fails hard if something goes wrong 22 | """ 23 | print("\n\t", " ".join(args), "\n") 24 | return_code = subprocess.call(args, **kwargs) 25 | if return_code != 0: 26 | print("\nERROR", return_code, " ".join(args)) 27 | sys.exit(return_code) 28 | 29 | # verify the environment is self-consistent before even starting 30 | _(sys.executable, "-m", "pip", "check") 31 | 32 | # install the labextension 33 | _(sys.executable, "-m", "pip", "install", "-e", ".") 34 | _(sys.executable, "-m", "jupyter", "labextension", "develop", "--overwrite", ".") 35 | 36 | # verify the environment the extension didn't break anything 37 | _(sys.executable, "-m", "pip", "check") 38 | 39 | # list the extensions 40 | _("jupyter", "server", "extension", "list") 41 | 42 | # initially list installed extensions to determine if there are any surprises 43 | _("jupyter", "labextension", "list") 44 | 45 | 46 | print("JupyterLab with jupyterlab_js_logs is ready to run with:\n") 47 | print("\tjupyter lab\n") 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, jupyterlab-js-logs contributors All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /.github/workflows/prep-release.yml: -------------------------------------------------------------------------------- 1 | name: "Step 1: Prep Release" 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | version_spec: 6 | description: "New Version Specifier" 7 | default: "next" 8 | required: false 9 | branch: 10 | description: "The branch to target" 11 | required: false 12 | post_version_spec: 13 | description: "Post Version Specifier" 14 | required: false 15 | # silent: 16 | # description: "Set a placeholder in the changelog and don't publish the release." 17 | # required: false 18 | # type: boolean 19 | since: 20 | description: "Use PRs with activity since this date or git reference" 21 | required: false 22 | since_last_stable: 23 | description: "Use PRs with activity since the last stable git tag" 24 | required: false 25 | type: boolean 26 | jobs: 27 | prep_release: 28 | runs-on: ubuntu-latest 29 | permissions: 30 | contents: write 31 | steps: 32 | - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 33 | 34 | - name: Prep Release 35 | id: prep-release 36 | uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2 37 | with: 38 | token: ${{ secrets.GITHUB_TOKEN }} 39 | version_spec: ${{ github.event.inputs.version_spec }} 40 | # silent: ${{ github.event.inputs.silent }} 41 | post_version_spec: ${{ github.event.inputs.post_version_spec }} 42 | branch: ${{ github.event.inputs.branch }} 43 | since: ${{ github.event.inputs.since }} 44 | since_last_stable: ${{ github.event.inputs.since_last_stable }} 45 | 46 | - name: "** Next Step **" 47 | run: | 48 | echo "Optional): Review Draft Release: ${{ steps.prep-release.outputs.release_url }}" 49 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: "Step 2: Publish Release" 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | branch: 6 | description: "The target branch" 7 | required: false 8 | release_url: 9 | description: "The URL of the draft GitHub release" 10 | required: false 11 | steps_to_skip: 12 | description: "Comma separated list of steps to skip" 13 | required: false 14 | 15 | jobs: 16 | publish_release: 17 | runs-on: ubuntu-latest 18 | environment: release 19 | permissions: 20 | id-token: write 21 | steps: 22 | - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 23 | 24 | - uses: actions/create-github-app-token@v1 25 | id: app-token 26 | with: 27 | app-id: ${{ vars.APP_ID }} 28 | private-key: ${{ secrets.APP_PRIVATE_KEY }} 29 | 30 | - name: Populate Release 31 | id: populate-release 32 | uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2 33 | with: 34 | token: ${{ steps.app-token.outputs.token }} 35 | branch: ${{ github.event.inputs.branch }} 36 | release_url: ${{ github.event.inputs.release_url }} 37 | steps_to_skip: ${{ github.event.inputs.steps_to_skip }} 38 | 39 | - name: Finalize Release 40 | id: finalize-release 41 | env: 42 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 43 | uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2 44 | with: 45 | token: ${{ steps.app-token.outputs.token }} 46 | release_url: ${{ steps.populate-release.outputs.release_url }} 47 | 48 | - name: "** Next Step **" 49 | if: ${{ success() }} 50 | run: | 51 | echo "Verify the final release" 52 | echo ${{ steps.finalize-release.outputs.release_url }} 53 | 54 | - name: "** Failure Message **" 55 | if: ${{ failure() }} 56 | run: | 57 | echo "Failed to Publish the Draft Release Url:" 58 | echo ${{ steps.populate-release.outputs.release_url }} 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bundle.* 2 | lib/ 3 | node_modules/ 4 | *.log 5 | .eslintcache 6 | .stylelintcache 7 | *.egg-info/ 8 | .ipynb_checkpoints 9 | *.tsbuildinfo 10 | jupyterlab_js_logs/labextension 11 | # Version file is handled by hatchling 12 | jupyterlab_js_logs/_version.py 13 | 14 | # Created by https://www.gitignore.io/api/python 15 | # Edit at https://www.gitignore.io/?templates=python 16 | 17 | ### Python ### 18 | # Byte-compiled / optimized / DLL files 19 | __pycache__/ 20 | *.py[cod] 21 | *$py.class 22 | 23 | # C extensions 24 | *.so 25 | 26 | # Distribution / packaging 27 | .Python 28 | build/ 29 | develop-eggs/ 30 | dist/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | lib/ 35 | lib64/ 36 | parts/ 37 | sdist/ 38 | var/ 39 | wheels/ 40 | pip-wheel-metadata/ 41 | share/python-wheels/ 42 | .installed.cfg 43 | *.egg 44 | MANIFEST 45 | 46 | # PyInstaller 47 | # Usually these files are written by a python script from a template 48 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 49 | *.manifest 50 | *.spec 51 | 52 | # Installer logs 53 | pip-log.txt 54 | pip-delete-this-directory.txt 55 | 56 | # Unit test / coverage reports 57 | htmlcov/ 58 | .tox/ 59 | .nox/ 60 | .coverage 61 | .coverage.* 62 | .cache 63 | nosetests.xml 64 | coverage/ 65 | coverage.xml 66 | *.cover 67 | .hypothesis/ 68 | .pytest_cache/ 69 | 70 | # Translations 71 | *.mo 72 | *.pot 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # pyenv 84 | .python-version 85 | 86 | # celery beat schedule file 87 | celerybeat-schedule 88 | 89 | # SageMath parsed files 90 | *.sage.py 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # Mr Developer 100 | .mr.developer.cfg 101 | .project 102 | .pydevproject 103 | 104 | # mkdocs documentation 105 | /site 106 | 107 | # mypy 108 | .mypy_cache/ 109 | .dmypy.json 110 | dmypy.json 111 | 112 | # Pyre type checker 113 | .pyre/ 114 | 115 | # End of https://www.gitignore.io/api/python 116 | 117 | # OSX files 118 | .DS_Store 119 | 120 | # Yarn cache 121 | .yarn/ 122 | 123 | # Upgrade script 124 | _temp_extension 125 | 126 | # Jupyter 127 | Untitled*.ipynb -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: main 6 | pull_request: 7 | branches: '*' 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Base Setup 22 | uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 23 | 24 | - name: Install dependencies 25 | run: python -m pip install -U "jupyterlab>=4.0.0,<5" 26 | 27 | - name: Lint the extension 28 | run: | 29 | set -eux 30 | jlpm 31 | jlpm run lint:check 32 | 33 | - name: Build the extension 34 | run: | 35 | set -eux 36 | python -m pip install .[test] 37 | 38 | jupyter labextension list 39 | jupyter labextension list 2>&1 | grep -ie "jupyterlab-js-logs.*OK" 40 | python -m jupyterlab.browser_check 41 | 42 | - name: Package the extension 43 | run: | 44 | set -eux 45 | 46 | pip install build 47 | python -m build 48 | pip uninstall -y "jupyterlab_js_logs" jupyterlab 49 | 50 | - name: Upload extension packages 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: extension-artifacts 54 | path: dist/jupyterlab_js_logs* 55 | if-no-files-found: error 56 | 57 | test_isolated: 58 | needs: build 59 | runs-on: ubuntu-latest 60 | 61 | steps: 62 | - name: Install Python 63 | uses: actions/setup-python@v5 64 | with: 65 | python-version: '3.9' 66 | architecture: 'x64' 67 | - uses: actions/download-artifact@v4 68 | with: 69 | name: extension-artifacts 70 | - name: Install and Test 71 | run: | 72 | set -eux 73 | # Remove NodeJS, twice to take care of system and locally installed node versions. 74 | sudo rm -rf $(which node) 75 | sudo rm -rf $(which node) 76 | 77 | pip install "jupyterlab>=4.0.0,<5" jupyterlab_js_logs*.whl 78 | 79 | 80 | jupyter labextension list 81 | jupyter labextension list 2>&1 | grep -ie "jupyterlab-js-logs.*OK" 82 | python -m jupyterlab.browser_check --no-browser-test 83 | 84 | 85 | check_links: 86 | name: Check Links 87 | runs-on: ubuntu-latest 88 | timeout-minutes: 15 89 | steps: 90 | - uses: actions/checkout@v4 91 | - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 92 | - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1 93 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version>=0.3.2"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "jupyterlab_js_logs" 7 | readme = "README.md" 8 | license = { file = "LICENSE" } 9 | requires-python = ">=3.8" 10 | classifiers = [ 11 | "Framework :: Jupyter", 12 | "Framework :: Jupyter :: JupyterLab", 13 | "Framework :: Jupyter :: JupyterLab :: 4", 14 | "Framework :: Jupyter :: JupyterLab :: Extensions", 15 | "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt", 16 | "License :: OSI Approved :: BSD License", 17 | "Programming Language :: Python", 18 | "Programming Language :: Python :: 3", 19 | "Programming Language :: Python :: 3.8", 20 | "Programming Language :: Python :: 3.9", 21 | "Programming Language :: Python :: 3.10", 22 | "Programming Language :: Python :: 3.11", 23 | "Programming Language :: Python :: 3.12", 24 | ] 25 | dependencies = [ 26 | ] 27 | dynamic = ["version", "description", "authors", "urls", "keywords"] 28 | 29 | [tool.hatch.version] 30 | source = "nodejs" 31 | 32 | [tool.hatch.metadata.hooks.nodejs] 33 | fields = ["description", "authors", "urls", "keywords"] 34 | 35 | [tool.hatch.build.targets.sdist] 36 | artifacts = ["jupyterlab_js_logs/labextension"] 37 | exclude = [".github", "binder"] 38 | 39 | [tool.hatch.build.targets.wheel.shared-data] 40 | "jupyterlab_js_logs/labextension" = "share/jupyter/labextensions/jupyterlab-js-logs" 41 | "install.json" = "share/jupyter/labextensions/jupyterlab-js-logs/install.json" 42 | 43 | [tool.hatch.build.hooks.version] 44 | path = "jupyterlab_js_logs/_version.py" 45 | 46 | [tool.hatch.build.hooks.jupyter-builder] 47 | dependencies = ["hatch-jupyter-builder>=0.5"] 48 | build-function = "hatch_jupyter_builder.npm_builder" 49 | ensured-targets = [ 50 | "jupyterlab_js_logs/labextension/static/style.js", 51 | "jupyterlab_js_logs/labextension/package.json", 52 | ] 53 | skip-if-exists = ["jupyterlab_js_logs/labextension/static/style.js"] 54 | 55 | [tool.hatch.build.hooks.jupyter-builder.build-kwargs] 56 | build_cmd = "build:prod" 57 | npm = ["jlpm"] 58 | 59 | [tool.hatch.build.hooks.jupyter-builder.editable-build-kwargs] 60 | build_cmd = "install:extension" 61 | npm = ["jlpm"] 62 | source_dir = "src" 63 | build_dir = "jupyterlab_js_logs/labextension" 64 | 65 | [tool.jupyter-releaser.options] 66 | version_cmd = "hatch version" 67 | 68 | [tool.jupyter-releaser.hooks] 69 | before-build-npm = [ 70 | "python -m pip install 'jupyterlab>=4.0.0,<5'", 71 | "jlpm", 72 | "jlpm build:prod" 73 | ] 74 | before-build-python = ["jlpm clean:all"] 75 | 76 | [tool.check-wheel-contents] 77 | ignore = ["W002"] 78 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Making a new release of jupyterlab_js_logs 2 | 3 | The extension can be published to `PyPI` and `npm` manually or using the [Jupyter Releaser](https://github.com/jupyter-server/jupyter_releaser). 4 | 5 | ## Manual release 6 | 7 | ### Python package 8 | 9 | This extension can be distributed as Python packages. All of the Python 10 | packaging instructions are in the `pyproject.toml` file to wrap your extension in a 11 | Python package. Before generating a package, you first need to install some tools: 12 | 13 | ```bash 14 | pip install build twine hatch 15 | ``` 16 | 17 | Bump the version using `hatch`. By default this will create a tag. 18 | See the docs on [hatch-nodejs-version](https://github.com/agoose77/hatch-nodejs-version#semver) for details. 19 | 20 | ```bash 21 | hatch version 22 | ``` 23 | 24 | Make sure to clean up all the development files before building the package: 25 | 26 | ```bash 27 | jlpm clean:all 28 | ``` 29 | 30 | You could also clean up the local git repository: 31 | 32 | ```bash 33 | git clean -dfX 34 | ``` 35 | 36 | To create a Python source package (`.tar.gz`) and the binary package (`.whl`) in the `dist/` directory, do: 37 | 38 | ```bash 39 | python -m build 40 | ``` 41 | 42 | > `python setup.py sdist bdist_wheel` is deprecated and will not work for this package. 43 | 44 | Then to upload the package to PyPI, do: 45 | 46 | ```bash 47 | twine upload dist/* 48 | ``` 49 | 50 | ### NPM package 51 | 52 | To publish the frontend part of the extension as a NPM package, do: 53 | 54 | ```bash 55 | npm login 56 | npm publish --access public 57 | ``` 58 | 59 | ## Automated releases with the Jupyter Releaser 60 | 61 | The extension repository should already be compatible with the Jupyter Releaser. But 62 | the GitHub repository and the package managers need to be properly set up. Please 63 | follow the instructions of the Jupyter Releaser [checklist](https://jupyter-releaser.readthedocs.io/en/latest/how_to_guides/convert_repo_from_repo.html). 64 | 65 | Here is a summary of the steps to cut a new release: 66 | 67 | - Go to the Actions panel 68 | - Run the "Step 1: Prep Release" workflow 69 | - Check the draft changelog 70 | - Run the "Step 2: Publish Release" workflow 71 | 72 | > [!NOTE] 73 | > Check out the [workflow documentation](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html) 74 | > for more information. 75 | 76 | ## Publishing to `conda-forge` 77 | 78 | If 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 79 | 80 | Otherwise a bot should pick up the new version publish to PyPI, and open a new PR on the feedstock repository automatically. 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jupyterlab-js-logs 2 | 3 | [![Extension status](https://img.shields.io/badge/status-ready-success 'ready to be used')](https://jupyterlab-contrib.github.io/) 4 | ![Github Actions Status](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/workflows/Build/badge.svg) 5 | [![lite-badge](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://jupyterlab-contrib.github.io/jupyterlab-js-logs) 6 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyterlab-contrib/jupyterlab-js-logs/main?urlpath=lab) 7 | 8 | JupyterLab extension to show the console logs from the browser dev tools console. 9 | 10 | https://github.com/user-attachments/assets/8d907bf6-8363-4b67-8431-22fca19f7d1e 11 | 12 | ## Requirements 13 | 14 | - JupyterLab >= 2.0 15 | 16 | ## Motivation 17 | 18 | The main purpose for this extension is to provide a way to surface the console logs from the browser dev tools console in the JupyterLab interface. 19 | Especially for users that are not familiar with the browser dev tools, this extension can be very useful to debug and understand the logs generated by the JavaScript code running in the browser. 20 | 21 | ## Install 22 | 23 | ```bash 24 | jupyter labextension install jupyterlab-js-logs 25 | ``` 26 | 27 | ## Contributing 28 | 29 | This extension was bootstrapped from the [custom-log-console](https://github.com/jupyterlab/extension-examples/tree/master/custom-log-console) example contributed by [Carlos Herrero](https://github.com/hbcarlos), from the [`jupyterlab-extension-examples`](https://github.com/jupyterlab/extension-examples) repository. 30 | 31 | ### Install 32 | 33 | The `jlpm` command is JupyterLab's pinned version of 34 | [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use 35 | `yarn` or `npm` in lieu of `jlpm` below. 36 | 37 | ```bash 38 | # Clone the repo to your local environment 39 | # Move to jupyterlab-js-logs directory 40 | 41 | # Install dependencies 42 | jlpm 43 | # Build Typescript source 44 | jlpm build 45 | # Link your development version of the extension with JupyterLab 46 | jupyter labextension install . 47 | # Rebuild Typescript source after making changes 48 | jlpm build 49 | # Rebuild JupyterLab after making any changes 50 | jupyter lab build 51 | ``` 52 | 53 | You can watch the source directory and run JupyterLab in watch mode to watch for changes in the extension's source and automatically rebuild the extension and application. 54 | 55 | ```bash 56 | # Watch the source directory in another terminal tab 57 | jlpm watch 58 | # Run jupyterlab in watch mode in one terminal tab 59 | jupyter lab --watch 60 | ``` 61 | 62 | Now every change will be built locally and bundled into JupyterLab. Be sure to refresh your browser page after saving file changes to reload the extension (note: you'll need to wait for webpack to finish, which can take 10s+ at times). 63 | 64 | ### Uninstall 65 | 66 | ```bash 67 | 68 | jupyter labextension uninstall jupyterlab-js-logs 69 | ``` 70 | -------------------------------------------------------------------------------- /src/logLevelSwitcher.tsx: -------------------------------------------------------------------------------- 1 | import { ReactWidget } from '@jupyterlab/apputils'; 2 | 3 | import { LogConsolePanel, LogLevel } from '@jupyterlab/logconsole'; 4 | 5 | import { HTMLSelect } from '@jupyterlab/ui-components'; 6 | 7 | import { IChangedArgs } from '@jupyterlab/coreutils'; 8 | 9 | import { UUID } from '@lumino/coreutils'; 10 | 11 | import React from 'react'; 12 | 13 | /** 14 | * A toolbar widget that switches log levels. 15 | */ 16 | export default class LogLevelSwitcher extends ReactWidget { 17 | /** 18 | * Construct a new cell type switcher. 19 | * 20 | * @param widget The log console panel 21 | */ 22 | constructor(widget: LogConsolePanel) { 23 | super(); 24 | this.addClass('jp-LogConsole-toolbarLogLevel'); 25 | this._logConsole = widget; 26 | if (this._logConsole.logger) { 27 | this._logConsole.logger.level = 'debug'; 28 | } 29 | if (widget.source) { 30 | this.update(); 31 | } 32 | widget.sourceChanged.connect(this._updateSource, this); 33 | } 34 | 35 | private _updateSource( 36 | sender: LogConsolePanel, 37 | { oldValue, newValue }: IChangedArgs 38 | ): void { 39 | // Transfer stateChanged handler to new source logger 40 | if (oldValue !== null) { 41 | const logger = sender.loggerRegistry.getLogger(oldValue); 42 | logger.stateChanged.disconnect(this.update, this); 43 | } 44 | if (newValue !== null) { 45 | const logger = sender.loggerRegistry.getLogger(newValue); 46 | logger.stateChanged.connect(this.update, this); 47 | } 48 | this.update(); 49 | } 50 | 51 | /** 52 | * Handle `change` events for the HTMLSelect component. 53 | * 54 | * @param event The HTML select event. 55 | */ 56 | handleChange = (event: React.ChangeEvent): void => { 57 | if (this._logConsole.logger) { 58 | this._logConsole.logger.level = event.target.value as LogLevel; 59 | } 60 | this.update(); 61 | }; 62 | 63 | /** 64 | * Handle `keydown` events for the HTMLSelect component. 65 | * 66 | * @param event The keyboard event. 67 | */ 68 | handleKeyDown = (event: React.KeyboardEvent): void => { 69 | if (event.keyCode === 13) { 70 | this._logConsole.activate(); 71 | } 72 | }; 73 | 74 | render(): JSX.Element { 75 | const logger = this._logConsole.logger; 76 | return ( 77 | <> 78 | 88 | ({ label, value: label.toLowerCase() }) 101 | ) 102 | } 103 | /> 104 | 105 | ); 106 | } 107 | private _logConsole: LogConsolePanel; 108 | private _id = `level-${UUID.uuid4()}`; 109 | } 110 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | 5 | ## 0.2.6 6 | 7 | ([Full Changelog](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/compare/v0.2.5...854af201b8186f50ce781096fc46ccec758adce0)) 8 | 9 | ### Enhancements made 10 | 11 | - Improve Check Release workflow [#23](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/23) ([@hbcarlos](https://github.com/hbcarlos)) 12 | 13 | ### Bugs fixed 14 | 15 | - Remove prepare script and revert PR #20 [#24](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/24) ([@hbcarlos](https://github.com/hbcarlos)) 16 | 17 | ### Maintenance and upkeep improvements 18 | 19 | - Upload artifacts only on Check Release [#25](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/25) ([@hbcarlos](https://github.com/hbcarlos)) 20 | - Remove prepare script and revert PR #20 [#24](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/24) ([@hbcarlos](https://github.com/hbcarlos)) 21 | - Improve Check Release workflow [#23](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/23) ([@hbcarlos](https://github.com/hbcarlos)) 22 | 23 | ### Contributors to this release 24 | 25 | ([GitHub contributors page for this release](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/graphs/contributors?from=2022-01-21&to=2022-01-25&type=c)) 26 | 27 | [@hbcarlos](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-js-logs+involves%3Ahbcarlos+updated%3A2022-01-21..2022-01-25&type=Issues) | [@jtpio](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-js-logs+involves%3Ajtpio+updated%3A2022-01-21..2022-01-25&type=Issues) 28 | 29 | 30 | 31 | ## 0.2.5 32 | 33 | ([Full Changelog](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/compare/v0.2.4...2b893318a9ee85a3c649fcb92f1ded9ba732a00d)) 34 | 35 | ### Merged PRs 36 | 37 | - Build js in prod mode for the release [#20](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/20) ([@hbcarlos](https://github.com/hbcarlos)) 38 | 39 | ### Contributors to this release 40 | 41 | ([GitHub contributors page for this release](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/graphs/contributors?from=2021-09-15&to=2022-01-21&type=c)) 42 | 43 | [@hbcarlos](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-js-logs+involves%3Ahbcarlos+updated%3A2021-09-15..2022-01-21&type=Issues) 44 | 45 | ## 0.2.4 46 | 47 | ([Full Changelog](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/compare/v0.2.3...62cef996ed0c8434147907aa3a8bdb7bf6ffc9a6)) 48 | 49 | ### Maintenance and upkeep improvements 50 | 51 | - Set name as jupyterlab-js-logs [#17](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/17) ([@jtpio](https://github.com/jtpio)) 52 | 53 | ### Documentation improvements 54 | 55 | - Update Binder link to point to the `main` branch [#16](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/16) ([@jtpio](https://github.com/jtpio)) 56 | 57 | ### Contributors to this release 58 | 59 | ([GitHub contributors page for this release](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/graphs/contributors?from=2021-09-14&to=2021-09-15&type=c)) 60 | 61 | [@jtpio](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-js-logs+involves%3Ajtpio+updated%3A2021-09-14..2021-09-15&type=Issues) 62 | 63 | ## 0.2.3 64 | 65 | ([Full Changelog](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/compare/0.2.2...14e59dc4e5d15a3a6246d8bb1967e18fddac9b10)) 66 | 67 | ### Bugs fixed 68 | 69 | - Remove circular references when stringifying an object [#11](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/11) ([@hbcarlos](https://github.com/hbcarlos)) 70 | 71 | ### Maintenance and upkeep improvements 72 | 73 | - Add `publishConfig` to `package.json` [#13](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/13) ([@jtpio](https://github.com/jtpio)) 74 | - Add github action to check release [#12](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/pull/12) ([@hbcarlos](https://github.com/hbcarlos)) 75 | 76 | ### Contributors to this release 77 | 78 | ([GitHub contributors page for this release](https://github.com/jupyterlab-contrib/jupyterlab-js-logs/graphs/contributors?from=2021-08-06&to=2021-09-14&type=c)) 79 | 80 | [@hbcarlos](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-js-logs+involves%3Ahbcarlos+updated%3A2021-08-06..2021-09-14&type=Issues) | [@jtpio](https://github.com/search?q=repo%3AQuantStack%2Fjupyterlab-js-logs+involves%3Ajtpio+updated%3A2021-08-06..2021-09-14&type=Issues) 81 | 82 | ## 0.2.2 83 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyterlab-js-logs", 3 | "version": "0.2.6", 4 | "description": "JupyterLab extension to show the js logs from the browser dev tools console", 5 | "keywords": [ 6 | "jupyter", 7 | "jupyterlab", 8 | "jupyterlab-extension" 9 | ], 10 | "homepage": "https://github.com/jupyterlab-contrib/jupyterlab-js-logs", 11 | "bugs": { 12 | "url": "https://github.com/jupyterlab-contrib/jupyterlab-js-logs/issues" 13 | }, 14 | "license": "BSD-3-Clause", 15 | "author": { 16 | "name": "JupyterLab Contrib Team", 17 | "email": "" 18 | }, 19 | "files": [ 20 | "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", 21 | "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", 22 | "style/index.js" 23 | ], 24 | "main": "lib/index.js", 25 | "types": "lib/index.d.ts", 26 | "style": "style/index.css", 27 | "styleModule": "style/index.js", 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/jupyterlab-contrib/jupyterlab-js-logs.git" 31 | }, 32 | "scripts": { 33 | "build": "jlpm build:lib && jlpm build:labextension:dev", 34 | "build:labextension": "jupyter labextension build .", 35 | "build:labextension:dev": "jupyter labextension build --development True .", 36 | "build:lib": "tsc --sourceMap", 37 | "build:lib:prod": "tsc", 38 | "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension", 39 | "clean": "jlpm clean:lib", 40 | "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache", 41 | "clean:labextension": "rimraf jupyterlab_js_logs/labextension jupyterlab_js_logs/_version.py", 42 | "clean:lib": "rimraf lib tsconfig.tsbuildinfo", 43 | "clean:lintcache": "rimraf .eslintcache .stylelintcache", 44 | "eslint": "jlpm eslint:check --fix", 45 | "eslint:check": "eslint . --cache --ext .ts,.tsx", 46 | "install:extension": "jlpm build", 47 | "lint": "jlpm stylelint && jlpm prettier && jlpm eslint", 48 | "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check", 49 | "prettier": "jlpm prettier:base --write --list-different", 50 | "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", 51 | "prettier:check": "jlpm prettier:base --check", 52 | "stylelint": "jlpm stylelint:check --fix", 53 | "stylelint:check": "stylelint --cache \"style/**/*.css\"", 54 | "watch": "run-p watch:src watch:labextension", 55 | "watch:labextension": "jupyter labextension watch .", 56 | "watch:src": "tsc -w --sourceMap" 57 | }, 58 | "dependencies": { 59 | "@jupyterlab/application": "^4.2.5", 60 | "@jupyterlab/apputils": "^4.3.5", 61 | "@jupyterlab/coreutils": "^6.2.5", 62 | "@jupyterlab/logconsole": "^4.2.5", 63 | "@jupyterlab/mainmenu": "^4.2.5", 64 | "@jupyterlab/nbformat": "^4.2.5", 65 | "@jupyterlab/rendermime": "^4.2.5", 66 | "@jupyterlab/ui-components": "^4.2.5", 67 | "@lumino/coreutils": "^2.2.0", 68 | "@lumino/widgets": "^2.5.0" 69 | }, 70 | "devDependencies": { 71 | "@jupyterlab/builder": "^4.0.0", 72 | "@types/json-schema": "^7.0.11", 73 | "@types/react": "^18.0.26", 74 | "@types/react-addons-linked-state-mixin": "^0.14.22", 75 | "@typescript-eslint/eslint-plugin": "^6.1.0", 76 | "@typescript-eslint/parser": "^6.1.0", 77 | "css-loader": "^6.7.1", 78 | "eslint": "^8.36.0", 79 | "eslint-config-prettier": "^8.8.0", 80 | "eslint-plugin-prettier": "^5.0.0", 81 | "npm-run-all": "^4.1.5", 82 | "prettier": "^3.0.0", 83 | "rimraf": "^5.0.1", 84 | "source-map-loader": "^1.0.2", 85 | "style-loader": "^3.3.1", 86 | "stylelint": "^15.10.1", 87 | "stylelint-config-recommended": "^13.0.0", 88 | "stylelint-config-standard": "^34.0.0", 89 | "stylelint-csstree-validator": "^3.0.0", 90 | "stylelint-prettier": "^4.0.0", 91 | "typescript": "~5.0.2", 92 | "yjs": "^13.5.40" 93 | }, 94 | "sideEffects": [ 95 | "style/*.css", 96 | "style/index.js" 97 | ], 98 | "jupyterlab": { 99 | "extension": true, 100 | "outputDir": "jupyterlab_js_logs/labextension" 101 | }, 102 | "publishConfig": { 103 | "access": "public" 104 | }, 105 | "eslintConfig": { 106 | "extends": [ 107 | "eslint:recommended", 108 | "plugin:@typescript-eslint/eslint-recommended", 109 | "plugin:@typescript-eslint/recommended", 110 | "plugin:prettier/recommended" 111 | ], 112 | "parser": "@typescript-eslint/parser", 113 | "parserOptions": { 114 | "project": "tsconfig.json", 115 | "sourceType": "module" 116 | }, 117 | "plugins": [ 118 | "@typescript-eslint" 119 | ], 120 | "rules": { 121 | "@typescript-eslint/naming-convention": [ 122 | "error", 123 | { 124 | "selector": "interface", 125 | "format": [ 126 | "PascalCase" 127 | ], 128 | "custom": { 129 | "regex": "^I[A-Z]", 130 | "match": true 131 | } 132 | } 133 | ], 134 | "@typescript-eslint/no-unused-vars": [ 135 | "warn", 136 | { 137 | "args": "none" 138 | } 139 | ], 140 | "@typescript-eslint/no-explicit-any": "off", 141 | "@typescript-eslint/no-namespace": "off", 142 | "@typescript-eslint/no-use-before-define": "off", 143 | "@typescript-eslint/quotes": [ 144 | "error", 145 | "single", 146 | { 147 | "avoidEscape": true, 148 | "allowTemplateLiterals": false 149 | } 150 | ], 151 | "curly": [ 152 | "error", 153 | "all" 154 | ], 155 | "eqeqeq": "error", 156 | "prefer-arrow-callback": "error" 157 | } 158 | }, 159 | "eslintIgnore": [ 160 | "node_modules", 161 | "dist", 162 | "coverage", 163 | "**/*.d.ts" 164 | ], 165 | "prettier": { 166 | "singleQuote": true, 167 | "trailingComma": "none", 168 | "arrowParens": "avoid", 169 | "endOfLine": "auto", 170 | "overrides": [ 171 | { 172 | "files": "package.json", 173 | "options": { 174 | "tabWidth": 4 175 | } 176 | } 177 | ] 178 | }, 179 | "stylelint": { 180 | "extends": [ 181 | "stylelint-config-recommended", 182 | "stylelint-config-standard", 183 | "stylelint-prettier/recommended" 184 | ], 185 | "plugins": [ 186 | "stylelint-csstree-validator" 187 | ], 188 | "rules": { 189 | "csstree/validator": true, 190 | "property-no-vendor-prefix": null, 191 | "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$", 192 | "selector-no-vendor-prefix": null, 193 | "value-no-vendor-prefix": null 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | JupyterFrontEnd, 3 | JupyterFrontEndPlugin, 4 | ILayoutRestorer 5 | } from '@jupyterlab/application'; 6 | 7 | import { 8 | ICommandPalette, 9 | MainAreaWidget, 10 | WidgetTracker, 11 | CommandToolbarButton 12 | } from '@jupyterlab/apputils'; 13 | 14 | import { LoggerRegistry, LogConsolePanel } from '@jupyterlab/logconsole'; 15 | 16 | import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; 17 | 18 | import { addIcon, clearIcon, LabIcon } from '@jupyterlab/ui-components'; 19 | 20 | import LogLevelSwitcher from './logLevelSwitcher'; 21 | 22 | import jsIconStr from '../style/js.svg'; 23 | 24 | /** 25 | * The command IDs used by the js-logs plugin. 26 | */ 27 | export namespace CommandIDs { 28 | export const checkpoint = 'js-logs:checkpoint'; 29 | 30 | export const clear = 'js-logs:clear'; 31 | 32 | export const level = 'js-logs:level'; 33 | 34 | export const open = 'js-logs:open'; 35 | } 36 | 37 | /** 38 | * The main jupyterlab-js-logs plugin. 39 | */ 40 | const extension: JupyterFrontEndPlugin = { 41 | id: 'js-logs', 42 | autoStart: true, 43 | requires: [IRenderMimeRegistry], 44 | optional: [ICommandPalette, ILayoutRestorer], 45 | activate: ( 46 | app: JupyterFrontEnd, 47 | rendermime: IRenderMimeRegistry, 48 | palette: ICommandPalette | null, 49 | restorer: ILayoutRestorer | null 50 | ) => { 51 | const { commands } = app; 52 | 53 | let logConsolePanel: LogConsolePanel | null = null; 54 | let logConsoleWidget: MainAreaWidget | null = null; 55 | 56 | const tracker = new WidgetTracker>({ 57 | namespace: 'jupyterlab-js-logs' 58 | }); 59 | 60 | const jsIcon = new LabIcon({ 61 | name: 'js-logs:js-icon', 62 | svgstr: jsIconStr 63 | }); 64 | 65 | const createLogConsoleWidget = (): void => { 66 | logConsolePanel = new LogConsolePanel( 67 | new LoggerRegistry({ 68 | defaultRendermime: rendermime, 69 | maxLength: 1000 70 | }) 71 | ); 72 | 73 | logConsolePanel.source = 'js-logs'; 74 | 75 | logConsoleWidget = new MainAreaWidget({ 76 | content: logConsolePanel 77 | }); 78 | logConsoleWidget.addClass('jp-LogConsole'); 79 | logConsoleWidget.title.label = 'Dev Tools Console Logs'; 80 | logConsoleWidget.title.icon = jsIcon; 81 | 82 | logConsoleWidget.toolbar.addItem( 83 | 'checkpoint', 84 | new CommandToolbarButton({ 85 | commands, 86 | id: CommandIDs.checkpoint 87 | }) 88 | ); 89 | 90 | logConsoleWidget.toolbar.addItem( 91 | 'clear', 92 | new CommandToolbarButton({ 93 | commands, 94 | id: CommandIDs.clear 95 | }) 96 | ); 97 | 98 | logConsoleWidget.toolbar.addItem( 99 | 'level', 100 | new LogLevelSwitcher(logConsoleWidget.content) 101 | ); 102 | 103 | logConsoleWidget.disposed.connect(() => { 104 | logConsoleWidget = null; 105 | logConsolePanel = null; 106 | commands.notifyCommandChanged(); 107 | }); 108 | 109 | app.shell.add(logConsoleWidget, 'main', { mode: 'split-bottom' }); 110 | void tracker.add(logConsoleWidget); 111 | 112 | logConsoleWidget.update(); 113 | commands.notifyCommandChanged(); 114 | }; 115 | 116 | commands.addCommand(CommandIDs.checkpoint, { 117 | execute: () => logConsolePanel?.logger?.checkpoint(), 118 | icon: addIcon, 119 | isEnabled: () => logConsolePanel?.source !== null, 120 | label: 'Add Checkpoint' 121 | }); 122 | 123 | commands.addCommand(CommandIDs.clear, { 124 | execute: () => logConsolePanel?.logger?.clear(), 125 | icon: clearIcon, 126 | isEnabled: () => logConsolePanel?.source !== null, 127 | label: 'Clear Log' 128 | }); 129 | 130 | commands.addCommand(CommandIDs.level, { 131 | execute: (args: any) => { 132 | if (logConsolePanel?.logger) { 133 | logConsolePanel.logger.level = args.level; 134 | } 135 | }, 136 | isEnabled: () => logConsolePanel?.source !== null, 137 | label: args => `Set Log Level to ${args.level as string}` 138 | }); 139 | 140 | commands.addCommand(CommandIDs.open, { 141 | label: 'Show Dev Tools Console Logs', 142 | caption: 'Show Dev Tools Console Logs', 143 | isToggled: () => logConsoleWidget !== null, 144 | execute: () => { 145 | if (logConsoleWidget) { 146 | logConsoleWidget.dispose(); 147 | } else { 148 | createLogConsoleWidget(); 149 | } 150 | } 151 | }); 152 | 153 | window.onerror = (msg, url, lineNo, columnNo, error): boolean => { 154 | logConsolePanel?.logger?.log({ 155 | type: 'text', 156 | level: 'critical', 157 | data: `${url}:${lineNo}:${columnNo} ${msg}\n${error}` 158 | }); 159 | return false; 160 | }; 161 | 162 | const _debug = console.debug; 163 | const _log = console.log; 164 | const _info = console.info; 165 | const _warn = console.warn; 166 | const _error = console.error; 167 | 168 | // const _exception = console.exception; 169 | const _trace = console.trace; 170 | const _table = console.table; 171 | 172 | // https://stackoverflow.com/a/11616993 173 | // We need to clear cache after each use. 174 | let cache: any = []; 175 | const refReplacer = (key: any, value: any) => { 176 | if (typeof value === 'object' && value !== null) { 177 | if (cache.indexOf(value) !== -1) { 178 | return; 179 | } 180 | cache.push(value); 181 | } 182 | return value; 183 | }; 184 | 185 | const parseArgs = (args: any[]): string => { 186 | let data = ''; 187 | args.forEach(arg => { 188 | try { 189 | if (arg instanceof Error) { 190 | data += arg.stack || arg.message || arg; 191 | } else { 192 | data += 193 | (typeof arg === 'object' && arg !== null 194 | ? JSON.stringify(arg) 195 | : arg) + ' '; 196 | } 197 | } catch (e) { 198 | try { 199 | const msg = 200 | 'This error contains a object with a circular reference. Duplicated attributes might have been dropped during the process of removing the reference.\n'; 201 | const obj = JSON.stringify(arg, refReplacer); 202 | cache = []; 203 | console.error(msg, obj); 204 | data += obj; 205 | } catch (e) { 206 | data += ' '; 207 | } 208 | } 209 | }); 210 | return data; 211 | }; 212 | 213 | window.console.debug = (...args: any[]): void => { 214 | logConsolePanel?.logger?.log({ 215 | type: 'text', 216 | level: 'debug', 217 | data: parseArgs(args) 218 | }); 219 | _debug(...args); 220 | }; 221 | 222 | window.console.log = (...args: any[]): void => { 223 | logConsolePanel?.logger?.log({ 224 | type: 'text', 225 | level: 'debug', 226 | data: parseArgs(args) 227 | }); 228 | _log(...args); 229 | }; 230 | 231 | window.console.info = (...args: any[]): void => { 232 | logConsolePanel?.logger?.log({ 233 | type: 'text', 234 | level: 'info', 235 | data: parseArgs(args) 236 | }); 237 | _info(...args); 238 | }; 239 | 240 | window.console.warn = (...args: any[]): void => { 241 | logConsolePanel?.logger?.log({ 242 | type: 'text', 243 | level: 'warning', 244 | data: parseArgs(args) 245 | }); 246 | _warn(...args); 247 | }; 248 | 249 | window.console.error = (...args: any[]): void => { 250 | logConsolePanel?.logger?.log({ 251 | type: 'text', 252 | level: 'critical', 253 | data: parseArgs(args) 254 | }); 255 | _error(...args); 256 | }; 257 | 258 | // window.console.exception = (message?: string, ...args: any[]): void => { 259 | // logConsolePanel?.logger?.log({ 260 | // type: 'text', 261 | // level: 'critical', 262 | // data: `Exception: ${message}\n${parseArgs(args)}` 263 | // }); 264 | // _exception(...args); 265 | // }; 266 | 267 | window.console.trace = (...args: any[]): void => { 268 | logConsolePanel?.logger?.log({ 269 | type: 'text', 270 | level: 'info', 271 | data: parseArgs(args) 272 | }); 273 | _trace(...args); 274 | }; 275 | 276 | window.console.table = (...args: any[]): void => { 277 | logConsolePanel?.logger?.log({ 278 | type: 'text', 279 | level: 'info', 280 | data: parseArgs(args) 281 | }); 282 | _table(...args); 283 | }; 284 | 285 | if (palette) { 286 | palette.addItem({ 287 | command: CommandIDs.open, 288 | category: 'Developer' 289 | }); 290 | } 291 | 292 | if (restorer) { 293 | restorer.restore(tracker, { 294 | command: CommandIDs.open, 295 | name: () => 'js-logs' 296 | }); 297 | } 298 | } 299 | }; 300 | 301 | export default extension; 302 | --------------------------------------------------------------------------------