Compare commits
No commits in common. 'main' and '6.4' have entirely different histories.
@ -0,0 +1,19 @@
|
||||
[bumpversion]
|
||||
current_version = 7, 0, 0, 'alpha', 2
|
||||
commit = False
|
||||
tag = False
|
||||
parse = (?P<major>\d+)\,\ (?P<minor>\d+)\,\ (?P<patch>\d+)\,\ \'(?P<release>\S+)\'\,\ (?P<build>\d+)
|
||||
serialize =
|
||||
{major}, {minor}, {patch}, '{release}', {build}
|
||||
|
||||
[bumpversion:part:release]
|
||||
optional_value = final
|
||||
values =
|
||||
alpha
|
||||
beta
|
||||
candidate
|
||||
final
|
||||
|
||||
[bumpversion:part:build]
|
||||
|
||||
[bumpversion:file:notebook/_version.py]
|
||||
@ -1,13 +0,0 @@
|
||||
FROM mcr.microsoft.com/devcontainers/base:jammy
|
||||
|
||||
ARG PIXI_VERSION=v0.42.1
|
||||
|
||||
RUN curl -L -o /usr/local/bin/pixi -fsSL --compressed "https://github.com/prefix-dev/pixi/releases/download/${PIXI_VERSION}/pixi-$(uname -m)-unknown-linux-musl" \
|
||||
&& chmod +x /usr/local/bin/pixi \
|
||||
&& pixi info
|
||||
|
||||
# set some user and workdir settings to work nicely with vscode
|
||||
USER vscode
|
||||
WORKDIR /home/vscode
|
||||
|
||||
RUN echo 'eval "$(pixi completion -s bash)"' >> /home/vscode/.bashrc
|
||||
@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "Jupyter Notebook",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"context": ".."
|
||||
},
|
||||
"forwardPorts": [8888],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": ["ms-python.python", "charliermarsh.ruff", "GitHub.copilot"]
|
||||
}
|
||||
},
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
|
||||
},
|
||||
"mounts": [
|
||||
"source=${localWorkspaceFolderBasename}-pixi,target=${containerWorkspaceFolder}/.pixi,type=volume"
|
||||
],
|
||||
"postCreateCommand": "sudo chown vscode .pixi && pixi install && pixi run develop && pixi run pre-commit install -f"
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
# Run auto-formatters: https://github.com/jupyter/notebook/pull/6335
|
||||
a7717d90f128368296fe3434deba5acd6031edab
|
||||
@ -1,2 +0,0 @@
|
||||
# SCM syntax highlighting & preventing 3-way merges
|
||||
pixi.lock merge=binary linguist-language=YAML linguist-generated=true
|
||||
@ -1,79 +1,50 @@
|
||||
---
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help us repair something that is currently broken
|
||||
labels: bug, status:Needs Triage
|
||||
---
|
||||
|
||||
<!-- Welcome! Thank you for contributing. These HTML comments will not render in the issue, but you can delete them once you've read them if you prefer! -->
|
||||
|
||||
<!--
|
||||
Right now, you're opening an issue to report a bug in Jupyter Notebook.
|
||||
|
||||
Please answer the following questions for yourself before submitting an issue
|
||||
- [ ] I checked the documentation and found no answer
|
||||
- [ ] I checked to make sure that this issue has not already been filed
|
||||
- [ ] I'm reporting the issue to the correct repository
|
||||
name: Is this a bug in Notebook? Open an issue.
|
||||
about: If you're not sure, feel free to post your question on Jupyter's Discourse channel.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---<!--
|
||||
BEFORE YOU OPEN AN ISSUE, PLEASE READ THIS
|
||||
|
||||
If you have further questions after reading below, please visit the Jupyter Notebook discourse channel (https://discourse.jupyter.org/) and submit your questions there. There are many more people in the Jupyter community that engage on that channel.
|
||||
Hello! Thank you for using Jupyter Notebook. We're glad you're here.
|
||||
|
||||
NOTE:
|
||||
Jupyter Notebook 6.x development is in maintenance-only mode. Bugs found in Notebook 6.x and that don't reproduce in Notebook 7.x may not get fixed.
|
||||
Right now, you're opening an issue. Before you do, let's make sure this is the right place to post your question/issue.
|
||||
|
||||
Work in this repository is focused on Jupyter Notebook 7.x, the former RetroLab project. The code base for Notebook 7.x is very different than Notebook 6.x. If you can, please try reproducing Notebook 6.x bugs with Notebook 7.x using the main branch of this repository.
|
||||
First, it's important to know that Jupyter Notebook development has moved into a phase of maintenance-only. There are very few people with limited time maintaining this repository. This means, we won't likely accept new features here. Instead, we recommend that you check out JupyterLab (https://github.com/jupyterlab/jupyterlab)—Jupyter's next generation Notebook interface.
|
||||
|
||||
We recommend that you check out JupyterLab (https://github.com/jupyterlab/jupyterlab), Jupyter's next generation Notebook interface.
|
||||
Here, we're looking for specific bugs in the Jupyter Notebook codebase. If you think you've identified such a bug, you can continue opening your issue here. We'd appreciate if you include as much detail as possible, i.e. links to the offending code, snapshots of the UI issue, code-blocks with your console logs, etc.
|
||||
|
||||
Here, we're looking for specific bugs in the Jupyter Notebook codebase. If you think you've identified such a bug, you can continue opening your issue here. We'd appreciate if you include as much detail as possible, such as links to the offending code, snapshots of the UI issue, code blocks with your console logs, etc.
|
||||
-->
|
||||
If you're having issues installing Jupyter Notebook, or you're having another issue and don't know how to proceed, try the following:
|
||||
|
||||
## Description
|
||||
1. scan the "What to do when things go wrong" (https://jupyter-notebook.readthedocs.io/en/stable/troubleshooting.html#what-to-do-when-things-go-wrong) page in our documentation to see if your question has already been answered
|
||||
|
||||
<!--Describe the bug clearly and concisely. Include screenshots if possible-->
|
||||
2. post your question on the Jupyter Notebook discourse channel (https://discourse.jupyter.org/c/notebook/31). There are many more people in the Jupyter community that engage on that channel.
|
||||
-->
|
||||
|
||||
## Reproduce
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
<!--Describe step-by-step instructions to reproduce the behavior-->
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '...'
|
||||
3. Scroll down to '...'
|
||||
4. See error '...'
|
||||
|
||||
<!--Describe how you diagnosed the issue. See the guidelines at
|
||||
https://jupyter-notebook.readthedocs.io/en/latest/troubleshooting.html -->
|
||||
|
||||
## Expected behavior
|
||||
|
||||
<!--Describe what you expected to happen-->
|
||||
|
||||
## Context
|
||||
|
||||
<!--Complete the following for context, and add any other relevant context-->
|
||||
|
||||
- Operating System and version: <!-- e.g. Linux Ubuntu 21.04 -->
|
||||
- Browser and version: <!-- e.g. Chrome 92 -->
|
||||
<!-- Please note the Notebook version you are working with. You can find this in the Help -> About Jupyter Notebook menu option or by running `jupyter --version` from your terminal -->
|
||||
- Jupyter Notebook version: <!-- e.g. 3.1.7 -->
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
<!--The more content you provide, the more we can help!-->
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
<details><summary>Troubleshoot Output</summary>
|
||||
<pre>
|
||||
Paste the output from running `jupyter troubleshoot` from the command line here.
|
||||
You may want to sanitize the paths in the output.
|
||||
</pre>
|
||||
</details>
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
<details><summary>Command Line Output</summary>
|
||||
<pre>
|
||||
Paste the output from your command line running `jupyter notebook` here, use `--debug` if possible.
|
||||
</pre>
|
||||
</details>
|
||||
**Desktop (please complete the following information):**
|
||||
|
||||
<details><summary>Browser Output</summary>
|
||||
<!--See https://webmasters.stackexchange.com/a/77337 for how to access the JavaScript console-->
|
||||
<pre>
|
||||
Paste the output from your browser Javascript console here, if applicable.
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
</pre>
|
||||
</details>
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Is this a common issue? See our Docs.
|
||||
url: https://jupyter-notebook.readthedocs.io/en/latest/troubleshooting.html#what-to-do-when-things-go-wrong
|
||||
about: Before opening an issue, make sure your issue hasn't already been addressed in the documentation.
|
||||
- name: 🤔 Support and all other questions, including if you're not sure what to do.
|
||||
url: https://jupyter-notebook.readthedocs.io/en/stable/troubleshooting.html#what-to-do-when-things-go-wrong
|
||||
about: Before posting an issue here, make sure your issue hasn't already been addressed here.
|
||||
- name: Do you need support or a question answered? See Jupyter Discourse.
|
||||
url: https://discourse.jupyter.org/c/notebook/31
|
||||
about: If you have a question or you're having issues installing Jupyter Notebook, try posting on Discourse.
|
||||
- name: 💬 Chat with the devs
|
||||
url: https://jupyter.zulipchat.com/
|
||||
about: Ask short questions about using Jupyter Notebook
|
||||
- name: 📝 Do you have a feature request that may be applied upstream? See JupyterLab.
|
||||
about: If you have a question or you're having issues installing Jupyter Notebook, try posting on Discourse. There are lots of friendly Joyvans there to help!
|
||||
- name: Do you have a feature request? See JupyterLab.
|
||||
url: https://github.com/jupyterlab/jupyterlab
|
||||
about: We recommend that you cross-reference JupyterLab for information when requesting new features and support for Notebook 7. We won't likely accept new features for Jupyter Notebook 6.x.
|
||||
about: Jupyter Notebook is in a maintenance-only phase. We won't likely accept new features; instead, we recommend you check out JupyterLab for new features and support.
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
---
|
||||
name: "\U0001F680 Feature Request"
|
||||
about: Suggest a new feature or a change
|
||||
labels: enhancement, status:Needs Triage
|
||||
---
|
||||
|
||||
<!-- Welcome! These HTML comments will not render in the issue, but you can delete them once you've read them if you prefer! -->
|
||||
|
||||
<!--
|
||||
Thanks for thinking of a way to improve Jupyter Notebook. If this solves a problem for you, then it probably solves that problem for lots of people! So the whole community will benefit from this request.
|
||||
|
||||
NOTE: Please note that Jupyter Notebook 6.x development is in maintenance-only mode.
|
||||
|
||||
Finally, please answer the following questions for yourself before submitting an issue.
|
||||
|
||||
- [ ] I checked to make sure that this issue has not already been filed
|
||||
- [ ] I'm reporting the issue to the correct repository
|
||||
|
||||
-->
|
||||
|
||||
### Problem
|
||||
|
||||
<!-- Provide a clear and concise description of what problem this feature will solve. For example:
|
||||
|
||||
* I'm always frustrated when [...] because [...]
|
||||
* I would like it if [...] happened when I [...] because [...]
|
||||
-->
|
||||
|
||||
### Proposed Solution
|
||||
|
||||
<!-- Provide a clear and concise description of a way to accomplish what you want. For example:
|
||||
|
||||
* Add an option so that when [...] [...] will happen
|
||||
-->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here. You can also include links to examples of other programs that have something similar to your request. For example:
|
||||
|
||||
* Another project [...] solved this by [...]
|
||||
-->
|
||||
@ -1,14 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: 'pip'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
@ -1,2 +0,0 @@
|
||||
addBinderLink: false
|
||||
triageLabel: 'status:Needs Triage'
|
||||
@ -1,17 +0,0 @@
|
||||
# https://github.com/marketplace/actions/auto-author-assign
|
||||
name: 'Auto Author Assign'
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
assign-author:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: toshimaru/auto-author-assign@v2.1.1
|
||||
@ -0,0 +1,35 @@
|
||||
name: Docs Tests
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Base Setup
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
with:
|
||||
python_version: '3.7'
|
||||
- name: Install the Python dependencies
|
||||
run: |
|
||||
pip install -e .[test] codecov
|
||||
pip install -r docs/doc-requirements.txt
|
||||
wget https://github.com/jgm/pandoc/releases/download/1.19.1/pandoc-1.19.1-1-amd64.deb && sudo dpkg -i pandoc-1.19.1-1-amd64.deb
|
||||
- name: List installed packages
|
||||
run: |
|
||||
pip freeze
|
||||
pip check
|
||||
- name: Run tests on documentation
|
||||
run: |
|
||||
EXIT_STATUS=0
|
||||
make -C docs/ html SPHINXOPTS="-W" || EXIT_STATUS=$?
|
||||
pytest --nbval --current-env docs || EXIT_STATUS=$?
|
||||
exit $EXIT_STATUS
|
||||
@ -1,16 +1,11 @@
|
||||
name: Enforce PR label
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled, opened, edited, synchronize]
|
||||
jobs:
|
||||
enforce-label:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: enforce-triage-label
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/enforce-label@v1
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
name: Update Playwright Snapshots
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-snapshots:
|
||||
if: >
|
||||
(
|
||||
github.event.issue.author_association == 'OWNER' ||
|
||||
github.event.issue.author_association == 'COLLABORATOR' ||
|
||||
github.event.issue.author_association == 'MEMBER' ||
|
||||
github.event.comment.author_association == 'OWNER' ||
|
||||
github.event.comment.author_association == 'COLLABORATOR' ||
|
||||
github.event.comment.author_association == 'MEMBER'
|
||||
) && github.event.issue.pull_request && (
|
||||
contains(github.event.comment.body, 'please update playwright snapshots') ||
|
||||
contains(github.event.comment.body, 'please update galata snapshots') ||
|
||||
contains(github.event.comment.body, 'please update snapshots')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Required by actions/update-snapshots
|
||||
contents: write
|
||||
pull-requests: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: [firefox, chromium]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: React to the triggering comment
|
||||
run: |
|
||||
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions --raw-field 'content=+1'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure git to use https
|
||||
run: git config --global hub.protocol https
|
||||
|
||||
- name: Get PR Info
|
||||
id: pr
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.issue.number }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
COMMENT_AT: ${{ github.event.comment.created_at }}
|
||||
run: |
|
||||
pr="$(gh api /repos/${GH_REPO}/pulls/${PR_NUMBER})"
|
||||
head_sha="$(echo "$pr" | jq -r .head.sha)"
|
||||
pushed_at="$(echo "$pr" | jq -r .pushed_at)"
|
||||
|
||||
if [[ $(date -d "$pushed_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
|
||||
echo "Updating is not allowed because the PR was pushed to (at $pushed_at) after the triggering comment was issued (at $COMMENT_AT)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout the branch from the PR that triggered the job
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: gh pr checkout ${{ github.event.issue.number }}
|
||||
|
||||
- name: Validate the fetched branch HEAD revision
|
||||
env:
|
||||
EXPECTED_SHA: ${{ steps.pr.outputs.head_sha }}
|
||||
run: |
|
||||
actual_sha="$(git rev-parse HEAD)"
|
||||
|
||||
if [[ "$actual_sha" != "$EXPECTED_SHA" ]]; then
|
||||
echo "The HEAD of the checked out branch ($actual_sha) differs from the HEAD commit available at the time when trigger comment was submitted ($EXPECTED_SHA)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Base Setup
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
|
||||
- name: Build
|
||||
uses: ./.github/actions/build-dist
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: notebook-dist-${{ github.run_number }}
|
||||
path: ./dist
|
||||
|
||||
- name: Install the package
|
||||
run: |
|
||||
cd dist
|
||||
python -m pip install -vv notebook*.whl
|
||||
|
||||
# disable git hooks
|
||||
git config core.hooksPath no-hooks
|
||||
|
||||
- name: Install the test dependencies
|
||||
run: |
|
||||
cd ui-tests
|
||||
jlpm
|
||||
jlpm playwright install
|
||||
|
||||
- name: Update snapshots
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/update-snapshots@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
npm_client: jlpm
|
||||
test_folder: ui-tests
|
||||
start_server_script: 'null'
|
||||
update_script: test:update --browser ${{ matrix.browser }}
|
||||
env:
|
||||
DEBUG: pw:webserver
|
||||
@ -1,49 +0,0 @@
|
||||
name: "Step 1: Prep Release"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_spec:
|
||||
description: "New Version Specifier"
|
||||
default: "next"
|
||||
required: false
|
||||
branch:
|
||||
description: "The branch to target"
|
||||
required: false
|
||||
post_version_spec:
|
||||
description: "Post Version Specifier"
|
||||
required: false
|
||||
silent:
|
||||
description: "Set a placeholder in the changelog and don't publish the release."
|
||||
required: false
|
||||
type: boolean
|
||||
since:
|
||||
description: "Use PRs with activity since this date or git reference"
|
||||
required: false
|
||||
since_last_stable:
|
||||
description: "Use PRs with activity since the last stable git tag"
|
||||
required: false
|
||||
type: boolean
|
||||
jobs:
|
||||
prep_release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
|
||||
- name: Prep Release
|
||||
id: prep-release
|
||||
uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version_spec: ${{ github.event.inputs.version_spec }}
|
||||
silent: ${{ github.event.inputs.silent }}
|
||||
post_version_spec: ${{ github.event.inputs.post_version_spec }}
|
||||
target: ${{ github.event.inputs.target }}
|
||||
branch: ${{ github.event.inputs.branch }}
|
||||
since: ${{ github.event.inputs.since }}
|
||||
since_last_stable: ${{ github.event.inputs.since_last_stable }}
|
||||
|
||||
- name: "** Next Step **"
|
||||
run: |
|
||||
echo "Optional): Review Draft Release: ${{ steps.prep-release.outputs.release_url }}"
|
||||
@ -1,34 +0,0 @@
|
||||
name: "Publish Changelog"
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "The branch to target"
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
publish_changelog:
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
steps:
|
||||
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Publish changelog
|
||||
id: publish-changelog
|
||||
uses: jupyter-server/jupyter_releaser/.github/actions/publish-changelog@v2
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
branch: ${{ github.event.inputs.branch }}
|
||||
|
||||
- name: "** Next Step **"
|
||||
run: |
|
||||
echo "Merge the changelog update PR: ${{ steps.publish-changelog.outputs.pr_url }}"
|
||||
@ -1,60 +0,0 @@
|
||||
name: "Step 2: Publish Release"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: "The target branch"
|
||||
required: false
|
||||
release_url:
|
||||
description: "The URL of the draft GitHub release"
|
||||
required: false
|
||||
steps_to_skip:
|
||||
description: "Comma separated list of steps to skip"
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
publish_release:
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
with:
|
||||
node_version: '24'
|
||||
|
||||
- uses: actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.APP_ID }}
|
||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
|
||||
- name: Populate Release
|
||||
id: populate-release
|
||||
uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
branch: ${{ github.event.inputs.branch }}
|
||||
release_url: ${{ github.event.inputs.release_url }}
|
||||
steps_to_skip: ${{ github.event.inputs.steps_to_skip }}
|
||||
|
||||
- name: Finalize Release
|
||||
id: finalize-release
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
release_url: ${{ steps.populate-release.outputs.release_url }}
|
||||
|
||||
- name: "** Next Step **"
|
||||
if: ${{ success() }}
|
||||
run: |
|
||||
echo "Verify the final release"
|
||||
echo ${{ steps.finalize-release.outputs.release_url }}
|
||||
|
||||
- name: "** Failure Message **"
|
||||
if: ${{ failure() }}
|
||||
run: |
|
||||
echo "Failed to Publish the Draft Release Url:"
|
||||
echo ${{ steps.populate-release.outputs.release_url }}
|
||||
@ -1,128 +0,0 @@
|
||||
name: Check for latest JupyterLab releases
|
||||
|
||||
on:
|
||||
# schedule:
|
||||
# - cron: 30 17 * * *
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'JupyterLab version'
|
||||
default: latest
|
||||
required: true
|
||||
type: string
|
||||
branch:
|
||||
description: 'The branch to target'
|
||||
default: main
|
||||
required: false
|
||||
type: string
|
||||
target_repo:
|
||||
description: 'Target repository'
|
||||
required: false
|
||||
default: jupyter/notebook
|
||||
type: string
|
||||
|
||||
env:
|
||||
version_tag: 'latest'
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
check_for_lab_updates:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.branch || 'main' }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: Install npm dependencies and build buildutils
|
||||
run: |
|
||||
python -m pip install -e ".[dev]"
|
||||
|
||||
jlpm
|
||||
jlpm run build:utils
|
||||
|
||||
- name: Check for new releases and update
|
||||
shell: bash
|
||||
run: |
|
||||
set -eux
|
||||
for version in ${{ inputs.version || env.version_tag }}
|
||||
do
|
||||
if [[ "${version}" == "latest" ]]; then
|
||||
export LATEST=$(jlpm run get:lab:version --set-version ${version})
|
||||
else
|
||||
export LATEST=${version}
|
||||
fi
|
||||
done
|
||||
|
||||
echo "latest=${LATEST}" >> $GITHUB_ENV
|
||||
jlpm upgrade:lab:dependencies --set-version ${LATEST}
|
||||
if [[ ! -z "$(git status --porcelain package.json)" ]]; then
|
||||
jlpm
|
||||
jlpm deduplicate
|
||||
|
||||
cd ui-tests
|
||||
jlpm
|
||||
jlpm deduplicate
|
||||
fi
|
||||
- uses: prefix-dev/setup-pixi@v0.9.3
|
||||
with:
|
||||
pixi-version: v0.41.4
|
||||
manifest-path: pyproject.toml
|
||||
locked: false
|
||||
|
||||
- name: Update pixi.lock
|
||||
run: pixi install
|
||||
|
||||
- name: Create a PR
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PERSONAL_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -eux
|
||||
|
||||
export LATEST=${{ env.latest }}
|
||||
export BRANCH_NAME=update-to-v${LATEST}
|
||||
|
||||
# if resulted in any change:
|
||||
if [[ ! -z "$(git status --porcelain package.json)" ]]; then
|
||||
# if branch already exists.
|
||||
if git ls-remote --heads origin | grep "refs/heads/${BRANCH_NAME}$" > /dev/null; then
|
||||
echo "Branch '${BRANCH_NAME}' exists."
|
||||
else
|
||||
# new branch is created
|
||||
git checkout -b "${BRANCH_NAME}"
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
git commit . -m "Update to JupyterLab v${LATEST}"
|
||||
|
||||
git push --set-upstream origin "${BRANCH_NAME}"
|
||||
|
||||
PR_ARGS=(
|
||||
--base "${{ inputs.branch || 'main' }}"
|
||||
--title "Update to JupyterLab v${LATEST}"
|
||||
--body "New JupyterLab release [v${LATEST}](https://github.com/jupyterlab/jupyterlab/releases/tag/v${LATEST}) is available. Please review the lock file carefully."
|
||||
)
|
||||
|
||||
# Add --repo flag only if target_repo is specified
|
||||
if [[ -n "${{ inputs.target_repo }}" ]]; then
|
||||
PR_ARGS+=(--repo "${{ inputs.target_repo }}")
|
||||
fi
|
||||
|
||||
gh pr create "${PR_ARGS[@]}"
|
||||
fi
|
||||
fi
|
||||
@ -1,86 +0,0 @@
|
||||
ci:
|
||||
autoupdate_schedule: monthly
|
||||
autoupdate_commit_msg: 'chore: update pre-commit hooks'
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-ast
|
||||
- id: check-docstring-first
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- id: debug-statements
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.30.0
|
||||
hooks:
|
||||
- id: check-github-workflows
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: 'v2.3.0'
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ['-L', 'hart,noteable', '--skip', "*.spec.ts"]
|
||||
exclude: |
|
||||
(?x)^(
|
||||
yarn.lock|
|
||||
pixi.lock|
|
||||
binder/example.ipynb|
|
||||
docs/source/examples/images/FrontendKernel.graffle/data.plist|
|
||||
)$
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: "v1.14.1"
|
||||
hooks:
|
||||
- id: mypy
|
||||
files: "^notebook"
|
||||
stages: [manual]
|
||||
args: ["--install-types", "--non-interactive"]
|
||||
additional_dependencies: ["traitlets>=5.13", "tornado", "jupyter_server>=2.10", "jupyterlab_server>=2.25", "jupyterlab>=4.6.0a0,<4.7"]
|
||||
|
||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||
rev: 'v1.10.0'
|
||||
hooks:
|
||||
- id: rst-backticks
|
||||
- id: rst-directive-colons
|
||||
- id: rst-inline-touching-normal
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.6
|
||||
hooks:
|
||||
- id: ruff
|
||||
types_or: [ python, jupyter ]
|
||||
exclude: '^docs/source/examples/Notebook/Importing Notebooks.ipynb'
|
||||
args: ['--fix', '--show-fixes']
|
||||
- id: ruff-format
|
||||
types_or: [ python, jupyter ]
|
||||
exclude: '^docs/source/examples/Notebook/Importing Notebooks.ipynb'
|
||||
|
||||
- repo: https://github.com/scientific-python/cookie
|
||||
rev: '2024.08.19'
|
||||
hooks:
|
||||
- id: sp-repo-review
|
||||
additional_dependencies: ['repo-review[cli]']
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: prettier
|
||||
name: prettier
|
||||
entry: 'npm run prettier:files'
|
||||
language: node
|
||||
types_or: [json, ts, tsx, javascript, jsx, css, markdown]
|
||||
stages: [manual]
|
||||
- id: integrity
|
||||
name: integrity
|
||||
entry: 'npm run integrity --force'
|
||||
language: node
|
||||
stages: [pre-push]
|
||||
@ -1,13 +0,0 @@
|
||||
version: 2
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: '3.9'
|
||||
nodejs: '16'
|
||||
python:
|
||||
install:
|
||||
# install notebook itself
|
||||
- method: pip
|
||||
path: '.[docs]'
|
||||
@ -1,5 +0,0 @@
|
||||
enableImmutableInstalls: false
|
||||
enableInlineBuilds: false
|
||||
enableTelemetry: false
|
||||
httpTimeout: 60000
|
||||
nodeLinker: node-modules
|
||||
@ -0,0 +1,26 @@
|
||||
include LICENSE
|
||||
include *.md
|
||||
include pyproject.toml
|
||||
include setup.py
|
||||
include jupyter-config/notebook.json
|
||||
|
||||
include package.json
|
||||
include install.json
|
||||
include ts*.json
|
||||
|
||||
graft notebook/labextension
|
||||
graft notebook/static
|
||||
graft notebook/templates
|
||||
|
||||
# Javascript files
|
||||
graft src
|
||||
graft style
|
||||
prune **/node_modules
|
||||
prune lib
|
||||
|
||||
# Patterns to exclude from any directory
|
||||
global-exclude *~
|
||||
global-exclude *.pyc
|
||||
global-exclude *.pyo
|
||||
global-exclude .git
|
||||
global-exclude .ipynb_checkpoints
|
||||
@ -1,40 +1,9 @@
|
||||
# Releasing Jupyter Notebook
|
||||
|
||||
## Using `jupyter_releaser`
|
||||
## Automated releases
|
||||
|
||||
The recommended way to make a release is to use [`jupyter_releaser`](https://jupyter-releaser.readthedocs.io/en/latest/get_started/making_release_from_repo.html).
|
||||
The recommended way to make a release is to use [`jupyter_releaser`](https://github.com/jupyter-server/jupyter_releaser#checklist-for-adoption).
|
||||
|
||||
## Manual Release
|
||||
We follow a similar bump strategy as in JupyterLab: https://github.com/jupyterlab/jupyterlab/blob/master/RELEASE.md#bump-version
|
||||
|
||||
To create a manual release, perform the following steps:
|
||||
|
||||
### Set up
|
||||
|
||||
```bash
|
||||
pip install hatch twine
|
||||
git pull origin $(git branch --show-current)
|
||||
git clean -dffx
|
||||
```
|
||||
|
||||
### Update the version and apply the tag
|
||||
|
||||
```bash
|
||||
echo "Enter new version"
|
||||
read new_version
|
||||
hatch version ${new_version}
|
||||
git tag -a ${new_version} -m "Release ${new_version}"
|
||||
```
|
||||
|
||||
### Build the artifacts
|
||||
|
||||
```bash
|
||||
rm -rf dist
|
||||
hatch build
|
||||
```
|
||||
|
||||
### Publish the artifacts to pypi
|
||||
|
||||
```bash
|
||||
twine check dist/*
|
||||
twine upload dist/*
|
||||
```
|
||||
If you would still like to do the release manually instead, read below.
|
||||
|
||||
@ -0,0 +1,338 @@
|
||||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/dev_mode/index.js
|
||||
|
||||
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
||||
|
||||
require('./style.js');
|
||||
require('./extraStyle.js');
|
||||
|
||||
function loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const newScript = document.createElement('script');
|
||||
newScript.onerror = reject;
|
||||
newScript.onload = resolve;
|
||||
newScript.async = true;
|
||||
document.head.appendChild(newScript);
|
||||
newScript.src = url;
|
||||
});
|
||||
}
|
||||
async function loadComponent(url, scope) {
|
||||
await loadScript(url);
|
||||
|
||||
// From MIT-licensed https://github.com/module-federation/module-federation-examples/blob/af043acd6be1718ee195b2511adf6011fba4233c/advanced-api/dynamic-remotes/app1/src/App.js#L6-L12
|
||||
// eslint-disable-next-line no-undef
|
||||
await __webpack_init_sharing__('default');
|
||||
const container = window._JUPYTERLAB[scope];
|
||||
// Initialize the container, it may provide shared modules and may need ours
|
||||
// eslint-disable-next-line no-undef
|
||||
await container.init(__webpack_share_scopes__.default);
|
||||
}
|
||||
|
||||
async function createModule(scope, module) {
|
||||
try {
|
||||
const factory = await window._JUPYTERLAB[scope].get(module);
|
||||
return factory();
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Failed to create module: package: ${scope}; module: ${module}`
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function
|
||||
*/
|
||||
async function main() {
|
||||
// load extra packages
|
||||
require('@jupyterlab/celltags');
|
||||
|
||||
const mimeExtensionsMods = [
|
||||
require('@jupyterlab/javascript-extension'),
|
||||
require('@jupyterlab/json-extension'),
|
||||
require('@jupyterlab/pdf-extension'),
|
||||
require('@jupyterlab/vega5-extension')
|
||||
];
|
||||
const mimeExtensions = await Promise.all(mimeExtensionsMods);
|
||||
|
||||
const disabled = [];
|
||||
// TODO: formalize the way the set of initial extensions and plugins are specified
|
||||
let baseMods = [
|
||||
// @jupyter-notebook plugins
|
||||
require('@jupyter-notebook/application-extension'),
|
||||
require('@jupyter-notebook/console-extension'),
|
||||
require('@jupyter-notebook/docmanager-extension'),
|
||||
require('@jupyter-notebook/documentsearch-extension'),
|
||||
require('@jupyter-notebook/help-extension'),
|
||||
require('@jupyter-notebook/notebook-extension'),
|
||||
// to handle opening new tabs after creating a new terminal
|
||||
require('@jupyter-notebook/terminal-extension'),
|
||||
|
||||
// @jupyterlab plugins
|
||||
require('@jupyterlab/application-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/application-extension:commands',
|
||||
'@jupyterlab/application-extension:context-menu',
|
||||
'@jupyterlab/application-extension:faviconbusy'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/apputils-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/apputils-extension:palette',
|
||||
'@jupyterlab/apputils-extension:settings',
|
||||
'@jupyterlab/apputils-extension:state',
|
||||
'@jupyterlab/apputils-extension:themes',
|
||||
'@jupyterlab/apputils-extension:themes-palette-menu',
|
||||
'@jupyterlab/apputils-extension:toolbar-registry'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/codemirror-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/codemirror-extension:services',
|
||||
'@jupyterlab/codemirror-extension:codemirror'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/completer-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/completer-extension:base-service',
|
||||
'@jupyterlab/completer-extension:tracker'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/console-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/console-extension:completer',
|
||||
'@jupyterlab/console-extension:factory',
|
||||
'@jupyterlab/console-extension:foreign',
|
||||
'@jupyterlab/console-extension:tracker'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/docmanager-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/docmanager-extension:plugin',
|
||||
'@jupyterlab/docmanager-extension:download'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/docprovider-extension'),
|
||||
require('@jupyterlab/documentsearch-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/documentsearch-extension:plugin'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/filebrowser-extension:factory'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/fileeditor-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/fileeditor-extension:plugin'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/mainmenu-extension'),
|
||||
require('@jupyterlab/markedparser-extension'),
|
||||
require('@jupyterlab/mathjax2-extension'),
|
||||
require('@jupyterlab/notebook-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/notebook-extension:code-console',
|
||||
'@jupyterlab/notebook-extension:export',
|
||||
'@jupyterlab/notebook-extension:factory',
|
||||
'@jupyterlab/notebook-extension:tracker',
|
||||
'@jupyterlab/notebook-extension:widget-factory'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/rendermime-extension'),
|
||||
require('@jupyterlab/shortcuts-extension'),
|
||||
// so new terminals can be create from the menu
|
||||
require('@jupyterlab/terminal-extension'),
|
||||
require('@jupyterlab/theme-light-extension'),
|
||||
require('@jupyterlab/theme-dark-extension'),
|
||||
require('@jupyterlab/translation-extension'),
|
||||
// Add the "Hub Control Panel" menu option when running in JupyterHub
|
||||
require('@jupyterlab/user-extension'),
|
||||
require('@jupyterlab/hub-extension')
|
||||
];
|
||||
|
||||
// The motivation here is to only load a specific set of plugins dependending on
|
||||
// the current page
|
||||
const page = PageConfig.getOption('notebookPage');
|
||||
switch (page) {
|
||||
case 'tree': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/filebrowser-extension:browser',
|
||||
'@jupyterlab/filebrowser-extension:download',
|
||||
'@jupyterlab/filebrowser-extension:file-upload-status',
|
||||
'@jupyterlab/filebrowser-extension:open-with',
|
||||
'@jupyterlab/filebrowser-extension:share-file'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyter-notebook/tree-extension'),
|
||||
require('@jupyterlab/running-extension')
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'notebooks': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/notebook-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/notebook-extension:completer'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/tooltip-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/tooltip-extension:manager',
|
||||
'@jupyterlab/tooltip-extension:notebooks'
|
||||
].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'consoles': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/tooltip-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/tooltip-extension:manager',
|
||||
'@jupyterlab/tooltip-extension:consoles'
|
||||
].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/fileeditor-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/fileeditor-extension:completer'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/filebrowser-extension:browser'].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over active plugins in an extension.
|
||||
*
|
||||
* #### Notes
|
||||
* This also populates the disabled
|
||||
*/
|
||||
function* activePlugins(extension) {
|
||||
// Handle commonjs or es2015 modules
|
||||
let exports;
|
||||
if (Object.prototype.hasOwnProperty.call(extension, '__esModule')) {
|
||||
exports = extension.default;
|
||||
} else {
|
||||
// CommonJS exports.
|
||||
exports = extension;
|
||||
}
|
||||
|
||||
let plugins = Array.isArray(exports) ? exports : [exports];
|
||||
for (let plugin of plugins) {
|
||||
if (PageConfig.Extension.isDisabled(plugin.id)) {
|
||||
disabled.push(plugin.id);
|
||||
continue;
|
||||
}
|
||||
yield plugin;
|
||||
}
|
||||
}
|
||||
|
||||
const extension_data = JSON.parse(
|
||||
PageConfig.getOption('federated_extensions')
|
||||
);
|
||||
|
||||
const mods = [];
|
||||
const federatedExtensionPromises = [];
|
||||
const federatedMimeExtensionPromises = [];
|
||||
const federatedStylePromises = [];
|
||||
|
||||
const extensions = await Promise.allSettled(
|
||||
extension_data.map(async data => {
|
||||
await loadComponent(
|
||||
`${URLExt.join(
|
||||
PageConfig.getOption('fullLabextensionsUrl'),
|
||||
data.name,
|
||||
data.load
|
||||
)}`,
|
||||
data.name
|
||||
);
|
||||
return data;
|
||||
})
|
||||
);
|
||||
|
||||
extensions.forEach(p => {
|
||||
if (p.status === 'rejected') {
|
||||
// There was an error loading the component
|
||||
console.error(p.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = p.value;
|
||||
if (data.extension) {
|
||||
federatedExtensionPromises.push(createModule(data.name, data.extension));
|
||||
}
|
||||
if (data.mimeExtension) {
|
||||
federatedMimeExtensionPromises.push(
|
||||
createModule(data.name, data.mimeExtension)
|
||||
);
|
||||
}
|
||||
if (data.style) {
|
||||
federatedStylePromises.push(createModule(data.name, data.style));
|
||||
}
|
||||
});
|
||||
|
||||
// Add the base frontend extensions
|
||||
const baseFrontendMods = await Promise.all(baseMods);
|
||||
baseFrontendMods.forEach(p => {
|
||||
for (let plugin of activePlugins(p)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the federated extensions.
|
||||
const federatedExtensions = await Promise.allSettled(
|
||||
federatedExtensionPromises
|
||||
);
|
||||
federatedExtensions.forEach(p => {
|
||||
if (p.status === 'fulfilled') {
|
||||
for (let plugin of activePlugins(p.value)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
} else {
|
||||
console.error(p.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the federated mime extensions.
|
||||
const federatedMimeExtensions = await Promise.allSettled(
|
||||
federatedMimeExtensionPromises
|
||||
);
|
||||
federatedMimeExtensions.forEach(p => {
|
||||
if (p.status === 'fulfilled') {
|
||||
for (let plugin of activePlugins(p.value)) {
|
||||
mimeExtensions.push(plugin);
|
||||
}
|
||||
} else {
|
||||
console.error(p.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Load all federated component styles and log errors for any that do not
|
||||
(await Promise.allSettled(federatedStylePromises))
|
||||
.filter(({ status }) => status === 'rejected')
|
||||
.forEach(({ reason }) => {
|
||||
console.error(reason);
|
||||
});
|
||||
|
||||
const NotebookApp = require('@jupyter-notebook/application').NotebookApp;
|
||||
const app = new NotebookApp({ mimeExtensions });
|
||||
|
||||
app.registerPluginModules(mods);
|
||||
|
||||
// Expose global app instance when in dev mode or when toggled explicitly.
|
||||
const exposeAppInBrowser =
|
||||
(PageConfig.getOption('exposeAppInBrowser') || '').toLowerCase() === 'true';
|
||||
|
||||
if (exposeAppInBrowser) {
|
||||
window.jupyterapp = app;
|
||||
}
|
||||
|
||||
await app.start();
|
||||
}
|
||||
|
||||
window.addEventListener('load', main);
|
||||
@ -1,244 +0,0 @@
|
||||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/dev_mode/index.js
|
||||
|
||||
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
||||
|
||||
import { PluginRegistry } from '@lumino/coreutils';
|
||||
|
||||
require('./style.js');
|
||||
require('./extraStyle.js');
|
||||
|
||||
function loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const newScript = document.createElement('script');
|
||||
newScript.onerror = reject;
|
||||
newScript.onload = resolve;
|
||||
newScript.async = true;
|
||||
document.head.appendChild(newScript);
|
||||
newScript.src = url;
|
||||
});
|
||||
}
|
||||
async function loadComponent(url, scope) {
|
||||
await loadScript(url);
|
||||
|
||||
// From MIT-licensed https://github.com/module-federation/module-federation-examples/blob/af043acd6be1718ee195b2511adf6011fba4233c/advanced-api/dynamic-remotes/app1/src/App.js#L6-L12
|
||||
// eslint-disable-next-line no-undef
|
||||
await __webpack_init_sharing__('default');
|
||||
const container = window._JUPYTERLAB[scope];
|
||||
// Initialize the container, it may provide shared modules and may need ours
|
||||
// eslint-disable-next-line no-undef
|
||||
await container.init(__webpack_share_scopes__.default);
|
||||
}
|
||||
|
||||
async function createModule(scope, module) {
|
||||
try {
|
||||
const factory = await window._JUPYTERLAB[scope].get(module);
|
||||
const instance = factory();
|
||||
instance.__scope__ = scope;
|
||||
return instance;
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Failed to create module: package: ${scope}; module: ${module}`
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function
|
||||
*/
|
||||
async function main() {
|
||||
const mimeExtensionsMods = [
|
||||
{{#each notebook_mime_extensions}}
|
||||
require('{{ @key }}'),
|
||||
{{/each}}
|
||||
];
|
||||
const mimeExtensions = await Promise.all(mimeExtensionsMods);
|
||||
|
||||
// Load the base plugins available on all pages
|
||||
let baseMods = [
|
||||
{{#each notebook_plugins}}
|
||||
{{#if (ispage @key '/')}}
|
||||
{{{ list_plugins }}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
];
|
||||
|
||||
const page = `/${PageConfig.getOption('notebookPage')}`;
|
||||
switch (page) {
|
||||
{{#each notebook_plugins}}
|
||||
{{#unless (ispage @key '/')}}
|
||||
// list all the other plugins grouped by page
|
||||
case '{{ @key }}': {
|
||||
baseMods = baseMods.concat([
|
||||
{{{ list_plugins }}}
|
||||
]);
|
||||
break;
|
||||
}
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
}
|
||||
|
||||
// populate the list of disabled extensions
|
||||
const disabled = [];
|
||||
const availablePlugins = [];
|
||||
|
||||
/**
|
||||
* Iterate over active plugins in an extension.
|
||||
*
|
||||
* #### Notes
|
||||
* This also populates the disabled
|
||||
*/
|
||||
function* activePlugins(extension) {
|
||||
// Handle commonjs or es2015 modules
|
||||
let exports;
|
||||
if (Object.prototype.hasOwnProperty.call(extension, '__esModule')) {
|
||||
exports = extension.default;
|
||||
} else {
|
||||
// CommonJS exports.
|
||||
exports = extension;
|
||||
}
|
||||
|
||||
let plugins = Array.isArray(exports) ? exports : [exports];
|
||||
for (let plugin of plugins) {
|
||||
const isDisabled = PageConfig.Extension.isDisabled(plugin.id);
|
||||
availablePlugins.push({
|
||||
id: plugin.id,
|
||||
description: plugin.description,
|
||||
requires: plugin.requires ?? [],
|
||||
optional: plugin.optional ?? [],
|
||||
provides: plugin.provides ?? null,
|
||||
autoStart: plugin.autoStart,
|
||||
enabled: !isDisabled,
|
||||
extension: extension.__scope__
|
||||
});
|
||||
if (isDisabled) {
|
||||
disabled.push(plugin.id);
|
||||
continue;
|
||||
}
|
||||
yield plugin;
|
||||
}
|
||||
}
|
||||
|
||||
const extension_data = JSON.parse(
|
||||
PageConfig.getOption('federated_extensions')
|
||||
);
|
||||
|
||||
const mods = [];
|
||||
const federatedExtensionPromises = [];
|
||||
const federatedMimeExtensionPromises = [];
|
||||
const federatedStylePromises = [];
|
||||
|
||||
const extensions = await Promise.allSettled(
|
||||
extension_data.map(async data => {
|
||||
await loadComponent(
|
||||
`${URLExt.join(
|
||||
PageConfig.getOption('fullLabextensionsUrl'),
|
||||
data.name,
|
||||
data.load
|
||||
)}`,
|
||||
data.name
|
||||
);
|
||||
return data;
|
||||
})
|
||||
);
|
||||
|
||||
extensions.forEach(p => {
|
||||
if (p.status === 'rejected') {
|
||||
// There was an error loading the component
|
||||
console.error(p.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = p.value;
|
||||
if (data.extension) {
|
||||
federatedExtensionPromises.push(createModule(data.name, data.extension));
|
||||
}
|
||||
if (data.mimeExtension) {
|
||||
federatedMimeExtensionPromises.push(
|
||||
createModule(data.name, data.mimeExtension)
|
||||
);
|
||||
}
|
||||
if (data.style && !PageConfig.Extension.isDisabled(data.name)) {
|
||||
federatedStylePromises.push(createModule(data.name, data.style));
|
||||
}
|
||||
});
|
||||
|
||||
// Add the base frontend extensions
|
||||
const baseFrontendMods = await Promise.all(baseMods);
|
||||
baseFrontendMods.forEach(p => {
|
||||
for (let plugin of activePlugins(p)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the federated extensions.
|
||||
const federatedExtensions = await Promise.allSettled(
|
||||
federatedExtensionPromises
|
||||
);
|
||||
federatedExtensions.forEach(p => {
|
||||
if (p.status === 'fulfilled') {
|
||||
for (let plugin of activePlugins(p.value)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
} else {
|
||||
console.error(p.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the federated mime extensions.
|
||||
const federatedMimeExtensions = await Promise.allSettled(
|
||||
federatedMimeExtensionPromises
|
||||
);
|
||||
federatedMimeExtensions.forEach(p => {
|
||||
if (p.status === 'fulfilled') {
|
||||
for (let plugin of activePlugins(p.value)) {
|
||||
mimeExtensions.push(plugin);
|
||||
}
|
||||
} else {
|
||||
console.error(p.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Load all federated component styles and log errors for any that do not
|
||||
(await Promise.allSettled(federatedStylePromises))
|
||||
.filter(({ status }) => status === 'rejected')
|
||||
.forEach(({ reason }) => {
|
||||
console.error(reason);
|
||||
});
|
||||
|
||||
// Set the list of base notebook multi-page plugins so the app is aware of all
|
||||
// its built-in plugins even if they are not loaded on the current page.
|
||||
// For example this is useful so the Settings Editor can list the debugger
|
||||
// plugin even if the debugger is only loaded on the notebook page.
|
||||
PageConfig.setOption('allPlugins', '{{{ json notebook_plugins }}}');
|
||||
|
||||
|
||||
const pluginRegistry = new PluginRegistry();
|
||||
const NotebookApp = require('@jupyter-notebook/application').NotebookApp;
|
||||
|
||||
pluginRegistry.registerPlugins(mods);
|
||||
const IServiceManager = require('@jupyterlab/services').IServiceManager;
|
||||
const serviceManager = await pluginRegistry.resolveRequiredService(IServiceManager);
|
||||
|
||||
const app = new NotebookApp({
|
||||
pluginRegistry,
|
||||
serviceManager,
|
||||
mimeExtensions,
|
||||
availablePlugins
|
||||
});
|
||||
|
||||
// Expose global app instance when in dev mode or when toggled explicitly.
|
||||
const exposeAppInBrowser =
|
||||
(PageConfig.getOption('exposeAppInBrowser') || '').toLowerCase() === 'true';
|
||||
|
||||
if (exposeAppInBrowser) {
|
||||
window.jupyterapp = app;
|
||||
}
|
||||
|
||||
await app.start();
|
||||
}
|
||||
|
||||
window.addEventListener('load', main);
|
||||
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Jupyter Development Team.
|
||||
* Distributed under the terms of the Modified BSD License.
|
||||
*/
|
||||
|
||||
const base = require('./rspack.config');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
...base[0],
|
||||
bail: false,
|
||||
watch: true,
|
||||
},
|
||||
...base.slice(1),
|
||||
];
|
||||
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Jupyter Development Team.
|
||||
* Distributed under the terms of the Modified BSD License.
|
||||
*/
|
||||
|
||||
const merge = require('webpack-merge').default;
|
||||
const config = require('./rspack.config');
|
||||
const WPPlugin = require('@jupyterlab/builder').WPPlugin;
|
||||
|
||||
config[0] = merge(config[0], {
|
||||
mode: 'production',
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
// Add version argument when in production so the Jupyter server
|
||||
// allows caching of files (i.e., does not set the CacheControl header to no-cache to prevent caching static files)
|
||||
filename: '[name].[contenthash].js?v=[contenthash]',
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
},
|
||||
plugins: [
|
||||
new WPPlugin.JSONLicenseWebpackPlugin({
|
||||
excludedPackageTest: (packageName) =>
|
||||
packageName === '@jupyter-notebook/app',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
@ -0,0 +1 @@
|
||||
import '@jupyterlab/celltags/style/index.js';
|
||||
@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{page_config['appName'] | e}} - Console</title>
|
||||
{% block favicon %}
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="{{ page_config['fullStaticUrl'] | e }}/favicons/favicon-console.ico"
|
||||
class="favicon"
|
||||
/>
|
||||
{% endblock %} {% if custom_css %}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{{ base_url | escape }}custom/custom.css"
|
||||
/>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body class="jp-ThemedContainer">
|
||||
{# Copy so we do not modify the page_config with updates. #} {% set
|
||||
page_config_full = page_config.copy() %} {# Set a dummy variable - we just
|
||||
want the side effect of the update. #} {% set _ =
|
||||
page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} {# Sentinel value
|
||||
to say that we are on the tree page #} {% set _ =
|
||||
page_config_full.update(notebookPage='consoles') %}
|
||||
|
||||
<script id="jupyter-config-data" type="application/json">
|
||||
{{ page_config_full | tojson }}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* Remove token from URL. */
|
||||
(function () {
|
||||
var parsedUrl = new URL(window.location.href);
|
||||
if (parsedUrl.searchParams.get('token')) {
|
||||
parsedUrl.searchParams.delete('token');
|
||||
window.history.replaceState({}, '', parsedUrl.href);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,39 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{page_config['appName'] | e}} - Edit</title>
|
||||
{% block favicon %}
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="{{ base_url | escape }}static/favicons/favicon-file.ico"
|
||||
class="favicon"
|
||||
/>
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body class="jp-ThemedContainer" data-notebook="edit">
|
||||
{# Copy so we do not modify the page_config with updates. #} {% set
|
||||
page_config_full = page_config.copy() %} {# Set a dummy variable - we just
|
||||
want the side effect of the update. #} {% set _ =
|
||||
page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} {# Sentinel value
|
||||
to say that we are on the tree page #} {% set _ =
|
||||
page_config_full.update(notebookPage='edit') %}
|
||||
|
||||
<script id="jupyter-config-data" type="application/json">
|
||||
{{ page_config_full | tojson }}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* Remove token from URL. */
|
||||
(function () {
|
||||
var parsedUrl = new URL(window.location.href);
|
||||
if (parsedUrl.searchParams.get('token')) {
|
||||
parsedUrl.searchParams.delete('token');
|
||||
window.history.replaceState({}, '', parsedUrl.href);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{page_config['appName'] | e}} - Notebook</title>
|
||||
{% block favicon %}
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="{{ base_url | escape }}static/favicons/favicon-notebook.ico"
|
||||
class="favicon"
|
||||
/>
|
||||
{% endblock %} {% if custom_css %}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{{ base_url | escape }}custom/custom.css"
|
||||
/>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body class="jp-ThemedContainer" data-notebook="notebooks">
|
||||
{# Copy so we do not modify the page_config with updates. #} {% set
|
||||
page_config_full = page_config.copy() %} {# Set a dummy variable - we just
|
||||
want the side effect of the update. #} {% set _ =
|
||||
page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} {# Sentinel value
|
||||
to say that we are on the tree page #} {% set _ =
|
||||
page_config_full.update(notebookPage='notebooks') %}
|
||||
|
||||
<script id="jupyter-config-data" type="application/json">
|
||||
{{ page_config_full | tojson }}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* Remove token from URL. */
|
||||
(function () {
|
||||
var parsedUrl = new URL(window.location.href);
|
||||
if (parsedUrl.searchParams.get('token')) {
|
||||
parsedUrl.searchParams.delete('token');
|
||||
window.history.replaceState({}, '', parsedUrl.href);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{page_config['appName'] | e}} - Terminal</title>
|
||||
{% block favicon %}
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="{{ base_url | escape }}static/favicons/favicon-terminal.ico"
|
||||
class="favicon"
|
||||
/>
|
||||
{% endblock %} {% if custom_css %}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{{ base_url | escape }}custom/custom.css"
|
||||
/>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body class="jp-ThemedContainer">
|
||||
{# Copy so we do not modify the page_config with updates. #} {% set
|
||||
page_config_full = page_config.copy() %} {# Set a dummy variable - we just
|
||||
want the side effect of the update. #} {% set _ =
|
||||
page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} {# Sentinel value
|
||||
to say that we are on the tree page #} {% set _ =
|
||||
page_config_full.update(notebookPage='terminals') %}
|
||||
|
||||
<script id="jupyter-config-data" type="application/json">
|
||||
{{ page_config_full | tojson }}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* Remove token from URL. */
|
||||
(function () {
|
||||
var parsedUrl = new URL(window.location.href);
|
||||
if (parsedUrl.searchParams.get('token')) {
|
||||
parsedUrl.searchParams.delete('token');
|
||||
window.history.replaceState({}, '', parsedUrl.href);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Home</title>
|
||||
{% block favicon %}
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="{{ base_url | escape }}static/favicons/favicon.ico"
|
||||
class="favicon"
|
||||
/>
|
||||
{% endblock %} {% if custom_css %}
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{{ base_url | escape }}custom/custom.css"
|
||||
/>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body class="jp-ThemedContainer">
|
||||
{# Copy so we do not modify the page_config with updates. #} {% set
|
||||
page_config_full = page_config.copy() %} {# Set a dummy variable - we just
|
||||
want the side effect of the update. #} {% set _ =
|
||||
page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} {# Sentinel value
|
||||
to say that we are on the tree page #} {% set _ =
|
||||
page_config_full.update(notebookPage='tree') %}
|
||||
|
||||
<script id="jupyter-config-data" type="application/json">
|
||||
{{ page_config_full | tojson }}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
/* Remove token from URL. */
|
||||
(function () {
|
||||
var parsedUrl = new URL(window.location.href);
|
||||
if (parsedUrl.searchParams.get('token')) {
|
||||
parsedUrl.searchParams.delete('token');
|
||||
window.history.replaceState({}, '', parsedUrl.href);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,17 @@
|
||||
const base = require('./webpack.config');
|
||||
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
...base[0],
|
||||
bail: false,
|
||||
watch: true,
|
||||
plugins: [
|
||||
...base[0].plugins,
|
||||
new ExtraWatchWebpackPlugin({
|
||||
files: ['../packages/_metapackage/tsconfig.tsbuildinfo']
|
||||
})
|
||||
]
|
||||
},
|
||||
...base.slice(1)
|
||||
];
|
||||
@ -1,12 +1,13 @@
|
||||
name: notebook
|
||||
channels:
|
||||
- conda-forge
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- ipywidgets=8
|
||||
- jupyterlab=4
|
||||
- jupyterlab-language-pack-fr-FR
|
||||
- matplotlib
|
||||
- numpy
|
||||
- nodejs=20
|
||||
- python >=3.10,<3.11
|
||||
- xeus-python
|
||||
- ipywidgets=7.6
|
||||
- jupyterlab=3
|
||||
- jupyterlab-language-pack-fr-FR
|
||||
- jupyterlab-link-share>=0.2
|
||||
- matplotlib
|
||||
- numpy
|
||||
- nodejs
|
||||
- python >=3.9,<3.10
|
||||
- xeus-python
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
python -m pip install -e ".[dev,test]"
|
||||
jlpm develop
|
||||
python -m pip install -e .
|
||||
jlpm && jlpm run build
|
||||
jlpm run develop
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as semver from 'semver';
|
||||
|
||||
function convertPythonVersion(version: string): string {
|
||||
return version
|
||||
.replace('a', '-alpha')
|
||||
.replace('b', '-beta')
|
||||
.replace('rc', '-rc');
|
||||
}
|
||||
|
||||
function extractVersionFromReleases(
|
||||
releases: any,
|
||||
versionTag: string,
|
||||
currentVersion: string
|
||||
): string | null {
|
||||
const npmCurrentVersion = convertPythonVersion(currentVersion);
|
||||
const isCurrentPreRelease = semver.prerelease(npmCurrentVersion) !== null;
|
||||
|
||||
if (versionTag === 'latest') {
|
||||
// Find first version that is newer than current and matches pre-release criteria
|
||||
const release = releases.find((r: any) => {
|
||||
const version = r['tag_name'].substring(1); // Remove 'v' prefix for semver
|
||||
const npmVersion = convertPythonVersion(version);
|
||||
return (
|
||||
(isCurrentPreRelease || !r['prerelease']) &&
|
||||
semver.gte(npmVersion, npmCurrentVersion)
|
||||
);
|
||||
});
|
||||
return release ? release['tag_name'] : null;
|
||||
} else {
|
||||
// Find exact version match
|
||||
const release = releases.find((r: any) => r['tag_name'] === versionTag);
|
||||
return release ? release['tag_name'] : null;
|
||||
}
|
||||
}
|
||||
|
||||
function extractCurrentJupyterLabVersion(): string {
|
||||
const toml = fs.readFileSync('pyproject.toml', 'utf8');
|
||||
const match = toml.match(/jupyterlab>=([^,]+)/);
|
||||
if (!match) {
|
||||
throw new Error('Could not find JupyterLab version in pyproject.toml');
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
async function findVersion(versionTag: string): Promise<string> {
|
||||
const url = 'https://api.github.com/repos/jupyterlab/jupyterlab/releases';
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
const error_message = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`;
|
||||
throw new Error(error_message);
|
||||
}
|
||||
|
||||
const currentVersion = extractCurrentJupyterLabVersion();
|
||||
|
||||
const releases: any = await response.json();
|
||||
const version: string | null = extractVersionFromReleases(
|
||||
releases,
|
||||
versionTag,
|
||||
currentVersion
|
||||
);
|
||||
if (version === null) {
|
||||
const error_message = 'Invalid release tag';
|
||||
throw new Error(error_message);
|
||||
}
|
||||
return version.substring(1);
|
||||
}
|
||||
|
||||
async function getLatestLabVersion(): Promise<void> {
|
||||
const args: string[] = process.argv.slice(2);
|
||||
if (args.length !== 2 || args[0] !== '--set-version') {
|
||||
console.error('Usage: node script.js --set-version <version>');
|
||||
process.exit(1);
|
||||
}
|
||||
const version_tag: string = args[1];
|
||||
|
||||
try {
|
||||
const result: string = await findVersion(version_tag);
|
||||
console.log(result);
|
||||
} catch (error: any) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
getLatestLabVersion();
|
||||
@ -1,170 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const PACKAGE_JSON_PATHS: string[] = [
|
||||
'app/package.json',
|
||||
'buildutils/package.json',
|
||||
'package.json',
|
||||
'packages/application-extension/package.json',
|
||||
'packages/application/package.json',
|
||||
'packages/console-extension/package.json',
|
||||
'packages/docmanager-extension/package.json',
|
||||
'packages/documentsearch-extension/package.json',
|
||||
'packages/help-extension/package.json',
|
||||
'packages/lab-extension/package.json',
|
||||
'packages/notebook-extension/package.json',
|
||||
'packages/terminal-extension/package.json',
|
||||
'packages/tree-extension/package.json',
|
||||
'packages/tree/package.json',
|
||||
'packages/ui-components/package.json',
|
||||
'ui-tests/package.json',
|
||||
];
|
||||
|
||||
const DEPENDENCY_GROUP = '@jupyterlab';
|
||||
|
||||
interface IVersion {
|
||||
major: number;
|
||||
minor: number;
|
||||
patch: number;
|
||||
preRelease?: string;
|
||||
}
|
||||
|
||||
function parseVersion(version: string): IVersion {
|
||||
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:(a|b|rc)(\d+))?$/);
|
||||
if (!match) {
|
||||
throw new Error(`Invalid version format: ${version}`);
|
||||
}
|
||||
|
||||
const [, major, minor, patch, type, preVersion] = match;
|
||||
const baseVersion = {
|
||||
major: parseInt(major, 10),
|
||||
minor: parseInt(minor, 10),
|
||||
patch: parseInt(patch, 10),
|
||||
};
|
||||
|
||||
if (type && preVersion) {
|
||||
return {
|
||||
...baseVersion,
|
||||
preRelease: `${type}${preVersion}`,
|
||||
};
|
||||
}
|
||||
|
||||
return baseVersion;
|
||||
}
|
||||
|
||||
function getVersionRange(version: IVersion): string {
|
||||
const baseVersion = `${version.major}.${version.minor}.${version.patch}${
|
||||
version.preRelease ?? ''
|
||||
}`;
|
||||
return `>=${baseVersion},<${version.major}.${version.minor + 1}`;
|
||||
}
|
||||
|
||||
function updateVersionInFile(
|
||||
filePath: string,
|
||||
pattern: RegExp,
|
||||
version: IVersion
|
||||
): void {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const versionRange = getVersionRange(version);
|
||||
const updatedContent = content.replace(pattern, `$1${versionRange}`);
|
||||
fs.writeFileSync(filePath, updatedContent);
|
||||
}
|
||||
|
||||
async function updatePackageJson(newVersion: string): Promise<void> {
|
||||
const url = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/jupyterlab/staging/package.json`;
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorMessage = `Failed to fetch package.json from ${url}. HTTP status code: ${response.status}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
// fetch the new galata version
|
||||
const galataUrl = `https://raw.githubusercontent.com/jupyterlab/jupyterlab/v${newVersion}/galata/package.json`;
|
||||
const galataResponse = await fetch(galataUrl);
|
||||
if (!galataResponse.ok) {
|
||||
const errorMessage = `Failed to fetch galata/package.json from ${galataUrl}. HTTP status code: ${galataResponse.status}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const newPackageJson = await response.json();
|
||||
const galataPackageJson = await galataResponse.json();
|
||||
|
||||
for (const packageJsonPath of PACKAGE_JSON_PATHS) {
|
||||
const filePath: string = path.resolve(packageJsonPath);
|
||||
const existingPackageJson = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||
|
||||
const newDependencies = {
|
||||
...newPackageJson.devDependencies,
|
||||
...newPackageJson.resolutions,
|
||||
[galataPackageJson.name]: galataPackageJson.version,
|
||||
};
|
||||
|
||||
updateDependencyVersion(existingPackageJson, newDependencies);
|
||||
|
||||
fs.writeFileSync(
|
||||
filePath,
|
||||
JSON.stringify(existingPackageJson, null, 2) + '\n'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDependencyVersion(existingJson: any, newJson: any): void {
|
||||
if (!existingJson) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sectionPaths: string[] = [
|
||||
'resolutions',
|
||||
'dependencies',
|
||||
'devDependencies',
|
||||
];
|
||||
|
||||
for (const section of sectionPaths) {
|
||||
if (!existingJson[section]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const updated = existingJson[section];
|
||||
|
||||
for (const [pkg, version] of Object.entries<string>(
|
||||
existingJson[section]
|
||||
)) {
|
||||
if (pkg.startsWith(DEPENDENCY_GROUP) && pkg in newJson) {
|
||||
if (version[0] === '^' || version[0] === '~') {
|
||||
updated[pkg] = version[0] + absoluteVersion(newJson[pkg]);
|
||||
} else {
|
||||
updated[pkg] = absoluteVersion(newJson[pkg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function absoluteVersion(version: string): string {
|
||||
if (version.length > 0 && (version[0] === '^' || version[0] === '~')) {
|
||||
return version.substring(1);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
const versionPattern = /(jupyterlab)(>=[\d.]+(?:[a|b|rc]\d+)?,<[\d.]+)/g;
|
||||
|
||||
const FILES_TO_UPDATE = ['pyproject.toml', '.pre-commit-config.yaml'];
|
||||
|
||||
async function upgradeLabDependencies(): Promise<void> {
|
||||
const args: string[] = process.argv.slice(2);
|
||||
|
||||
if (args.length < 2) {
|
||||
throw new Error('Please provide the set-version flag and version');
|
||||
}
|
||||
|
||||
const version = parseVersion(args[1]);
|
||||
await updatePackageJson(args[1]); // Keep original string version for package.json
|
||||
|
||||
for (const file of FILES_TO_UPDATE) {
|
||||
updateVersionInFile(path.resolve(file), versionPattern, version);
|
||||
}
|
||||
}
|
||||
|
||||
upgradeLabDependencies();
|
||||
@ -0,0 +1,9 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 10
|
||||
patch:
|
||||
default:
|
||||
target: 0%
|
||||
@ -0,0 +1,5 @@
|
||||
sphinx>=1.3.6
|
||||
pydata-sphinx-theme
|
||||
nbsphinx
|
||||
sphinxcontrib_github_alt
|
||||
myst_parser
|
||||
@ -0,0 +1,20 @@
|
||||
name: notebook_docs
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python=3.8
|
||||
- pydata-sphinx-theme
|
||||
- jinja2
|
||||
- tornado
|
||||
- nbformat
|
||||
- jupyter_client
|
||||
- ipykernel
|
||||
- pip
|
||||
- sphinx
|
||||
- terminado
|
||||
- myst-parser
|
||||
- pip:
|
||||
- nbsphinx
|
||||
- Send2Trash
|
||||
- prometheus_client
|
||||
- sphinxcontrib_github_alt
|
||||
@ -1,14 +1,14 @@
|
||||
exports.handlers = {
|
||||
newDoclet: function (e) {
|
||||
newDoclet: function(e) {
|
||||
// e.doclet will refer to the newly created doclet
|
||||
// you can read and modify properties of that doclet if you wish
|
||||
if (typeof e.doclet.name === 'string') {
|
||||
if (e.doclet.name[0] === '_') {
|
||||
if (e.doclet.name[0] == '_') {
|
||||
console.log(
|
||||
'Private method "' + e.doclet.longname + '" not documented.'
|
||||
);
|
||||
e.doclet.memberof = '<anonymous>';
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 518 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
@ -1,10 +0,0 @@
|
||||
# Configuration
|
||||
|
||||
```{toctree}
|
||||
:caption: Configuration
|
||||
:maxdepth: 1
|
||||
|
||||
configuring/config_overview
|
||||
Security <https://jupyter-server.readthedocs.io/en/stable/operators/security.html>
|
||||
extending/index.rst
|
||||
```
|
||||
@ -0,0 +1,11 @@
|
||||
=============
|
||||
Configuration
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Configuration
|
||||
|
||||
config_overview
|
||||
Security <https://jupyter-server.readthedocs.io/en/stable/operators/security.html>
|
||||
extending/index.rst
|
||||
@ -1,62 +0,0 @@
|
||||
# Interface Customization
|
||||
|
||||
Multiple elements in the Notebook interface can be customized via the Settings Editor.
|
||||
|
||||
## Layout
|
||||
|
||||
By default some widgets are displayed in pre-defined parts of the user interface, which are often called "areas" or "regions".
|
||||
For example the table of contents will be displayed in the `left` area by default, while the debugger will be displayed in the `right` area.
|
||||
|
||||
However the positioning of some of these components can also be customized via the Settings Editor. Below are a few examples of how to do this.
|
||||
|
||||
### Open the Markdown Preview on the left
|
||||
|
||||
It is often useful to be able to see a rendered preview of a Markdown document while editing it.
|
||||
|
||||
By default the Markdown Preview opens on the right side of the application. However it is also possible to open it on the left side by changing the Notebook Shell settings in the Advanced Settings Editor:
|
||||
|
||||
```json
|
||||
{
|
||||
"layout": {
|
||||
"Markdown Preview": {
|
||||
"area": "left"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
### Configuring a third-party widget
|
||||
|
||||
Third-party extensions can also add widgets to the application shell. This is for example the case with the [Voila extension](https://github.com/voila-dashboards/voila), which adds a preview widget to visualize a notebook as a dashboard.
|
||||
|
||||
By default in JupyterLab the Voila Preview is added to the `main` area next to the corresponding notebook. With Notebook 7 it is possible to move the Voila Preview to the `right` area by changing the Notebook Shell setting in the Advanced Settings Editor as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"layout": {
|
||||
"Voila Preview": {
|
||||
"area": "right"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
```{note}
|
||||
Refer to the [JupyterLab Layout Documentation](https://jupyterlab.readthedocs.io/en/latest/user/interface_customization.html#layout)
|
||||
to learn more about the default positioning of other UI elements.
|
||||
```
|
||||
|
||||
## Toolbars, Menu bar and Context Menu
|
||||
|
||||
It is also possible to customize toolbars, menus and context menu entries via the Settings Editor.
|
||||
|
||||
For example the items of the notebook toolbar can be reordered, or some menu entries can be hidden.
|
||||
|
||||
```{note}
|
||||
Refer to the [JupyterLab Documentation](https://jupyterlab.readthedocs.io/en/latest/user/interface_customization.html)
|
||||
to learn more about general interface customization via the settings editor.
|
||||
```
|
||||
@ -1,21 +0,0 @@
|
||||
# Managing plugins
|
||||
|
||||
Notebook 7 uses the same extension system as JupyterLab. An extension can provide multiple plugins.
|
||||
|
||||
```{note}
|
||||
See the [JupyterLab documentation](https://jupyterlab.readthedocs.io/en/latest/user/extensions.html) to learn more about the extension system.
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Disabling the download button
|
||||
|
||||
By default Notebook 7 provides a way to download files from the file browser. This functionality consists of a context menu entry and a main menu entry. They are provided by an application plugin that can be disabled.
|
||||
|
||||
To disable the download entry of file browser context menus, open a terminal and run the following command:
|
||||
|
||||
```text
|
||||
jupyter labextension disable @jupyterlab/filebrowser-extension:download
|
||||
```
|
||||
|
||||
Then restart the application and refresh the page.
|
||||
@ -1,9 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
```{toctree}
|
||||
:caption: Contributor Documentation
|
||||
:maxdepth: 1
|
||||
|
||||
contributing
|
||||
development_faq
|
||||
```
|
||||
@ -0,0 +1,10 @@
|
||||
===========
|
||||
Contributor
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contributor Documentation
|
||||
|
||||
contributing
|
||||
development_faq
|
||||
@ -1,79 +0,0 @@
|
||||
# Applying Custom CSS
|
||||
|
||||
To apply custom CSS, you can add a `/custom/custom.css` file in the jupyter `config` directory. You can find the path, `~/.jupyter`, to this directory by running `jupyter --paths`. There you can create a folder named `custom` and create a `custom.css` file within the folder.
|
||||
|
||||
## Jupyter Styling
|
||||
|
||||
You can use a custom CSS file to modify default Jupyter styling.
|
||||
|
||||
```css
|
||||
/* Modify Jupyter Styles */
|
||||
#top-panel-wrapper,
|
||||
#jp-top-bar {
|
||||
background-color: #aecad4 !important;
|
||||
}
|
||||
|
||||
#menu-panel-wrapper,
|
||||
#jp-MainMenu,
|
||||
#menu-panel {
|
||||
background-color: #aecad4 !important;
|
||||
}
|
||||
|
||||
.jp-NotebookPanel-toolbar {
|
||||
background-color: #aecad4 !important;
|
||||
}
|
||||
.lm-MenuBar-content {
|
||||
color: #02484d;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Markdown
|
||||
|
||||
Another potential application for custom CSS is styling markdown.
|
||||
|
||||
```css
|
||||
/* Headings */
|
||||
h1,
|
||||
h2 {
|
||||
font-family: Impact, Charcoal, sans-serif;
|
||||
font-weight: bold;
|
||||
text-shadow: 2px 2px 4px #000000;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 52px;
|
||||
margin-bottom: 40px;
|
||||
color: #10929e;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 448px;
|
||||
margin-bottom: 32px;
|
||||
color: #76b4be;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Block Quotes */
|
||||
blockquote {
|
||||
font-family: Georgia, serif;
|
||||
font-size: 16px;
|
||||
color: #19085c;
|
||||
border-left: 8px solid #effffc;
|
||||
background-color: #eafcff;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
ul,
|
||||
ol {
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
font-size: 18px;
|
||||
color: #333333;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
@ -1,17 +0,0 @@
|
||||
(development-faq)=
|
||||
|
||||
# Developer FAQ
|
||||
|
||||
1. How do I install a prerelease version such as a beta or release candidate?
|
||||
|
||||
You can install a prerelease version of the notebook using the `--pre` flag with `pip`:
|
||||
|
||||
```bash
|
||||
python -m pip install notebook --pre --upgrade
|
||||
```
|
||||
|
||||
If you are using `conda` or `mamba`, you can install a prerelease version of the notebook using the alpha or beta label. For example, to install the latest alpha release, you can run:
|
||||
|
||||
```bash
|
||||
conda install -c conda-forge -c conda-forge/label/notebook_alpha notebook=7.0.0a18
|
||||
```
|
||||
@ -0,0 +1,10 @@
|
||||
.. _development_faq:
|
||||
|
||||
Developer FAQ
|
||||
=============
|
||||
|
||||
1. How do I install a prerelease version such as a beta or release candidate?
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python -m pip install notebook --pre --upgrade
|
||||
@ -1,17 +0,0 @@
|
||||
(frontend-extensions)=
|
||||
|
||||
# Custom front-end extensions
|
||||
|
||||
This describes the basic steps to write a TypeScript extension for the Jupyter
|
||||
notebook front-end. This allows you to customize the behaviour of the various
|
||||
pages like the dashboard, the notebook, or the text editor.
|
||||
|
||||
Starting with Notebook 7, front-end extensions for the notebook can be developed
|
||||
as prebuilt JupyterLab extensions.
|
||||
|
||||
This means Notebook 7 is able to reuse many of the existing extensions from the JupyterLab ecosystem as is.
|
||||
|
||||
If you would like to develop a prebuilt extension for Notebook 7, check out:
|
||||
|
||||
- [JupyterLab Extension Tutorial](https://jupyterlab.readthedocs.io/en/latest/extension/extension_tutorial.html): A tutorial to learn how to make a simple JupyterLab extension.
|
||||
- The [JupyterLab Extension Examples Repository](https://github.com/jupyterlab/extension-examples): A repository containing many examples of JupyterLab extensions for performing various tasks: adding commands, adding a new widget, handling user settings, etc.
|
||||
@ -0,0 +1,19 @@
|
||||
.. _frontend_extensions:
|
||||
|
||||
===========================
|
||||
Custom front-end extensions
|
||||
===========================
|
||||
|
||||
This describes the basic steps to write a TypeScript extension for the Jupyter
|
||||
notebook front-end. This allows you to customize the behaviour of the various
|
||||
pages like the dashboard, the notebook, or the text editor.
|
||||
|
||||
Starting with Notebook v7, front-end extensions for the notebook can be developed
|
||||
as prebuilt JupyterLab extensions.
|
||||
|
||||
This means Notebook v7 is able to reuse many of the existing extensions from the JupyterLab ecosystem as is.
|
||||
|
||||
If you would like to develop a prebuilt extension for Notebook v7, check out:
|
||||
|
||||
- `JupyterLab Extension Tutorial <https://jupyterlab.readthedocs.io/en/latest/extension/extension_tutorial.html>`_: A tutorial to learn how to make a simple JupyterLab extension.
|
||||
- The `JupyterLab Extension Examples Repository <https://github.com/jupyterlab/extension-examples>`_: A short tutorial series to learn how to develop extensions for JupyterLab by example.
|
||||
@ -1,27 +0,0 @@
|
||||
# Extending the Notebook
|
||||
|
||||
```{warning}
|
||||
Please note that the extension system for Notebook 7 is radically different
|
||||
from the one used in Notebook 6.5.x and earlier. If you are looking for
|
||||
information on how to extend the classic Notebook, please refer to the
|
||||
[documentation for NbClassic](https://nbclassic.readthedocs.io/en/latest/extending/index.html).
|
||||
```
|
||||
|
||||
```{note}
|
||||
With Notebook 7 being developed on top of JupyterLab and Jupyter Server, the
|
||||
frontend extension system is now based on the same extension system used by JupyterLab.
|
||||
|
||||
Server extensions are also now based on the same system used by Jupyter Server.
|
||||
You will find below a link to the relevant documentations.
|
||||
```
|
||||
|
||||
Certain subsystems of the notebook server are designed to be extended or
|
||||
overridden by users. These documents explain these systems, and show how to
|
||||
override the notebook's defaults with your own custom behavior.
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
|
||||
Extending the Jupyter Server <https://jupyter-server.readthedocs.io/en/stable/developers/index.html>
|
||||
frontend_extensions
|
||||
```
|
||||
@ -0,0 +1,13 @@
|
||||
======================
|
||||
Extending the Notebook
|
||||
======================
|
||||
|
||||
Certain subsystems of the notebook server are designed to be extended or
|
||||
overridden by users. These documents explain these systems, and show how to
|
||||
override the notebook's defaults with your own custom behavior.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Extending the Jupyter Server <https://jupyter-server.readthedocs.io/en/stable/developers/index.html>
|
||||
frontend_extensions
|
||||