提交开源分析项目

main
李昌宸 3 weeks ago
parent 6240cc2fd3
commit f7170d0e3f

3
.gitignore vendored

@ -0,0 +1,3 @@
# 忽略文件配置
__pycache__/
*.pyc

@ -0,0 +1,4 @@
# 开源分析项目
项目根目录D:\kaiyuanfenxixiangmu
源代码目录src\
提交时间2025/12/27 周六 21:11:37.74

@ -0,0 +1,13 @@
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

@ -0,0 +1,21 @@
{
"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"
}

@ -0,0 +1,23 @@
node_modules
**/build
**/lib
**/node_modules
**/mock_packages
**/static
**/typings
**/schemas
**/themes
coverage
*.map.js
*.bundle.js
app/index.template.js
# jetbrains IDE stuff
.idea/
# ms IDE stuff
.history/
.vscode/
# Pixi environments
.pixi

@ -0,0 +1,57 @@
module.exports = {
env: {
browser: true,
es6: true,
commonjs: true,
node: true,
'jest/globals': true,
},
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'plugin:react/recommended',
'plugin:jest/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.eslint.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'jest'],
rules: {
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'interface',
format: ['PascalCase'],
custom: {
regex: '^I[A-Z]',
match: true,
},
},
],
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/quotes': [
'error',
'single',
{ avoidEscape: true, allowTemplateLiterals: false },
],
'jest/no-done-callback': 'off',
curly: ['error', 'all'],
eqeqeq: 'error',
'prefer-arrow-callback': 'error',
},
settings: {
react: {
version: 'detect',
},
},
};

@ -0,0 +1,13 @@
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"rules": {
"semi": 1,
"no-cond-assign": 2,
"no-debugger": 2,
"comma-dangle": 0,
"no-unreachable": 2
}
}

@ -0,0 +1,2 @@
# Run auto-formatters: https://github.com/jupyter/notebook/pull/6335
a7717d90f128368296fe3434deba5acd6031edab

@ -0,0 +1,2 @@
# SCM syntax highlighting & preventing 3-way merges
pixi.lock merge=binary linguist-language=YAML linguist-generated=true

@ -0,0 +1,79 @@
---
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
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.
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.
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.
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, such as links to the offending code, snapshots of the UI issue, code blocks with your console logs, etc.
-->
## Description
<!--Describe the bug clearly and concisely. Include screenshots if possible-->
## Reproduce
<!--Describe step-by-step instructions 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 -->
<!--The more content you provide, the more we can help!-->
<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>
<details><summary>Command Line Output</summary>
<pre>
Paste the output from your command line running `jupyter notebook` here, use `--debug` if possible.
</pre>
</details>
<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.
</pre>
</details>

@ -0,0 +1,14 @@
blank_issues_enabled: false
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://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.
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.

@ -0,0 +1,41 @@
---
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 [...]
-->

@ -0,0 +1,44 @@
name: 'Build Jupyter Notebook'
description: 'Build Jupyter Notebook from source'
runs:
using: 'composite'
steps:
- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Install dependencies
shell: bash
run: |
python -m pip install hatch
- name: Build pypi distributions
shell: bash
run: |
hatch build
- name: Build npm distributions
shell: bash
run: |
mkdir pkgs
hatch run npm_pack
cp packages/*/*.tgz pkgs
- name: Build checksum file
shell: bash
run: |
cd dist
sha256sum * | tee SHA256SUMS
cd ../pkgs
sha256sum * | tee SHA256SUMS
- name: Upload distributions
uses: actions/upload-artifact@v4
with:
name: notebook-dist-${{ github.run_number }}
path: ./dist
- name: Upload distributions
uses: actions/upload-artifact@v4
with:
name: notebook-pkgs-${{ github.run_number }}
path: ./pkgs

@ -0,0 +1,20 @@
# This action automatically schedules issues to be closed that have been
# labeled as answered if there is no activity on them for 30 days. This takes
# care of the common usecase of an issue being answered to the best of our
# ability and no other follow-up from the submitter.
name: 'Close answered issues'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
skip-stale-issue-message: true
days-before-stale: 30
days-before-close: 7
stale-issue-label: 'status:Closing as Answered'
only-issue-labels: 'status:Answered'

@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
groups:
actions:
patterns:
- "*"
- package-ecosystem: 'pip'
directory: '/'
schedule:
interval: 'weekly'

@ -0,0 +1,2 @@
addBinderLink: false
triageLabel: 'status:Needs Triage'

@ -0,0 +1,17 @@
# 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,18 @@
name: Binder Badge
on:
pull_request_target:
types: [opened]
permissions:
contents: read
jobs:
binder:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: jupyterlab/maintainer-tools/.github/actions/binder-link@v1
with:
github_token: ${{ secrets.github_token }}
url_path: tree

@ -0,0 +1,214 @@
name: Build
on:
push:
branches: ['main']
pull_request:
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
PIP_DISABLE_PIP_VERSION_CHECK: 1
defaults:
run:
shell: bash -eux {0}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build
uses: ./.github/actions/build-dist
test:
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
# used by the jupyterlab/maintainer-tools base-setup action
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Test the package
run: hatch run cov:test
- name: JavaScript tests
run: |
hatch run js_test
- name: Integration Tests
run: |
pip install .
cd
jupyter labextension list 2>&1 | grep -ie "@jupyter-notebook/lab-extension.*enabled.*ok" -
jupyter server extension list 2>&1 | grep -ie "notebook.*enabled" -
python -m jupyterlab.browser_check
- uses: jupyterlab/maintainer-tools/.github/actions/upload-coverage@v1
coverage:
runs-on: ubuntu-latest
needs:
- test
steps:
- uses: actions/checkout@v6
- uses: jupyterlab/maintainer-tools/.github/actions/report-coverage@v1
with:
fail_under: 78
test_docs:
name: Test Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- run: |
sudo apt-get update
sudo apt install enchant-2 # for spelling
# pandoc is not up to date in the ubuntu repos, so we install directly
wget https://github.com/jgm/pandoc/releases/download/2.14.2/pandoc-2.14.2-1-amd64.deb && sudo dpkg -i pandoc-2.14.2-1-amd64.deb
- run: hatch run docs:build
test_minimum_versions:
name: Test Minimum Versions
timeout-minutes: 20
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
with:
dependency_type: minimum
python_version: '3.10'
- name: Run the unit tests
run: |
hatch run test:nowarn || hatch run test:nowarn --lf
test_prereleases:
name: Test Prereleases
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
with:
dependency_type: pre
- name: Run the tests
run: |
hatch run test:nowarn || hatch run test:nowarn --lf
install:
needs: [build]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ['3.9', '3.11', '3.13']
include:
- python: '3.9'
dist: 'notebook*.tar.gz'
- python: '3.11'
dist: 'notebook*.whl'
- python: '3.13'
dist: 'notebook*.whl'
- os: windows-latest
py_cmd: python
- os: macos-latest
py_cmd: python3
- os: ubuntu-latest
py_cmd: python
steps:
- name: Install Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python }}
allow-prereleases: true
- uses: actions/download-artifact@v6
with:
name: notebook-dist-${{ github.run_number }}
path: ./dist
- name: Install the prerequisites
run: |
${{ matrix.py_cmd }} -m pip install -U pip wheel
- name: Install the package
run: |
cd dist
${{ matrix.py_cmd }} -m pip install -vv ${{ matrix.dist }}
- name: Validate environment
run: |
${{ matrix.py_cmd }} -m pip freeze
${{ matrix.py_cmd }} -m pip check
- name: Validate the install
run: |
jupyter labextension list
jupyter labextension list 2>&1 | grep -ie "@jupyter-notebook/lab-extension.*enabled.*ok" -
jupyter server extension list
jupyter server extension list 2>&1 | grep -ie "notebook.*enabled" -
jupyter notebook --version
jupyter notebook --help
check_links:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1
with:
ignore_links: 'https://playwright.dev/docs/test-cli/ https://blog.jupyter.org/.* https://mybinder.org/v2/gh/jupyter/notebook/main https://nbviewer.jupyter.org https://stackoverflow.com https://github.com/[^/]+/?$'
ignore_glob: 'ui-tests/test/notebooks/*'
test_lint:
name: Test Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Run Linters
run: |
hatch run typing:test
hatch run lint:build
pipx run interrogate -v .
pipx run doc8 --max-line-length=200 docs/source *.md
npm install -g yarn
yarn
yarn eslint:check
yarn prettier:check
yarn build:utils
yarn integrity
tests_check: # This job does nothing and is only used for the branch protection
if: always()
needs:
- coverage
- install
- test_lint
- test_docs
- test_minimum_versions
- test_prereleases
- check_links
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

@ -0,0 +1,84 @@
name: Build Utilities
on:
push:
branches: ['main']
pull_request:
defaults:
run:
shell: bash -l {0}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
jobs:
versioning:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Install dependencies
run: |
python -m pip install -U "jupyterlab>=4.6.0a0,<4.7" hatch
jlpm
jlpm run build
- name: Configure git identity to commit
run: |
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
- name: Reset version
run: |
hatch version 9.8.7
jlpm run lerna version 9.8.7 --no-push --force-publish --no-git-tag-version --yes
git commit -am "Release 9.8.7"
- name: Patch Release
run: |
jlpm release:patch --force
- name: Minor Release
run: |
jlpm release:bump minor --force
- name: Release Cycle
run: |
# beta
jlpm release:bump release --force
# rc
jlpm release:bump release --force
# final
jlpm release:bump release --force
- name: Major Release
run: |
jlpm release:bump major --force
npm:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Python
uses: actions/setup-python@v6
with:
python-version: '3.10'
architecture: 'x64'
- name: Install dependencies
run: |
python -m pip install -U "jupyterlab>=4.6.0a0,<4.7" pip
jlpm
jlpm run build

@ -0,0 +1,33 @@
name: Check Release
on:
push:
branches: ['main']
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
check_release:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- name: Check Release
uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
version_spec: next
- name: Upload Distributions
uses: actions/upload-artifact@v5
with:
name: notebook-jupyter-releaser-dist-${{ github.run_number }}
path: .jupyter_releaser_checkout/dist

@ -0,0 +1,16 @@
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

@ -0,0 +1,23 @@
name: 'Lock Closed Threads'
on:
schedule:
- cron: '0 0 * * *'
permissions:
contents: read
jobs:
lock:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '180'
issue-lock-labels: 'status:resolved-locked'
pr-lock-inactive-days: '180'
pr-lock-labels: 'status:resolved-locked'

@ -0,0 +1,117 @@
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

@ -0,0 +1,49 @@
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 }}"

@ -0,0 +1,34 @@
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 }}"

@ -0,0 +1,60 @@
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 }}

@ -0,0 +1,93 @@
name: UI Tests
on:
push:
branches: ['main']
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
permissions:
contents: read
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build
uses: ./.github/actions/build-dist
ui-tests:
needs: [build]
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
browser: [firefox, chromium]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Base Setup
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
- 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
- name: Install the test dependencies
run: |
cd ui-tests
jlpm
jlpm playwright install
- name: Test
run: |
cd ui-tests
jlpm test --browser ${{ matrix.browser }}
- name: Upload Playwright Test assets
if: always()
uses: actions/upload-artifact@v5
with:
name: notebook-${{ matrix.browser }}-test-assets
path: |
ui-tests/test-results
- name: Upload Playwright Test report
if: always()
uses: actions/upload-artifact@v5
with:
name: notebook-${{ matrix.browser }}-test-report
path: |
ui-tests/playwright-report
- name: Update snapshots
if: failure()
run: |
cd ui-tests
# remove previous snapshots from other browser
jlpm rimraf "test/**/*-snapshots/*.png"
# generate new snapshots
jlpm run test:update --browser ${{ matrix.browser }}
- name: Upload updated snapshots
if: failure()
uses: actions/upload-artifact@v5
with:
name: notebook-${{ matrix.browser }}-updated-snapshots
path: ui-tests/test

@ -0,0 +1,128 @@
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

20
src/.gitignore vendored

@ -0,0 +1,20 @@
# Python缓存文件
__pycache__/
*.pyc
*.pyo
ECHO 处于打开状态。
# 临时文件
*.swp
*.swo
ECHO 处于打开状态。
# IDE配置文件
.vscode/
.idea/
ECHO 处于打开状态。
# 系统文件
.DS_Store
Thumbs.db
ECHO 处于打开状态。
# 大型依赖目录
node_modules/
.yarn/

@ -0,0 +1,86 @@
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]

@ -0,0 +1,12 @@
node_modules
.mypy_cache
.ruff_cache
**/node_modules
**/lib
**/package.json
**/static
**/labextension
build
CHANGELOG.md
app/index.template.js
.pixi

@ -0,0 +1,3 @@
{
"singleQuote": true
}

@ -0,0 +1,13 @@
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]'

@ -0,0 +1,5 @@
enableImmutableInstalls: false
enableInlineBuilds: false
enableTelemetry: false
httpTimeout: 60000
nodeLinker: node-modules

File diff suppressed because one or more lines are too long

@ -0,0 +1,247 @@
# Contributing to Jupyter Notebook
Thanks for contributing to Jupyter Notebook!
Make sure to follow [Project Jupyter's Code of Conduct](https://jupyter.org/governance/conduct/code-of-conduct)
for a friendly and welcoming collaborative environment.
## Setting up a development environment
Note: You will need NodeJS to build the extension package.
The `jlpm` command is JupyterLab's pinned version of [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use
`yarn` or `npm` in lieu of `jlpm` below.
**Note**: we recommend using `mamba` to speed up the creation of the environment.
```bash
# create a new environment
mamba create -n notebook -c conda-forge python nodejs -y
# activate the environment
mamba activate notebook
# Install package in development mode
pip install -e ".[dev,docs,test]"
# Install dependencies and build packages
jlpm
jlpm build
# Link the notebook extension and @jupyter-notebook schemas
jlpm develop
# Enable the server extension
jupyter server extension enable notebook
```
`notebook` follows a monorepo structure. To build all the packages at once:
```bash
jlpm build
```
There is also a `watch` script to watch for changes and rebuild the app automatically:
```bash
jlpm watch
```
To make sure the `notebook` server extension is installed:
```bash
$ jupyter server extension list
Config dir: /home/username/.jupyter
Config dir: /home/username/miniforge3/envs/notebook/etc/jupyter
jupyterlab enabled
- Validating jupyterlab...
jupyterlab 3.0.0 OK
notebook enabled
- Validating notebook...
notebook 7.0.0a0 OK
Config dir: /usr/local/etc/jupyter
```
Then start Jupyter Notebook with:
```bash
jupyter notebook
```
### Local changes in Notebook dependencies
The development installation described above fetches JavaScript dependencies from `npm`.
according to the versions in the _package.json_ file.
However, it is sometimes useful to be able to test changes in Notebook, with dependencies (e.g. `@jupyterlab` packages) that have not yet
been published.
[yalc](https://github.com/wclr/yalc) can help you use local JavaScript packages when building Notebook, acting as a local package repository.
- Install yalc globally in your environment:
`npm install -g yalc`
- Publish your dependency package:\
`yalc publish`, from the package root directory.\
For instance, if you are developing on _@jupyterlab/ui-components_, this command must be executed from
_path_to_jupyterlab/packages/ui-components_.
- Depend on this local repository in Notebook:
- from the Notebook root directory:\
`yalc add your_package` : this will create a _dependencies_ entry in the main _package.json_ file.\
With the previous example, it would be `yalc add @jupyterlab/ui-components`.
- Notebook is a monorepo, so we want this dependency to be 'linked' as a resolution (for all sub-packages) instead
of a dependency.\
The easiest way is to manually move the new entry in _package.json_ from _dependencies_ to _resolutions_.
- Build Notebook with the local dependency:\
`jlpm install && jlpm build`
Changes in the dependency must then be built and pushed using `jlpm build && yalc push` (from the package root directory),
and fetched from Notebook using `yarn install`.
**Warning**: you need to make sure that the dependencies of Notebook and the local package match correctly,
otherwise there will be errors with webpack during build.\
In the previous example, both _@jupyterlab/ui-components_ and Notebook depend on _@jupyterlab/coreutils_. We
strongly advise you to depend on the same version.
## Running Tests
To run the tests:
```bash
jlpm run build:test
jlpm run test
```
There are also end to end tests to cover higher level user interactions, located in the `ui-tests` folder. To run these tests:
```bash
cd ui-tests
#install required packages for jlpm
jlpm
#install playwright
jlpm playwright install
# start a new Jupyter server in a terminal
jlpm start
# in a new terminal, run the tests
jlpm test
```
The `test` script calls the Playwright test runner. You can pass additional arguments to `playwright` by appending parameters to the command. For example to run the test in headed mode, `jlpm test --headed`.
Check out the [Playwright Command Line Reference](https://playwright.dev/docs/test-cli/) for more information about the available command line options.
Running the end to end tests in headful mode will trigger something like the following:
![playwright-headed-demo](https://user-images.githubusercontent.com/591645/141274633-ca9f9c2f-eef6-430e-9228-a35827f8133d.gif)
## Tasks caching
The repository is configured to use the Lerna caching system (via `nx`) for some of the development scripts.
This helps speed up rebuilds when running `jlpm run build` multiple times to avoid rebuilding packages that have not changed on disk.
You can generate a graph to have a better idea of the dependencies between all the packages using the following command:
```
npx nx graph
```
Running the command will open a browser tab by default with a graph that looks like the following:
![a screenshot showing the nx task graph](https://github.com/jupyter/notebook/assets/591645/34eb46f0-b0e5-44b6-9430-ae5fbd673a4b)
To learn more about Lerna caching:
- https://lerna.js.org/docs/features/cache-tasks
- https://nx.dev/features/cache-task-results
### Updating reference snapshots
Often a PR might make changes to the user interface, which can cause the visual regression tests to fail.
If you want to update the reference snapshots while working on a PR you can post the following sentence as a GitHub comment:
```
bot please update playwright snapshots
```
This will trigger a GitHub Action that will run the UI tests automatically and push new commits to the branch if the reference snapshots have changed.
## Code Styling
All non-python source code is formatted using [prettier](https://prettier.io) and python source code is formatted using [black](https://github.com/psf/black).
When code is modified and committed, all staged files will be
automatically formatted using pre-commit git hooks (with help from
[pre-commit](https://github.com/pre-commit/pre-commit). The benefit of
using code formatters like `prettier` and `black` is that it removes the topic of
code style from the conversation when reviewing pull requests, thereby
speeding up the review process.
As long as your code is valid,
the pre-commit hook should take care of how it should look.
`pre-commit` and its associated hooks will automatically be installed when
you run `pip install -e ".[dev,test]"`
To install `pre-commit` manually, run the following:
```shell
pip install pre-commit
pre-commit install
```
You can invoke the pre-commit hook by hand at any time with:
```shell
pre-commit run
```
which should run any autoformatting on your code
and tell you about any errors it couldn't fix automatically.
You may also install [black integration](https://github.com/psf/black#editor-integration)
into your text editor to format code automatically.
If you have already committed files before setting up the pre-commit
hook with `pre-commit install`, you can fix everything up using
`pre-commit run --all-files`. You need to make the fixing commit
yourself after that.
You may also use the prettier npm script (e.g. `npm run prettier` or
`yarn prettier` or `jlpm prettier`) to format the entire code base.
We recommend installing a prettier extension for your code editor and
configuring it to format your code with a keyboard shortcut, or
automatically on save.
Some of the hooks only run on CI by default, but you can invoke them by
running with the `--hook-stage manual` argument.
## Documentation
First make sure you have set up a development environment as described above.
Then run the following command to build the docs:
```shell
hatch run docs:build
```
In a separate terminal window, run the following command to serve the documentation:
```shell
hatch run docs:serve
```
Now open a web browser and navigate to `http://localhost:8000` to access the documentation.
## Contributing from the browser
Alternatively you can also contribute to Jupyter Notebook without setting up a local environment, directly from a web browser:
- [GitHub CodeSpaces](https://github.com/codespaces) is directly integrated into GitHub. This repository uses the [pixi](https://pixi.sh/) package manager to set up the development environment. To contribute after the Codespace is started:
- Run `pixi shell` in a terminal to activate the development environment
- Use the commands above for building the extension and running the tests, for example: `jlpm build`
- To start the application: `pixi run start`. A popup should appear with a button to open the Jupyter Notebook in a new browser tab. If the popup does not appear, you can navigate to the "Forwarded ports" panel to find the URL to the application.
- GitHub's [built-in editor](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files) is suitable for contributing small fixes.
- A more advanced [github.dev](https://docs.github.com/en/codespaces/the-githubdev-web-based-editor) editor can be accessed by pressing the dot (.) key while in the Jupyter Notebook GitHub repository

@ -0,0 +1,31 @@
BSD 3-Clause License
- Copyright (c) 2001-2015, IPython Development Team
- Copyright (c) 2015-, Jupyter Development Team
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,133 @@
# Jupyter Notebook
![Github Actions Status](https://github.com/jupyter/notebook/workflows/Build/badge.svg)
[![Documentation Status](https://readthedocs.org/projects/jupyter-notebook/badge/?version=latest)](https://jupyter-notebook.readthedocs.io/en/latest/?badge=latest)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/jupyter/notebook/main?urlpath=tree)
The Jupyter notebook is a web-based notebook environment for interactive
computing.
![Jupyter notebook example](docs/resources/running_code_med.png 'Jupyter notebook example')
## Maintained versions
We maintain the **two most recently released major versions of Jupyter Notebook**,
Classic Notebook v6 and Notebook v7. Notebook v5 is no longer maintained.
All Notebook v5 users are strongly advised to upgrade to Classic Notebook v6 as soon as possible.
Upgrading to Notebook v7 may require more work, if you use custom extensions, as extensions written
for Notebook v5 or Classic Notebook v6 are not compatible with Notebook v7.
### Notebook v7
The newest major version of Notebook is based on:
- JupyterLab components for the frontend
- Jupyter Server for the Python server
This represents a significant change to the `jupyter/notebook` code base.
To learn more about Notebook v7: https://jupyter.org/enhancement-proposals/79-notebook-v7/notebook-v7.html
### Classic Notebook v6
Maintenance and security-related issues [only](https://github.com/jupyter/notebook-team-compass/issues/5#issuecomment-1085254000) are now being addressed in the [`6.5.x`](https://github.com/jupyter/notebook/tree/6.5.x) branch.
It depends on [`nbclassic`](https://github.com/jupyter/nbclassic) for the HTML/JavaScript/CSS assets.
New features and continuous improvement is now focused on Notebook v7 (see section above).
If you have an open pull request with a new feature or if you were planning to open one, we encourage switching over to the Jupyter Server and JupyterLab architecture, and distribute it as a server extension and / or JupyterLab prebuilt extension. That way your new feature will also be compatible with the new Notebook v7.
## Jupyter notebook, the language-agnostic evolution of IPython notebook
Jupyter notebook is a language-agnostic HTML notebook application for
Project Jupyter. In 2015, Jupyter notebook was released as a part of
The Big Split™ of the IPython codebase. IPython 3 was the last major monolithic
release containing both language-agnostic code, such as the _IPython notebook_,
and language specific code, such as the _IPython kernel for Python_. As
computing spans across many languages, Project Jupyter will continue to develop the
language-agnostic **Jupyter notebook** in this repo and with the help of the
community develop language specific kernels which are found in their own
discrete repos.
- [The Big Split™ announcement](https://blog.jupyter.org/the-big-split-9d7b88a031a7)
- [Jupyter Ascending blog post](https://blog.jupyter.org/jupyter-ascending-1bf5b362d97e)
## Installation
You can find the installation documentation for the
[Jupyter platform, on ReadTheDocs](https://jupyter.readthedocs.io/en/latest/install.html).
The documentation for advanced usage of Jupyter notebook can be found
[here](https://jupyter-notebook.readthedocs.io/en/latest/).
For a local installation, make sure you have
[pip installed](https://pip.readthedocs.io/en/stable/installing/) and run:
```bash
pip install notebook
```
## Usage - Running Jupyter notebook
### Running in a local installation
Launch with:
```bash
jupyter notebook
```
### Running in a remote installation
You need some configuration before starting Jupyter notebook remotely. See [Running a notebook server](https://jupyter-server.readthedocs.io/en/latest/operators/public-server.html).
## Development Installation
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for how to set up a local development installation.
## Contributing
If you are interested in contributing to the project, see [`CONTRIBUTING.md`](CONTRIBUTING.md).
## Community Guidelines and Code of Conduct
This repository is a Jupyter project and follows the Jupyter
[Community Guides and Code of Conduct](https://jupyter.readthedocs.io/en/latest/community/content-community.html).
## Resources
- [Project Jupyter website](https://jupyter.org)
- [Online Demo at jupyter.org/try](https://jupyter.org/try)
- [Documentation for Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/latest/)
- [Korean Version of Installation](https://github.com/ChungJooHo/Jupyter_Kor_doc/)
- [Documentation for Project Jupyter](https://jupyter.readthedocs.io/en/latest/index.html)
- [Issues](https://github.com/jupyter/notebook/issues)
- [Technical support - Jupyter Google Group](https://discourse.jupyter.org/)
## About the Jupyter Development Team
The Jupyter Development Team is the set of all contributors to the Jupyter project.
This includes all of the Jupyter subprojects.
The core team that coordinates development on GitHub can be found here:
https://github.com/jupyter/.
## Our Copyright Policy
Jupyter uses a shared copyright model. Each contributor maintains copyright
over their contributions to Jupyter. But, it is important to note that these
contributions are typically only changes to the repositories. Thus, the Jupyter
source code, in its entirety is not the copyright of any single person or
institution. Instead, it is the collective copyright of the entire Jupyter
Development Team. If individual contributors want to maintain a record of what
changes/contributions they have specific copyright on, they should indicate
their copyright in the commit message of the change, when they commit the
change to one of the Jupyter repositories.
With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:
```
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
```

@ -0,0 +1,40 @@
# Releasing Jupyter Notebook
## Using `jupyter_releaser`
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).
## Manual Release
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/*
```

@ -0,0 +1,19 @@
from __future__ import annotations
from typing import Any
from ._version import __version__, version_info # noqa: F401
def _jupyter_server_extension_paths() -> list[dict[str, str]]:
return [{"module": "notebook"}]
def _jupyter_server_extension_points() -> list[dict[str, Any]]:
from .app import JupyterNotebookApp
return [{"module": "notebook", "app": JupyterNotebookApp}]
def _jupyter_labextension_paths() -> list[dict[str, str]]:
return [{"src": "labextension", "dest": "@jupyter-notebook/lab-extension"}]

@ -0,0 +1,7 @@
"""CLI entry point for notebook."""
import sys
from notebook.app import main
sys.exit(main()) # type:ignore[no-untyped-call]

@ -0,0 +1,40 @@
"""Version info for notebook."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import re
from collections import namedtuple
# Use "hatch version xx.yy.zz" to handle version changes
__version__ = "7.6.0a0"
# PEP440 version parser
_version_regex = re.compile(
r"""
(?P<major>\d+)
\.
(?P<minor>\d+)
\.
(?P<micro>\d+)
(?P<releaselevel>((a|b|rc|\.dev)))?
(?P<serial>\d+)?
""",
re.VERBOSE,
)
_version_fields = _version_regex.match(__version__).groupdict() # type:ignore[union-attr]
VersionInfo = namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"]) # noqa: PYI024
version_info = VersionInfo(
*[
field
for field in (
int(_version_fields["major"]),
int(_version_fields["minor"]),
int(_version_fields["micro"]),
_version_fields["releaselevel"] or "",
_version_fields["serial"] or "",
)
]
)

@ -0,0 +1,369 @@
"""Jupyter notebook application."""
from __future__ import annotations
import os
import re
import typing as t
from pathlib import Path
from jupyter_client.utils import ensure_async # type:ignore[attr-defined]
from jupyter_core.application import base_aliases
from jupyter_core.paths import jupyter_config_dir
from jupyter_server.base.handlers import JupyterHandler
from jupyter_server.extension.handler import (
ExtensionHandlerJinjaMixin,
ExtensionHandlerMixin,
)
from jupyter_server.serverapp import flags
from jupyter_server.utils import url_escape, url_is_absolute
from jupyter_server.utils import url_path_join as ujoin
from jupyterlab.commands import ( # type:ignore[import-untyped]
get_app_dir,
get_user_settings_dir,
get_workspaces_dir,
)
from jupyterlab_server import LabServerApp
from jupyterlab_server.config import ( # type:ignore[attr-defined]
LabConfig,
get_page_config,
recursive_update,
)
from jupyterlab_server.handlers import _camelCase, is_url
from notebook_shim.shim import NotebookConfigShimMixin # type:ignore[import-untyped]
from tornado import web
from traitlets import Bool, Unicode, default
from traitlets.config.loader import Config
from ._version import __version__
HERE = Path(__file__).parent.resolve()
Flags = dict[t.Union[str, tuple[str, ...]], tuple[t.Union[dict[str, t.Any], Config], str]]
app_dir = Path(get_app_dir())
version = __version__
# mypy: disable-error-code="no-untyped-call"
class NotebookBaseHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, JupyterHandler):
"""The base notebook API handler."""
@property
def custom_css(self) -> t.Any:
return self.settings.get("custom_css", True)
def get_page_config(self) -> dict[str, t.Any]:
"""Get the page config."""
config = LabConfig()
app: JupyterNotebookApp = self.extensionapp # type:ignore[assignment]
base_url = self.settings.get("base_url", "/")
page_config_data = self.settings.setdefault("page_config_data", {})
page_config = {
**page_config_data,
"appVersion": version,
"baseUrl": self.base_url,
"terminalsAvailable": self.settings.get("terminals_available", False),
"token": self.settings["token"],
"fullStaticUrl": ujoin(self.base_url, "static", self.name),
"frontendUrl": ujoin(self.base_url, "/"),
"exposeAppInBrowser": app.expose_app_in_browser,
}
server_root = self.settings.get("server_root_dir", "")
server_root = server_root.replace(os.sep, "/")
server_root = os.path.normpath(Path(server_root).expanduser())
try:
# Remove the server_root from pref dir
if self.serverapp.preferred_dir != server_root:
page_config["preferredPath"] = "/" + os.path.relpath(
self.serverapp.preferred_dir, server_root
)
else:
page_config["preferredPath"] = "/"
except Exception:
page_config["preferredPath"] = "/"
mathjax_config = self.settings.get("mathjax_config", "TeX-AMS_HTML-full,Safe")
# TODO Remove CDN usage.
mathjax_url = self.settings.get(
"mathjax_url",
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js",
)
if not url_is_absolute(mathjax_url) and not mathjax_url.startswith(self.base_url):
mathjax_url = ujoin(self.base_url, mathjax_url)
page_config.setdefault("mathjaxConfig", mathjax_config)
page_config.setdefault("fullMathjaxUrl", mathjax_url)
page_config.setdefault("jupyterConfigDir", jupyter_config_dir())
# Put all our config in page_config
for name in config.trait_names():
page_config[_camelCase(name)] = getattr(app, name)
# Add full versions of all the urls
for name in config.trait_names():
if not name.endswith("_url"):
continue
full_name = _camelCase("full_" + name)
full_url = getattr(app, name)
if not is_url(full_url):
# Relative URL will be prefixed with base_url
full_url = ujoin(base_url, full_url)
page_config[full_name] = full_url
labextensions_path = app.extra_labextensions_path + app.labextensions_path
recursive_update(
page_config,
get_page_config(
labextensions_path,
logger=self.log,
),
)
# modify page config with custom hook
page_config_hook = self.settings.get("page_config_hook", None)
if page_config_hook:
page_config = page_config_hook(self, page_config)
return page_config
class TreeHandler(NotebookBaseHandler):
"""A tree page handler."""
@web.authenticated
async def get(self, path: str = "") -> None:
"""
Display appropriate page for given path.
- A directory listing is shown if path is a directory
- Redirected to notebook page if path is a notebook
- Render the raw file if path is any other file
"""
path = path.strip("/")
cm = self.contents_manager
if await ensure_async(cm.dir_exists(path=path)):
if await ensure_async(cm.is_hidden(path)) and not cm.allow_hidden:
self.log.info("Refusing to serve hidden directory, via 404 Error")
raise web.HTTPError(404)
# Set treePath for routing to the directory
page_config = self.get_page_config()
page_config["treePath"] = path
tpl = self.render_template("tree.html", page_config=page_config)
return self.write(tpl)
if await ensure_async(cm.file_exists(path)):
# it's not a directory, we have redirecting to do
model = await ensure_async(cm.get(path, content=False))
if model["type"] == "notebook":
url = ujoin(self.base_url, "notebooks", url_escape(path))
else:
# Return raw content if file is not a notebook
url = ujoin(self.base_url, "files", url_escape(path))
self.log.debug("Redirecting %s to %s", self.request.path, url)
self.redirect(url)
return None
raise web.HTTPError(404)
class ConsoleHandler(NotebookBaseHandler):
"""A console page handler."""
@web.authenticated
def get(self, path: str | None = None) -> t.Any: # noqa: ARG002
"""Get the console page."""
tpl = self.render_template("consoles.html", page_config=self.get_page_config())
return self.write(tpl)
class TerminalHandler(NotebookBaseHandler):
"""A terminal page handler."""
@web.authenticated
def get(self, path: str | None = None) -> t.Any: # noqa: ARG002
"""Get the terminal page."""
tpl = self.render_template("terminals.html", page_config=self.get_page_config())
return self.write(tpl)
class FileHandler(NotebookBaseHandler):
"""A file page handler."""
@web.authenticated
def get(self, path: str | None = None) -> t.Any: # noqa: ARG002
"""Get the file page."""
tpl = self.render_template("edit.html", page_config=self.get_page_config())
return self.write(tpl)
class NotebookHandler(NotebookBaseHandler):
"""A notebook page handler."""
@web.authenticated
async def get(self, path: str = "") -> t.Any:
"""Get the notebook page. Redirect if it's a directory."""
path = path.strip("/")
cm = self.contents_manager
if await ensure_async(cm.dir_exists(path=path)):
url = ujoin(self.base_url, "tree", url_escape(path))
self.log.debug("Redirecting %s to %s since path is a directory", self.request.path, url)
self.redirect(url)
return None
tpl = self.render_template("notebooks.html", page_config=self.get_page_config())
return self.write(tpl)
class CustomCssHandler(NotebookBaseHandler):
"""A custom CSS handler."""
@web.authenticated
def get(self) -> t.Any:
"""Get the custom css file."""
self.set_header("Content-Type", "text/css")
page_config = self.get_page_config()
custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css"
if not Path(custom_css_file).is_file():
static_path_root = re.match("^(.*?)static", page_config["staticDir"])
if static_path_root is not None:
custom_dir = static_path_root.groups()[0]
custom_css_file = f"{custom_dir}custom/custom.css"
with Path(custom_css_file).open() as css_f:
return self.write(css_f.read())
aliases = dict(base_aliases)
class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp): # type:ignore[misc]
"""The notebook server extension app."""
name = "notebook"
app_name = "Jupyter Notebook"
description = "Jupyter Notebook - A web-based notebook environment for interactive computing"
version = version
app_version = Unicode(version, help="The version of the application.")
extension_url = "/"
default_url = Unicode("/tree", config=True, help="The default URL to redirect to from `/`")
file_url_prefix = "/tree"
load_other_extensions = True
app_dir = app_dir
subcommands: dict[str, t.Any] = {}
expose_app_in_browser = Bool(
False,
config=True,
help="Whether to expose the global app instance to browser via window.jupyterapp",
)
custom_css = Bool(
True,
config=True,
help="""Whether custom CSS is loaded on the page.
Defaults to True and custom CSS is loaded.
""",
)
flags: Flags = flags # type:ignore[assignment]
flags["expose-app-in-browser"] = (
{"JupyterNotebookApp": {"expose_app_in_browser": True}},
"Expose the global app instance to browser via window.jupyterapp.",
)
flags["custom-css"] = (
{"JupyterNotebookApp": {"custom_css": True}},
"Load custom CSS in template html files. Default is True",
)
@default("static_dir")
def _default_static_dir(self) -> str:
return str(HERE / "static")
@default("templates_dir")
def _default_templates_dir(self) -> str:
return str(HERE / "templates")
@default("app_settings_dir")
def _default_app_settings_dir(self) -> str:
return str(app_dir / "settings")
@default("schemas_dir")
def _default_schemas_dir(self) -> str:
return str(app_dir / "schemas")
@default("themes_dir")
def _default_themes_dir(self) -> str:
return str(app_dir / "themes")
@default("user_settings_dir")
def _default_user_settings_dir(self) -> str:
return t.cast(str, get_user_settings_dir())
@default("workspaces_dir")
def _default_workspaces_dir(self) -> str:
return t.cast(str, get_workspaces_dir())
def _prepare_templates(self) -> None:
super(LabServerApp, self)._prepare_templates()
self.jinja2_env.globals.update(custom_css=self.custom_css) # type:ignore[has-type]
def server_extension_is_enabled(self, extension: str) -> bool:
"""Check if server extension is enabled."""
if self.serverapp is None:
return False
try:
extension_enabled = (
self.serverapp.extension_manager.extensions[extension].enabled is True
)
except (AttributeError, KeyError, TypeError):
extension_enabled = False
return extension_enabled
def initialize_handlers(self) -> None:
"""Initialize handlers."""
assert self.serverapp is not None # noqa: S101
page_config = self.serverapp.web_app.settings.setdefault("page_config_data", {})
nbclassic_enabled = self.server_extension_is_enabled("nbclassic")
page_config["nbclassic_enabled"] = nbclassic_enabled
# If running under JupyterHub, add more metadata.
if "hub_prefix" in self.serverapp.tornado_settings:
tornado_settings = self.serverapp.tornado_settings
hub_prefix = tornado_settings["hub_prefix"]
page_config["hubPrefix"] = hub_prefix
page_config["hubHost"] = tornado_settings["hub_host"]
page_config["hubUser"] = tornado_settings["user"]
page_config["shareUrl"] = ujoin(hub_prefix, "user-redirect")
# Assume the server_name property indicates running JupyterHub 1.0.
if hasattr(self.serverapp, "server_name"):
page_config["hubServerName"] = self.serverapp.server_name
# avoid setting API token in page config
# $JUPYTERHUB_API_TOKEN identifies the server, not the client
# but at least make sure we don't use the token
# if the serverapp set one
page_config["token"] = ""
self.handlers.append(("/tree(.*)", TreeHandler))
self.handlers.append(("/notebooks(.*)", NotebookHandler))
self.handlers.append(("/edit(.*)", FileHandler))
self.handlers.append(("/consoles/(.*)", ConsoleHandler))
self.handlers.append(("/terminals/(.*)", TerminalHandler))
self.handlers.append(("/custom/custom.css", CustomCssHandler))
super().initialize_handlers()
def initialize(self, argv: list[str] | None = None) -> None: # noqa: ARG002
"""Subclass because the ExtensionApp.initialize() method does not take arguments"""
super().initialize()
main = launch_new_instance = JupyterNotebookApp.launch_instance
if __name__ == "__main__":
main()

@ -0,0 +1,244 @@
// 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);

@ -0,0 +1,274 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// Heavily inspired (and slightly tweaked) from:
// https://github.com/jupyterlab/jupyterlab/blob/master/examples/federated/core_package/rspack.config.js
const fs = require('fs-extra');
const path = require('path');
const rspack = require('@rspack/core');
const merge = require('webpack-merge').default;
const Handlebars = require('handlebars');
const { ModuleFederationPlugin } = rspack.container;
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const Build = require('@jupyterlab/builder').Build;
const WPPlugin = require('@jupyterlab/builder').WPPlugin;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const baseConfig = require('@jupyterlab/builder/lib/webpack.config.base');
const data = require('./package.json');
const names = Object.keys(data.dependencies).filter((name) => {
const packageData = require(path.join(name, 'package.json'));
return packageData.jupyterlab !== undefined;
});
// Ensure a clear build directory.
const buildDir = path.resolve(__dirname, 'build');
if (fs.existsSync(buildDir)) {
fs.removeSync(buildDir);
}
fs.ensureDirSync(buildDir);
// Handle the extensions.
const { mimeExtensions, plugins } = data.jupyterlab;
// Create the list of extension packages from the package.json metadata
const extensionPackages = new Set();
Object.keys(plugins).forEach((page) => {
const pagePlugins = plugins[page];
Object.keys(pagePlugins).forEach((name) => {
extensionPackages.add(name);
});
});
Handlebars.registerHelper('json', function (context) {
return JSON.stringify(context);
});
// custom help to check if a page corresponds to a value
Handlebars.registerHelper('ispage', function (key, page) {
return key === page;
});
// custom helper to load the plugins on the index page
Handlebars.registerHelper('list_plugins', function () {
let str = '';
const page = this;
Object.keys(this).forEach((extension) => {
const plugin = page[extension];
if (plugin === true) {
str += `require(\'${extension}\'),\n `;
} else if (Array.isArray(plugin)) {
const plugins = plugin.map((p) => `'${p}',`).join('\n');
str += `
require(\'${extension}\').default.filter(({id}) => [
${plugins}
].includes(id)),
`;
}
});
return str;
});
// Create the entry point and other assets in build directory.
const source = fs.readFileSync('index.template.js').toString();
const template = Handlebars.compile(source);
const extData = {
notebook_plugins: plugins,
notebook_mime_extensions: mimeExtensions,
};
const indexOut = template(extData);
fs.writeFileSync(path.join(buildDir, 'index.js'), indexOut);
// Copy extra files
const cssImports = path.resolve(__dirname, 'style.js');
fs.copySync(cssImports, path.resolve(buildDir, 'extraStyle.js'));
const extras = Build.ensureAssets({
packageNames: names,
output: buildDir,
schemaOutput: path.resolve(__dirname, '..', 'notebook'),
});
/**
* Create the rspack ``shared`` configuration
*/
function createShared(packageData) {
// Set up module federation sharing config
const shared = {};
// Make sure any resolutions are shared
for (let [pkg, requiredVersion] of Object.entries(packageData.resolutions)) {
shared[pkg] = { requiredVersion };
}
// Add any extension packages that are not in resolutions (i.e., installed from npm)
for (let pkg of extensionPackages) {
if (!shared[pkg]) {
shared[pkg] = {
requiredVersion: require(`${pkg}/package.json`).version,
};
}
}
// Add dependencies and sharedPackage config from extension packages if they
// are not already in the shared config. This means that if there is a
// conflict, the resolutions package version is the one that is shared.
const extraShared = [];
for (let pkg of extensionPackages) {
let pkgShared = {};
let {
dependencies = {},
jupyterlab: { sharedPackages = {} } = {},
} = require(`${pkg}/package.json`);
for (let [dep, requiredVersion] of Object.entries(dependencies)) {
if (!shared[dep]) {
pkgShared[dep] = { requiredVersion };
}
}
// Overwrite automatic dependency sharing with custom sharing config
for (let [dep, config] of Object.entries(sharedPackages)) {
if (config === false) {
delete pkgShared[dep];
} else {
if ('bundled' in config) {
config.import = config.bundled;
delete config.bundled;
}
pkgShared[dep] = config;
}
}
extraShared.push(pkgShared);
}
// Now merge the extra shared config
const mergedShare = {};
for (let sharedConfig of extraShared) {
for (let [pkg, config] of Object.entries(sharedConfig)) {
// Do not override the basic share config from resolutions
if (shared[pkg]) {
continue;
}
// Add if we haven't seen the config before
if (!mergedShare[pkg]) {
mergedShare[pkg] = config;
continue;
}
// Choose between the existing config and this new config. We do not try
// to merge configs, which may yield a config no one wants
let oldConfig = mergedShare[pkg];
// if the old one has import: false, use the new one
if (oldConfig.import === false) {
mergedShare[pkg] = config;
}
}
}
Object.assign(shared, mergedShare);
// Transform any file:// requiredVersion to the version number from the
// imported package. This assumes (for simplicity) that the version we get
// importing was installed from the file.
for (let [pkg, { requiredVersion }] of Object.entries(shared)) {
if (requiredVersion && requiredVersion.startsWith('file:')) {
shared[pkg].requiredVersion = require(`${pkg}/package.json`).version;
}
}
// Add singleton package information
for (let pkg of packageData.jupyterlab.singletonPackages) {
if (shared[pkg]) {
shared[pkg].singleton = true;
}
}
return shared;
}
// Make a bootstrap entrypoint
const entryPoint = path.join(buildDir, 'bootstrap.js');
const bootstrap = 'import("./index.js");';
fs.writeFileSync(entryPoint, bootstrap);
if (process.env.NODE_ENV === 'production') {
baseConfig.mode = 'production';
}
if (process.argv.includes('--analyze')) {
extras.push(new BundleAnalyzerPlugin());
}
const htmlPlugins = [];
['consoles', 'edit', 'error', 'notebooks', 'terminals', 'tree'].forEach(
(name) => {
htmlPlugins.push(
new HtmlWebpackPlugin({
chunksSortMode: 'none',
template: path.join(
path.resolve('./templates'),
`${name}_template.html`
),
title: name,
filename: path.join(
path.resolve(__dirname, '..', 'notebook/templates'),
`${name}.html`
),
})
);
}
);
module.exports = [
merge(baseConfig, {
mode: 'development',
entry: ['./publicpath.js', './' + path.relative(__dirname, entryPoint)],
output: {
path: path.resolve(__dirname, '..', 'notebook/static/'),
publicPath: '{{page_config.fullStaticUrl}}/',
library: {
type: 'var',
name: ['_JUPYTERLAB', 'CORE_OUTPUT'],
},
filename: '[name].[contenthash].js',
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
jlab_core: {
test: /[\\/]node_modules[\\/]@(jupyterlab|jupyter-notebook|lumino(?!\/datagrid))[\\/]/,
name: 'notebook_core',
},
},
},
},
resolve: {
fallback: { util: false },
},
plugins: [
...htmlPlugins,
new WPPlugin.JSONLicenseWebpackPlugin({
excludedPackageTest: (packageName) =>
packageName === '@jupyter-notebook/app',
}),
new ModuleFederationPlugin({
library: {
type: 'var',
name: ['_JUPYTERLAB', 'CORE_LIBRARY_FEDERATION'],
},
name: 'CORE_FEDERATION',
shared: createShared(data),
}),
],
}),
].concat(extras);
const logPath = path.join(buildDir, 'build_log.json');
fs.writeFileSync(logPath, JSON.stringify(module.exports, null, ' '));

@ -0,0 +1,244 @@
// 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);

@ -0,0 +1,443 @@
{
"name": "@jupyter-notebook/app",
"version": "7.6.0-alpha.0",
"private": true,
"scripts": {
"build": "rspack",
"build:prod": "rspack --config ./rspack.prod.config.js",
"clean": "rimraf build && jlpm run clean:static",
"clean:static": "rimraf -g \"../notebook/static/!(favicons)\"",
"watch": "rspack --watch --config rspack.config.js"
},
"resolutions": {
"@codemirror/state": "~6.5.2",
"@codemirror/view": "~6.38.1",
"@jupyter-notebook/application": "~7.6.0-alpha.0",
"@jupyter-notebook/application-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/console-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/docmanager-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/documentsearch-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/help-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/notebook-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/terminal-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/tree": "~7.6.0-alpha.0",
"@jupyter-notebook/tree-extension": "~7.6.0-alpha.0",
"@jupyter-notebook/ui-components": "~7.6.0-alpha.0",
"@jupyter/react-components": "~0.16.7",
"@jupyter/web-components": "~0.16.7",
"@jupyter/ydoc": "~3.1.0",
"@jupyterlab/application": "~4.6.0-alpha.0",
"@jupyterlab/application-extension": "~4.6.0-alpha.0",
"@jupyterlab/apputils": "~4.7.0-alpha.0",
"@jupyterlab/apputils-extension": "~4.6.0-alpha.0",
"@jupyterlab/attachments": "~4.6.0-alpha.0",
"@jupyterlab/audio-extension": "~4.6.0-alpha.0",
"@jupyterlab/cell-toolbar": "~4.6.0-alpha.0",
"@jupyterlab/cell-toolbar-extension": "~4.6.0-alpha.0",
"@jupyterlab/celltags-extension": "~4.6.0-alpha.0",
"@jupyterlab/codeeditor": "~4.6.0-alpha.0",
"@jupyterlab/codemirror": "~4.6.0-alpha.0",
"@jupyterlab/codemirror-extension": "~4.6.0-alpha.0",
"@jupyterlab/completer": "~4.6.0-alpha.0",
"@jupyterlab/completer-extension": "~4.6.0-alpha.0",
"@jupyterlab/console": "~4.6.0-alpha.0",
"@jupyterlab/console-extension": "~4.6.0-alpha.0",
"@jupyterlab/coreutils": "~6.6.0-alpha.0",
"@jupyterlab/csvviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/debugger": "~4.6.0-alpha.0",
"@jupyterlab/debugger-extension": "~4.6.0-alpha.0",
"@jupyterlab/docmanager": "~4.6.0-alpha.0",
"@jupyterlab/docmanager-extension": "~4.6.0-alpha.0",
"@jupyterlab/documentsearch": "~4.6.0-alpha.0",
"@jupyterlab/documentsearch-extension": "~4.6.0-alpha.0",
"@jupyterlab/extensionmanager": "~4.6.0-alpha.0",
"@jupyterlab/extensionmanager-extension": "~4.6.0-alpha.0",
"@jupyterlab/filebrowser": "~4.6.0-alpha.0",
"@jupyterlab/filebrowser-extension": "~4.6.0-alpha.0",
"@jupyterlab/fileeditor": "~4.6.0-alpha.0",
"@jupyterlab/fileeditor-extension": "~4.6.0-alpha.0",
"@jupyterlab/help-extension": "~4.6.0-alpha.0",
"@jupyterlab/htmlviewer": "~4.6.0-alpha.0",
"@jupyterlab/htmlviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/hub-extension": "~4.6.0-alpha.0",
"@jupyterlab/imageviewer": "~4.6.0-alpha.0",
"@jupyterlab/imageviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/javascript-extension": "~4.6.0-alpha.0",
"@jupyterlab/json-extension": "~4.6.0-alpha.0",
"@jupyterlab/logconsole-extension": "~4.6.0-alpha.0",
"@jupyterlab/lsp": "~4.6.0-alpha.0",
"@jupyterlab/lsp-extension": "~4.6.0-alpha.0",
"@jupyterlab/mainmenu": "~4.6.0-alpha.0",
"@jupyterlab/mainmenu-extension": "~4.6.0-alpha.0",
"@jupyterlab/markdownviewer": "~4.6.0-alpha.0",
"@jupyterlab/markdownviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/markedparser-extension": "~4.6.0-alpha.0",
"@jupyterlab/mathjax-extension": "~4.6.0-alpha.0",
"@jupyterlab/mermaid": "~4.6.0-alpha.0",
"@jupyterlab/mermaid-extension": "~4.6.0-alpha.0",
"@jupyterlab/metadataform": "~4.6.0-alpha.0",
"@jupyterlab/metadataform-extension": "~4.6.0-alpha.0",
"@jupyterlab/notebook": "~4.6.0-alpha.0",
"@jupyterlab/notebook-extension": "~4.6.0-alpha.0",
"@jupyterlab/observables": "~5.6.0-alpha.0",
"@jupyterlab/outputarea": "~4.6.0-alpha.0",
"@jupyterlab/pdf-extension": "~4.6.0-alpha.0",
"@jupyterlab/pluginmanager-extension": "~4.6.0-alpha.0",
"@jupyterlab/rendermime": "~4.6.0-alpha.0",
"@jupyterlab/rendermime-interfaces": "~3.14.0-alpha.0",
"@jupyterlab/running-extension": "~4.6.0-alpha.0",
"@jupyterlab/services": "~7.6.0-alpha.0",
"@jupyterlab/services-extension": "~4.6.0-alpha.0",
"@jupyterlab/settingeditor": "~4.6.0-alpha.0",
"@jupyterlab/settingeditor-extension": "~4.6.0-alpha.0",
"@jupyterlab/settingregistry": "~4.6.0-alpha.0",
"@jupyterlab/shortcuts-extension": "~5.4.0-alpha.0",
"@jupyterlab/statedb": "~4.6.0-alpha.0",
"@jupyterlab/statusbar": "~4.6.0-alpha.0",
"@jupyterlab/terminal": "~4.6.0-alpha.0",
"@jupyterlab/terminal-extension": "~4.6.0-alpha.0",
"@jupyterlab/theme-dark-extension": "~4.6.0-alpha.0",
"@jupyterlab/theme-dark-high-contrast-extension": "~4.6.0-alpha.0",
"@jupyterlab/theme-light-extension": "~4.6.0-alpha.0",
"@jupyterlab/toc-extension": "~6.6.0-alpha.0",
"@jupyterlab/tooltip": "~4.6.0-alpha.0",
"@jupyterlab/tooltip-extension": "~4.6.0-alpha.0",
"@jupyterlab/translation": "~4.6.0-alpha.0",
"@jupyterlab/translation-extension": "~4.6.0-alpha.0",
"@jupyterlab/ui-components": "~4.6.0-alpha.0",
"@jupyterlab/ui-components-extension": "~4.6.0-alpha.0",
"@jupyterlab/vega5-extension": "~4.6.0-alpha.0",
"@jupyterlab/video-extension": "~4.6.0-alpha.0",
"@lezer/common": "~1.2.1",
"@lezer/highlight": "~1.2.0",
"@lumino/algorithm": "~2.0.4",
"@lumino/application": "~2.4.5",
"@lumino/commands": "~2.3.3",
"@lumino/coreutils": "~2.2.2",
"@lumino/disposable": "~2.1.5",
"@lumino/domutils": "~2.0.4",
"@lumino/dragdrop": "~2.1.7",
"@lumino/messaging": "~2.0.4",
"@lumino/properties": "~2.0.4",
"@lumino/signaling": "~2.1.5",
"@lumino/virtualdom": "~2.0.4",
"@lumino/widgets": "~2.7.2",
"react": "~18.2.0",
"react-dom": "~18.2.0",
"yjs": "~13.6.8"
},
"dependencies": {
"@jupyter-notebook/application": "^7.6.0-alpha.0",
"@jupyter-notebook/application-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/console-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/docmanager-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/documentsearch-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/help-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/notebook-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/terminal-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/tree": "^7.6.0-alpha.0",
"@jupyter-notebook/tree-extension": "^7.6.0-alpha.0",
"@jupyter-notebook/ui-components": "^7.6.0-alpha.0",
"@jupyterlab/application-extension": "~4.6.0-alpha.0",
"@jupyterlab/apputils-extension": "~4.6.0-alpha.0",
"@jupyterlab/attachments": "~4.6.0-alpha.0",
"@jupyterlab/audio-extension": "~4.6.0-alpha.0",
"@jupyterlab/cell-toolbar-extension": "~4.6.0-alpha.0",
"@jupyterlab/celltags-extension": "~4.6.0-alpha.0",
"@jupyterlab/codemirror": "~4.6.0-alpha.0",
"@jupyterlab/codemirror-extension": "~4.6.0-alpha.0",
"@jupyterlab/completer-extension": "~4.6.0-alpha.0",
"@jupyterlab/console-extension": "~4.6.0-alpha.0",
"@jupyterlab/coreutils": "~6.6.0-alpha.0",
"@jupyterlab/csvviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/debugger-extension": "~4.6.0-alpha.0",
"@jupyterlab/docmanager-extension": "~4.6.0-alpha.0",
"@jupyterlab/documentsearch-extension": "~4.6.0-alpha.0",
"@jupyterlab/extensionmanager-extension": "~4.6.0-alpha.0",
"@jupyterlab/filebrowser-extension": "~4.6.0-alpha.0",
"@jupyterlab/fileeditor-extension": "~4.6.0-alpha.0",
"@jupyterlab/help-extension": "~4.6.0-alpha.0",
"@jupyterlab/htmlviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/hub-extension": "~4.6.0-alpha.0",
"@jupyterlab/imageviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/javascript-extension": "~4.6.0-alpha.0",
"@jupyterlab/json-extension": "~4.6.0-alpha.0",
"@jupyterlab/logconsole-extension": "~4.6.0-alpha.0",
"@jupyterlab/lsp": "~4.6.0-alpha.0",
"@jupyterlab/lsp-extension": "~4.6.0-alpha.0",
"@jupyterlab/mainmenu-extension": "~4.6.0-alpha.0",
"@jupyterlab/markdownviewer-extension": "~4.6.0-alpha.0",
"@jupyterlab/markedparser-extension": "~4.6.0-alpha.0",
"@jupyterlab/mathjax-extension": "~4.6.0-alpha.0",
"@jupyterlab/mermaid-extension": "~4.6.0-alpha.0",
"@jupyterlab/metadataform-extension": "~4.6.0-alpha.0",
"@jupyterlab/notebook-extension": "~4.6.0-alpha.0",
"@jupyterlab/pdf-extension": "~4.6.0-alpha.0",
"@jupyterlab/pluginmanager-extension": "~4.6.0-alpha.0",
"@jupyterlab/running-extension": "~4.6.0-alpha.0",
"@jupyterlab/services-extension": "~4.6.0-alpha.0",
"@jupyterlab/settingeditor": "~4.6.0-alpha.0",
"@jupyterlab/settingeditor-extension": "~4.6.0-alpha.0",
"@jupyterlab/shortcuts-extension": "~5.4.0-alpha.0",
"@jupyterlab/terminal-extension": "~4.6.0-alpha.0",
"@jupyterlab/theme-dark-extension": "~4.6.0-alpha.0",
"@jupyterlab/theme-dark-high-contrast-extension": "~4.6.0-alpha.0",
"@jupyterlab/theme-light-extension": "~4.6.0-alpha.0",
"@jupyterlab/toc-extension": "~6.6.0-alpha.0",
"@jupyterlab/tooltip-extension": "~4.6.0-alpha.0",
"@jupyterlab/translation-extension": "~4.6.0-alpha.0",
"@jupyterlab/ui-components-extension": "~4.6.0-alpha.0",
"@jupyterlab/vega5-extension": "~4.6.0-alpha.0",
"@jupyterlab/video-extension": "~4.6.0-alpha.0",
"@lumino/coreutils": "~2.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"yjs": "^13.5.40"
},
"devDependencies": {
"@jupyterlab/builder": "~4.6.0-alpha.0",
"@jupyterlab/buildutils": "~4.6.0-alpha.0",
"@rspack/cli": "^1.1.8",
"@rspack/core": "^1.1.8",
"@types/rimraf": "^3.0.2",
"fs-extra": "^8.1.0",
"glob": "~7.1.6",
"handlebars": "^4.7.7",
"rimraf": "^3.0.2",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-merge": "^5.8.0",
"whatwg-fetch": "^3.0.0"
},
"jupyterlab": {
"name": "Jupyter Notebook",
"mimeExtensions": {
"@jupyterlab/javascript-extension": true,
"@jupyterlab/json-extension": true,
"@jupyterlab/pdf-extension": true,
"@jupyterlab/vega5-extension": true
},
"plugins": {
"/": {
"@jupyter-notebook/application-extension": true,
"@jupyter-notebook/console-extension": true,
"@jupyter-notebook/docmanager-extension": true,
"@jupyter-notebook/documentsearch-extension": true,
"@jupyter-notebook/help-extension": true,
"@jupyter-notebook/notebook-extension": true,
"@jupyter-notebook/terminal-extension": true,
"@jupyterlab/application-extension": [
"@jupyterlab/application-extension:commands",
"@jupyterlab/application-extension:context-menu",
"@jupyterlab/application-extension:faviconbusy",
"@jupyterlab/application-extension:router",
"@jupyterlab/application-extension:top-bar",
"@jupyterlab/application-extension:top-spacer"
],
"@jupyterlab/apputils-extension": [
"@jupyterlab/apputils-extension:kernels-settings",
"@jupyterlab/apputils-extension:palette",
"@jupyterlab/apputils-extension:notification",
"@jupyterlab/apputils-extension:sanitizer",
"@jupyterlab/apputils-extension:sessionDialogs",
"@jupyterlab/apputils-extension:settings",
"@jupyterlab/apputils-extension:state",
"@jupyterlab/apputils-extension:themes",
"@jupyterlab/apputils-extension:themes-palette-menu",
"@jupyterlab/apputils-extension:toolbar-registry",
"@jupyterlab/apputils-extension:utilityCommands"
],
"@jupyterlab/audio-extension": true,
"@jupyterlab/codemirror-extension": true,
"@jupyterlab/completer-extension": [
"@jupyterlab/completer-extension:base-service",
"@jupyterlab/completer-extension:inline-completer",
"@jupyterlab/completer-extension:inline-completer-factory",
"@jupyterlab/completer-extension:inline-history",
"@jupyterlab/completer-extension:manager"
],
"@jupyterlab/console-extension": [
"@jupyterlab/console-extension:cell-executor",
"@jupyterlab/console-extension:completer",
"@jupyterlab/console-extension:factory",
"@jupyterlab/console-extension:foreign",
"@jupyterlab/console-extension:tracker"
],
"@jupyterlab/csvviewer-extension": true,
"@jupyterlab/docmanager-extension": [
"@jupyterlab/docmanager-extension:plugin",
"@jupyterlab/docmanager-extension:download",
"@jupyterlab/docmanager-extension:contexts",
"@jupyterlab/docmanager-extension:manager"
],
"@jupyterlab/documentsearch-extension": [
"@jupyterlab/documentsearch-extension:plugin"
],
"@jupyterlab/filebrowser-extension": [
"@jupyterlab/filebrowser-extension:factory",
"@jupyterlab/filebrowser-extension:default-file-browser"
],
"@jupyterlab/fileeditor-extension": [
"@jupyterlab/fileeditor-extension:plugin",
"@jupyterlab/fileeditor-extension:widget-factory"
],
"@jupyterlab/help-extension": [
"@jupyterlab/help-extension:resources"
],
"@jupyterlab/htmlviewer-extension": true,
"@jupyterlab/hub-extension": true,
"@jupyterlab/imageviewer-extension": true,
"@jupyterlab/lsp-extension": true,
"@jupyterlab/mainmenu-extension": [
"@jupyterlab/mainmenu-extension:plugin"
],
"@jupyterlab/markedparser-extension": true,
"@jupyterlab/mathjax-extension": true,
"@jupyterlab/mermaid-extension": true,
"@jupyterlab/notebook-extension": [
"@jupyterlab/notebook-extension:cell-executor",
"@jupyterlab/notebook-extension:code-console",
"@jupyterlab/notebook-extension:export",
"@jupyterlab/notebook-extension:factory",
"@jupyterlab/notebook-extension:tracker",
"@jupyterlab/notebook-extension:widget-factory"
],
"@jupyterlab/pluginmanager-extension": true,
"@jupyterlab/services-extension": true,
"@jupyterlab/shortcuts-extension": true,
"@jupyterlab/terminal-extension": true,
"@jupyterlab/theme-light-extension": true,
"@jupyterlab/theme-dark-extension": true,
"@jupyterlab/theme-dark-high-contrast-extension": true,
"@jupyterlab/translation-extension": true,
"@jupyterlab/ui-components-extension": true,
"@jupyterlab/video-extension": true
},
"/tree": {
"@jupyterlab/cell-toolbar-extension": true,
"@jupyterlab/extensionmanager-extension": true,
"@jupyterlab/filebrowser-extension": [
"@jupyterlab/filebrowser-extension:browser",
"@jupyterlab/filebrowser-extension:create-new-language-file",
"@jupyterlab/filebrowser-extension:download",
"@jupyterlab/filebrowser-extension:file-upload-status",
"@jupyterlab/filebrowser-extension:open-with",
"@jupyterlab/filebrowser-extension:search",
"@jupyterlab/filebrowser-extension:share-file"
],
"@jupyter-notebook/tree-extension": true,
"@jupyterlab/running-extension": [
"@jupyterlab/running-extension:plugin"
],
"@jupyterlab/settingeditor-extension": true
},
"/notebooks": {
"@jupyterlab/celltags-extension": true,
"@jupyterlab/cell-toolbar-extension": true,
"@jupyterlab/debugger-extension": [
"@jupyterlab/debugger-extension:completions",
"@jupyterlab/debugger-extension:config",
"@jupyterlab/debugger-extension:debug-console",
"@jupyterlab/debugger-extension:main",
"@jupyterlab/debugger-extension:notebooks",
"@jupyterlab/debugger-extension:service",
"@jupyterlab/debugger-extension:sidebar",
"@jupyterlab/debugger-extension:sources",
"@jupyterlab/debugger-extension:display-registry"
],
"@jupyterlab/logconsole-extension": true,
"@jupyterlab/metadataform-extension": true,
"@jupyterlab/notebook-extension": [
"@jupyterlab/notebook-extension:active-cell-tool",
"@jupyterlab/notebook-extension:completer",
"@jupyterlab/notebook-extension:copy-output",
"@jupyterlab/notebook-extension:metadata-editor",
"@jupyterlab/notebook-extension:search",
"@jupyterlab/notebook-extension:toc",
"@jupyterlab/notebook-extension:tools",
"@jupyterlab/notebook-extension:update-raw-mimetype"
],
"@jupyterlab/toc-extension": [
"@jupyterlab/toc-extension:registry",
"@jupyterlab/toc-extension:tracker"
],
"@jupyterlab/tooltip-extension": [
"@jupyterlab/tooltip-extension:manager",
"@jupyterlab/tooltip-extension:notebooks"
]
},
"/consoles": {
"@jupyterlab/tooltip-extension": [
"@jupyterlab/tooltip-extension:manager",
"@jupyterlab/tooltip-extension:consoles"
]
},
"/edit": {
"@jupyterlab/fileeditor-extension": [
"@jupyterlab/fileeditor-extension:completer",
"@jupyterlab/fileeditor-extension:search"
],
"@jupyterlab/markdownviewer-extension": true
}
},
"singletonPackages": [
"@codemirror/state",
"@codemirror/view",
"@jupyter-notebook/tree",
"@jupyter/react-components",
"@jupyter/web-components",
"@jupyter/ydoc",
"@jupyterlab/application",
"@jupyterlab/apputils",
"@jupyterlab/cell-toolbar",
"@jupyterlab/codeeditor",
"@jupyterlab/codemirror",
"@jupyterlab/completer",
"@jupyterlab/console",
"@jupyterlab/coreutils",
"@jupyterlab/debugger",
"@jupyterlab/docmanager",
"@jupyterlab/documentsearch",
"@jupyterlab/extensionmanager",
"@jupyterlab/filebrowser",
"@jupyterlab/fileeditor",
"@jupyterlab/htmlviewer",
"@jupyterlab/imageviewer",
"@jupyterlab/lsp",
"@jupyterlab/mainmenu",
"@jupyterlab/markdownviewer",
"@jupyterlab/mermaid",
"@jupyterlab/metadataform",
"@jupyterlab/notebook",
"@jupyterlab/observables",
"@jupyterlab/outputarea",
"@jupyterlab/rendermime",
"@jupyterlab/rendermime-interfaces",
"@jupyterlab/services",
"@jupyterlab/settingeditor",
"@jupyterlab/settingregistry",
"@jupyterlab/statedb",
"@jupyterlab/statusbar",
"@jupyterlab/terminal",
"@jupyterlab/tooltip",
"@jupyterlab/translation",
"@jupyterlab/ui-components",
"@lezer/common",
"@lezer/highlight",
"@lumino/algorithm",
"@lumino/application",
"@lumino/commands",
"@lumino/coreutils",
"@lumino/disposable",
"@lumino/domutils",
"@lumino/dragdrop",
"@lumino/messaging",
"@lumino/properties",
"@lumino/signaling",
"@lumino/virtualdom",
"@lumino/widgets",
"react",
"react-dom",
"yjs"
]
}
}

@ -0,0 +1,37 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// We dynamically set the webpack public path based on the page config
// settings from the JupyterLab app. We copy some of the pageconfig parsing
// logic in @jupyterlab/coreutils below, since this must run before any other
// files are loaded (including @jupyterlab/coreutils).
/**
* Get global configuration data for the Jupyter application.
*
* @param name - The name of the configuration option.
*
* @returns The config value or an empty string if not found.
*
* #### Notes
* All values are treated as strings.
* For browser based applications, it is assumed that the page HTML
* includes a script tag with the id `jupyter-config-data` containing the
* configuration as valid JSON. In order to support the classic Notebook,
* we fall back on checking for `body` data of the given `name`.
*/
function getOption(name) {
let configData = Object.create(null);
// Use script tag if available.
if (typeof document !== 'undefined' && document) {
const el = document.getElementById('jupyter-config-data');
if (el) {
configData = JSON.parse(el.textContent || '{}');
}
}
return configData[name] || '';
}
// eslint-disable-next-line no-undef
__webpack_public_path__ = getOption('fullStaticUrl') + '/';

@ -0,0 +1,274 @@
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
// Heavily inspired (and slightly tweaked) from:
// https://github.com/jupyterlab/jupyterlab/blob/master/examples/federated/core_package/rspack.config.js
const fs = require('fs-extra');
const path = require('path');
const rspack = require('@rspack/core');
const merge = require('webpack-merge').default;
const Handlebars = require('handlebars');
const { ModuleFederationPlugin } = rspack.container;
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const Build = require('@jupyterlab/builder').Build;
const WPPlugin = require('@jupyterlab/builder').WPPlugin;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const baseConfig = require('@jupyterlab/builder/lib/webpack.config.base');
const data = require('./package.json');
const names = Object.keys(data.dependencies).filter((name) => {
const packageData = require(path.join(name, 'package.json'));
return packageData.jupyterlab !== undefined;
});
// Ensure a clear build directory.
const buildDir = path.resolve(__dirname, 'build');
if (fs.existsSync(buildDir)) {
fs.removeSync(buildDir);
}
fs.ensureDirSync(buildDir);
// Handle the extensions.
const { mimeExtensions, plugins } = data.jupyterlab;
// Create the list of extension packages from the package.json metadata
const extensionPackages = new Set();
Object.keys(plugins).forEach((page) => {
const pagePlugins = plugins[page];
Object.keys(pagePlugins).forEach((name) => {
extensionPackages.add(name);
});
});
Handlebars.registerHelper('json', function (context) {
return JSON.stringify(context);
});
// custom help to check if a page corresponds to a value
Handlebars.registerHelper('ispage', function (key, page) {
return key === page;
});
// custom helper to load the plugins on the index page
Handlebars.registerHelper('list_plugins', function () {
let str = '';
const page = this;
Object.keys(this).forEach((extension) => {
const plugin = page[extension];
if (plugin === true) {
str += `require(\'${extension}\'),\n `;
} else if (Array.isArray(plugin)) {
const plugins = plugin.map((p) => `'${p}',`).join('\n');
str += `
require(\'${extension}\').default.filter(({id}) => [
${plugins}
].includes(id)),
`;
}
});
return str;
});
// Create the entry point and other assets in build directory.
const source = fs.readFileSync('index.template.js').toString();
const template = Handlebars.compile(source);
const extData = {
notebook_plugins: plugins,
notebook_mime_extensions: mimeExtensions,
};
const indexOut = template(extData);
fs.writeFileSync(path.join(buildDir, 'index.js'), indexOut);
// Copy extra files
const cssImports = path.resolve(__dirname, 'style.js');
fs.copySync(cssImports, path.resolve(buildDir, 'extraStyle.js'));
const extras = Build.ensureAssets({
packageNames: names,
output: buildDir,
schemaOutput: path.resolve(__dirname, '..', 'notebook'),
});
/**
* Create the rspack ``shared`` configuration
*/
function createShared(packageData) {
// Set up module federation sharing config
const shared = {};
// Make sure any resolutions are shared
for (let [pkg, requiredVersion] of Object.entries(packageData.resolutions)) {
shared[pkg] = { requiredVersion };
}
// Add any extension packages that are not in resolutions (i.e., installed from npm)
for (let pkg of extensionPackages) {
if (!shared[pkg]) {
shared[pkg] = {
requiredVersion: require(`${pkg}/package.json`).version,
};
}
}
// Add dependencies and sharedPackage config from extension packages if they
// are not already in the shared config. This means that if there is a
// conflict, the resolutions package version is the one that is shared.
const extraShared = [];
for (let pkg of extensionPackages) {
let pkgShared = {};
let {
dependencies = {},
jupyterlab: { sharedPackages = {} } = {},
} = require(`${pkg}/package.json`);
for (let [dep, requiredVersion] of Object.entries(dependencies)) {
if (!shared[dep]) {
pkgShared[dep] = { requiredVersion };
}
}
// Overwrite automatic dependency sharing with custom sharing config
for (let [dep, config] of Object.entries(sharedPackages)) {
if (config === false) {
delete pkgShared[dep];
} else {
if ('bundled' in config) {
config.import = config.bundled;
delete config.bundled;
}
pkgShared[dep] = config;
}
}
extraShared.push(pkgShared);
}
// Now merge the extra shared config
const mergedShare = {};
for (let sharedConfig of extraShared) {
for (let [pkg, config] of Object.entries(sharedConfig)) {
// Do not override the basic share config from resolutions
if (shared[pkg]) {
continue;
}
// Add if we haven't seen the config before
if (!mergedShare[pkg]) {
mergedShare[pkg] = config;
continue;
}
// Choose between the existing config and this new config. We do not try
// to merge configs, which may yield a config no one wants
let oldConfig = mergedShare[pkg];
// if the old one has import: false, use the new one
if (oldConfig.import === false) {
mergedShare[pkg] = config;
}
}
}
Object.assign(shared, mergedShare);
// Transform any file:// requiredVersion to the version number from the
// imported package. This assumes (for simplicity) that the version we get
// importing was installed from the file.
for (let [pkg, { requiredVersion }] of Object.entries(shared)) {
if (requiredVersion && requiredVersion.startsWith('file:')) {
shared[pkg].requiredVersion = require(`${pkg}/package.json`).version;
}
}
// Add singleton package information
for (let pkg of packageData.jupyterlab.singletonPackages) {
if (shared[pkg]) {
shared[pkg].singleton = true;
}
}
return shared;
}
// Make a bootstrap entrypoint
const entryPoint = path.join(buildDir, 'bootstrap.js');
const bootstrap = 'import("./index.js");';
fs.writeFileSync(entryPoint, bootstrap);
if (process.env.NODE_ENV === 'production') {
baseConfig.mode = 'production';
}
if (process.argv.includes('--analyze')) {
extras.push(new BundleAnalyzerPlugin());
}
const htmlPlugins = [];
['consoles', 'edit', 'error', 'notebooks', 'terminals', 'tree'].forEach(
(name) => {
htmlPlugins.push(
new HtmlWebpackPlugin({
chunksSortMode: 'none',
template: path.join(
path.resolve('./templates'),
`${name}_template.html`
),
title: name,
filename: path.join(
path.resolve(__dirname, '..', 'notebook/templates'),
`${name}.html`
),
})
);
}
);
module.exports = [
merge(baseConfig, {
mode: 'development',
entry: ['./publicpath.js', './' + path.relative(__dirname, entryPoint)],
output: {
path: path.resolve(__dirname, '..', 'notebook/static/'),
publicPath: '{{page_config.fullStaticUrl}}/',
library: {
type: 'var',
name: ['_JUPYTERLAB', 'CORE_OUTPUT'],
},
filename: '[name].[contenthash].js',
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
jlab_core: {
test: /[\\/]node_modules[\\/]@(jupyterlab|jupyter-notebook|lumino(?!\/datagrid))[\\/]/,
name: 'notebook_core',
},
},
},
},
resolve: {
fallback: { util: false },
},
plugins: [
...htmlPlugins,
new WPPlugin.JSONLicenseWebpackPlugin({
excludedPackageTest: (packageName) =>
packageName === '@jupyter-notebook/app',
}),
new ModuleFederationPlugin({
library: {
type: 'var',
name: ['_JUPYTERLAB', 'CORE_LIBRARY_FEDERATION'],
},
name: 'CORE_FEDERATION',
shared: createShared(data),
}),
],
}),
].concat(extras);
const logPath = path.join(buildDir, 'build_log.json');
fs.writeFileSync(logPath, JSON.stringify(module.exports, null, ' '));

@ -0,0 +1,15 @@
/*
* 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),
];

@ -0,0 +1,29 @@
/*
* 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,45 @@
<!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>

@ -0,0 +1,39 @@
<!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>

@ -0,0 +1,59 @@
<!DOCTYPE html>
<!--
Copyright (c) Jupyter Development Team.
Distributed under the terms of the Modified BSD License.
-->
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}{{page_title | e}}{% endblock %}</title>
{% block favicon %}<link rel="shortcut icon" type="image/x-icon" href="/static/favicons/favicon.ico">{% endblock %}
</head>
<body class="jp-ThemedContainer">
{% block stylesheet %}
<style type="text/css">
/* disable initial hide */
div#header, div#site {
display: block;
}
</style>
{% endblock %}
{% block site %}
<div class="error">
{% block h1_error %}
<h1>{{status_code | e}} : {{status_message | e}}</h1>
{% endblock h1_error %}
{% block error_detail %}
{% if message %}
<p>The error was:</p>
<div class="traceback-wrapper">
<pre class="traceback">{{message | e}}</pre>
</div>
{% endif %}
{% endblock %}
</header>
{% endblock %}
{% block script %}
<script type='text/javascript'>
window.onload = function () {
var tb = document.getElementsByClassName('traceback')[0];
tb.scrollTop = tb.scrollHeight;
{% if message %}
console.error("{{message | e}}")
{% endif %}
};
</script>
{% endblock script %}
</body>
</html>

@ -0,0 +1,45 @@
<!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>

@ -0,0 +1,45 @@
<!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>

@ -0,0 +1,45 @@
<!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,12 @@
name: notebook
channels:
- conda-forge
dependencies:
- ipywidgets=8
- jupyterlab=4
- jupyterlab-language-pack-fr-FR
- matplotlib
- numpy
- nodejs=20
- python >=3.10,<3.11
- xeus-python

File diff suppressed because one or more lines are too long

@ -0,0 +1,5 @@
#!/bin/bash
set -euo pipefail
python -m pip install -e ".[dev,test]"
jlpm develop

@ -0,0 +1,44 @@
{
"name": "@jupyter-notebook/buildutils",
"version": "7.6.0-alpha.0",
"private": true,
"description": "Jupyter Notebook - Build Utilities",
"homepage": "https://github.com/jupyter/notebook",
"bugs": {
"url": "https://github.com/jupyter/notebook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/jupyter/notebook.git"
},
"license": "BSD-3-Clause",
"author": "Project Jupyter",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"directories": {
"lib": "lib/"
},
"files": [
"lib/*.d.ts",
"lib/*.js.map",
"lib/*.js"
],
"scripts": {
"build": "tsc",
"clean": "rimraf lib && rimraf tsconfig.tsbuildinfo",
"watch": "tsc -w --listEmittedFiles"
},
"dependencies": {
"@jupyterlab/buildutils": "~4.6.0-alpha.0",
"commander": "^6.2.0",
"fs-extra": "^9.1.0",
"semver": "^7.6.3",
"typescript": "~5.5.4"
},
"devDependencies": {
"@types/fs-extra": "^9.0.10",
"@types/node": "^22.13.4",
"@types/semver": "^7.5.8",
"rimraf": "^3.0.2"
}
}

@ -0,0 +1,56 @@
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import commander from 'commander';
import fs from 'fs-extra';
import path from 'path';
import process from 'process';
import { run } from '@jupyterlab/buildutils';
commander
.description('Setup the repository for develop mode')
.option('--overwrite', 'Force linking the notebook schemas')
.option('--source', 'The path to the notebook package')
.action((options: any) => {
const { overwrite } = options;
const prefix = run(
'python -c "import sys; print(sys.prefix)"',
{
stdio: 'pipe',
},
true
);
const source = path.resolve(options.source ?? process.cwd());
const sourceDir = path.join(
source,
'notebook',
'schemas',
'@jupyter-notebook'
);
const destDir = path.join(
prefix,
'share',
'jupyter',
'lab',
'schemas',
'@jupyter-notebook'
);
if (overwrite) {
try {
fs.removeSync(destDir);
console.log('Removed previous destination:', destDir);
} catch (e) {
console.info('Skip unlink', destDir);
}
}
console.log('Symlinking:', sourceDir, destDir);
fs.symlinkSync(sourceDir, destDir, 'dir');
});
commander.parse(process.argv);

@ -0,0 +1,45 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import { writePackageData } from '@jupyterlab/buildutils';
/**
* Ensure the application package resolutions.
*/
function ensureResolutions(): string[] {
const basePath = path.resolve('.');
const corePath = path.join(basePath, 'app', 'package.json');
const corePackage = fs.readJSONSync(corePath);
corePackage.resolutions = {};
const packages = Object.keys(corePackage.dependencies).concat(
corePackage.jupyterlab.singletonPackages
);
packages.forEach(async (name) => {
let version = '';
try {
const data = require(`${name}/package.json`);
version = data.version;
} catch {
const modulePath = require.resolve(name);
const parentDir = path.dirname(path.dirname(modulePath));
const data = require(path.join(parentDir, 'package.json'));
version = data.version;
}
// Insist on a restricted version in the yarn resolution.
corePackage.resolutions[name] = `~${version}`;
});
// Write the package.json back to disk.
if (writePackageData(corePath, corePackage)) {
return ['Updated dev mode'];
}
return [];
}
if (require.main === module) {
void ensureResolutions();
}

@ -0,0 +1,86 @@
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();

@ -0,0 +1,148 @@
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
/**
* Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/buildutils/src/bumpversion.ts
*/
import * as utils from '@jupyterlab/buildutils';
import commander from 'commander';
import { getPythonVersion, postbump } from './utils';
// Specify the program signature.
commander
.description('Update the version')
.option('--dry-run', 'Dry run')
.option('--force', 'Force the upgrade')
.option('--skip-commit', 'Whether to skip commit changes')
.arguments('<spec>')
.action((spec: any, opts: any) => {
// Get the previous version.
const prev = getPythonVersion();
const isFinal = /\d+\.\d+\.\d+$/.test(prev);
// Whether to commit after bumping
const commit = opts.skipCommit !== true;
// for "next", determine whether to use "patch" or "build"
if (spec === 'next') {
spec = isFinal ? 'patch' : 'build';
}
// For patch, defer to `patch:release` command
if (spec === 'patch') {
let cmd = 'jlpm run release:patch';
if (opts.force) {
cmd += ' --force';
}
if (!commit) {
cmd += ' --skip-commit';
}
utils.run(cmd);
process.exit(0);
}
// Make sure we have a valid version spec.
const options = ['major', 'minor', 'release', 'build'];
if (options.indexOf(spec) === -1) {
throw new Error(`Version spec must be one of: ${options}`);
}
if (isFinal && spec === 'build') {
throw new Error('Cannot increment a build on a final release');
}
// Run pre-bump script.
utils.prebump();
// Handle dry runs.
if (opts.dryRun) {
return;
}
// If this is a major release during the alpha cycle, bump
// just the Python version.
if (prev.indexOf('a') !== -1 && spec === 'major') {
// Bump the version.
utils.run(`hatch version ${spec}`);
// Run the post-bump script.
postbump(commit);
return;
}
// Determine the version spec to use for lerna.
let lernaVersion = 'preminor';
if (spec === 'build') {
lernaVersion = 'prerelease';
// a -> b
} else if (spec === 'release' && prev.indexOf('a') !== -1) {
lernaVersion = 'prerelease --preid=beta';
// b -> rc
} else if (spec === 'release' && prev.indexOf('b') !== -1) {
lernaVersion = 'prerelease --preid=rc';
// rc -> final
} else if (spec === 'release' && prev.indexOf('rc') !== -1) {
lernaVersion = 'patch';
}
if (lernaVersion === 'preminor') {
lernaVersion += ' --preid=alpha';
}
let cmd = `jlpm run lerna version --force-publish --no-push --no-git-tag-version ${lernaVersion}`;
if (opts.force) {
cmd += ' --yes';
}
// For a preminor release, we bump 10 minor versions so that we do
// not conflict with versions during minor releases of the top
// level package.
if (lernaVersion === 'preminor') {
for (let i = 0; i < 10; i++) {
utils.run(cmd);
}
} else {
utils.run(cmd);
}
// Bump the version.
let pySpec = spec;
if (spec === 'release') {
if (prev.indexOf('a') !== -1) {
pySpec = 'beta';
} else if (prev.indexOf('b') !== -1) {
pySpec = 'rc';
} else if (prev.indexOf('rc') !== -1) {
pySpec = 'release';
} else {
pySpec = 'alpha';
}
} else if (spec === 'build') {
if (prev.indexOf('a') !== -1) {
pySpec = 'a';
} else if (prev.indexOf('b') !== -1) {
pySpec = 'b';
} else if (prev.indexOf('rc') !== -1) {
pySpec = 'rc';
}
} else if (spec === 'major' || spec === 'minor') {
if (prev.indexOf('a') !== -1) {
pySpec = `${spec},beta`;
} else if (prev.indexOf('b') !== -1) {
pySpec = `${spec},rc`;
} else if (prev.indexOf('rc') !== -1) {
pySpec = `${spec},release`;
} else {
pySpec = `${spec},alpha`;
}
}
utils.run(`hatch version ${pySpec}`);
// Run the post-bump script.
postbump(commit);
});
commander.parse(process.argv);

@ -0,0 +1,51 @@
/* -----------------------------------------------------------------------------
| Copyright (c) Jupyter Development Team.
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
/**
* Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/buildutils/src/patch-release.ts
*/
import * as utils from '@jupyterlab/buildutils';
import commander from 'commander';
import { getPythonVersion, postbump } from './utils';
// Specify the program signature.
commander
.description('Create a patch release')
.option('--force', 'Force the upgrade')
.option('--skip-commit', 'Whether to skip commit changes')
.action((options: any) => {
// Make sure we can patch release.
const pyVersion = getPythonVersion();
if (
pyVersion.includes('a') ||
pyVersion.includes('b') ||
pyVersion.includes('rc')
) {
throw new Error('Can only make a patch release from a final version');
}
// Run pre-bump actions.
utils.prebump();
// Patch the python version
utils.run('hatch version patch');
// Version the changed
let cmd =
'jlpm run lerna version patch --no-push --force-publish --no-git-tag-version';
if (options.force) {
cmd += ' --yes';
}
utils.run(cmd);
// Whether to commit after bumping
const commit = options.skipCommit !== true;
postbump(commit);
});
commander.parse(process.argv);

@ -0,0 +1,170 @@
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,23 @@
import { run } from '@jupyterlab/buildutils';
/**
* Get the current version of notebook
*/
export function getPythonVersion(): string {
const cmd = 'hatch version';
const lines = run(cmd, { stdio: 'pipe' }, true).split('\n');
return lines[lines.length - 1];
}
export function postbump(commit = true): void {
// run the integrity
run('jlpm integrity');
const newPyVersion = getPythonVersion();
// Commit changes.
if (commit) {
run(`git commit -am "Release ${newPyVersion}"`);
run(`git tag ${newPyVersion}`);
}
}

@ -0,0 +1,10 @@
{
"extends": "../tsconfigbase",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"module": "commonjs"
},
"include": ["src/*"],
"references": []
}

@ -0,0 +1,7 @@
/*
Placeholder for custom user CSS
mainly to be overridden in profile/static/custom/custom.css
This will always be an empty file
*/

@ -0,0 +1,199 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage spelling gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
@echo " spelling to spell check the documentation"
clean:
rm -rf $(BUILDDIR)/*
rm -rf source/config.rst
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/JupyterNotebook.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/JupyterNotebook.qhc"
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/JupyterNotebook"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/JupyterNotebook"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
spelling:
$(SPHINXBUILD) -b spelling $(ALLSPHINXOPTS) $(BUILDDIR)/spelling
@echo "Spell check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/spelling/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

@ -0,0 +1,16 @@
{
"markdown": {
"parser": "gfm"
},
"plugins": ["plugins/markdown", "jsdoc_plugin.js"],
"source": {
"include": ["../notebook/static/notebook/js/notebook.js"]
},
"tags": {
"allowUnknownTags": true
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false
}
}

@ -0,0 +1,14 @@
exports.handlers = {
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] === '_') {
console.log(
'Private method "' + e.doclet.longname + '" not documented.'
);
e.doclet.memberof = '<anonymous>';
}
}
},
};

@ -0,0 +1,263 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
set I18NSPHINXOPTS=%SPHINXOPTS% source
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. xml to make Docutils-native XML files
echo. pseudoxml to make pseudoxml-XML files for display purposes
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
echo. coverage to run coverage check of the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
REM Check if sphinx-build is available and fallback to Python version if any
%SPHINXBUILD% 2> nul
if errorlevel 9009 goto sphinx_python
goto sphinx_ok
:sphinx_python
set SPHINXBUILD=python -m sphinx.__init__
%SPHINXBUILD% 2> nul
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
:sphinx_ok
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\JupyterNotebook.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\JupyterNotebook.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdf" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf
cd %~dp0
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "latexpdfja" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
cd %BUILDDIR%/latex
make all-pdf-ja
cd %~dp0
echo.
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
if "%1" == "coverage" (
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
if errorlevel 1 exit /b 1
echo.
echo.Testing of coverage in the sources finished, look at the ^
results in %BUILDDIR%/coverage/python.txt.
goto end
)
if "%1" == "xml" (
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The XML files are in %BUILDDIR%/xml.
goto end
)
if "%1" == "pseudoxml" (
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
goto end
)
:end

@ -0,0 +1,20 @@
# Add this into the info.plist file of an application
# and the icns icon in Contents/Resources
# then move the application twice :
# https://superuser.com/questions/178316/how-to-set-an-icon-for-a-file-type-on-mac
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>ipynb</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>ipynb_mac_icon</string>
<key>CFBundleTypeName</key>
<string>IPython notebook file</string>
<key>CFBundleTypeRole</key>
<string>None</string>
</dict>
<array>

@ -0,0 +1,16 @@
#!/bin/bash
INKSCAPE=inkscape
${INKSCAPE} -z -C --file=ipynb_icon_16x16.svg --export-png=ipynb_icon_16x16_uncrush.png
${INKSCAPE} -z -C --file=ipynb_icon_24x24.svg --export-png=ipynb_icon_24x24_uncrush.png
${INKSCAPE} -z -C --file=ipynb_icon_32x32.svg --export-png=ipynb_icon_32x32_uncrush.png
${INKSCAPE} -z -C --file=ipynb_icon_512x512.svg --export-png=ipynb_icon_64x64_uncrush.png -w 64 -h 64
${INKSCAPE} -z -C --file=ipynb_icon_512x512.svg --export-png=ipynb_icon_128x128_uncrush.png -w 128 -h 128
${INKSCAPE} -z -C --file=ipynb_icon_512x512.svg --export-png=ipynb_icon_256x256_uncrush.png -w 256 -h 256
${INKSCAPE} -z -C --file=ipynb_icon_512x512.svg --export-png=ipynb_icon_512x512_uncrush.png -w 512 -h 512
for file in `ls *_uncrush.png`; do
pngcrush -brute -l 9 -reduce -rem alla -rem text -rem time -rem gAMA -rem cHRM -rem iCCP -rem sRGB $file `basename $file _uncrush.png`.png
rm $file
done

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.91 r13725"
sodipodi:docname="ipynb_icon_16x16.svg"
width="16"
height="16"
inkscape:export-filename="/Users/bussonniermatthias/dev/ipython/docs/resources/ipynb_icon_16x16.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata371">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
inkscape:window-height="755"
inkscape:window-width="1343"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
inkscape:zoom="32.924658"
inkscape:cx="4.028552"
inkscape:cy="6.0286893"
inkscape:window-x="67"
inkscape:window-y="65"
inkscape:current-layer="text4040"
width="210mm"
height="40mm"
units="px"
showgrid="true"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid3926"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient4689">
<stop
id="stop4157"
offset="0"
style="stop-color:#5a9fd4;stop-opacity:1;" />
<stop
id="stop4159"
offset="1"
style="stop-color:#306998;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4161">
<stop
style="stop-color:#fab434;stop-opacity:1"
offset="0"
id="stop4691" />
<stop
style="stop-color:#fb9143;stop-opacity:1"
offset="1"
id="stop4693" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4161"
id="linearGradient3941"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.07938588,0,0,0.07938588,-5.0860229,1.6938337)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4161"
id="linearGradient3943"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.06968992,0,0,0.06968992,-2.9157072,3.1465468)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689"
id="linearGradient3945"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.06968992,0,0,0.06968992,-2.9157072,3.1465468)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
</defs>
<path
style="fill:#ffffff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 2,15 2,1 11,1 14,4 14,15 2,15"
id="path3956"
inkscape:connector-curvature="0" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7d7d7d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99363834;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m 1,0 0,16 14,0 0.03125,-12.4375 -3.6875,-3.53125 z m 9,1 0,4 4,0 0,10 L 2,15 2,1 z m 1,0 3,3 -3,0 z"
id="path4338"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccc" />
<g
style="font-size:9.22902393px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3945);fill-opacity:1;stroke:none;font-family:Monospace;-inkscape-font-specification:Monospace"
id="text4040"
transform="translate(0,-1)">
<path
d="M 6.3936117,14 4,7.4375 4,14 3,14 3,6 4.4316382,6 7,13 7,6 8,6 8,14"
style="fill:url(#linearGradient3943);font-family:Droid Sans;-inkscape-font-specification:Droid Sans;fill-opacity:1.0"
id="path3937"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccc" />
<path
d="m 11.611865,8.3182664 c 0.321685,5.7e-6 0.614285,0.061605 0.8778,0.1847998 0.263506,0.1232054 0.487661,0.3080051 0.672466,0.5543996 0.188217,0.2429824 0.333661,0.5458484 0.436333,0.9085998 0.102658,0.3627584 0.153994,0.7836904 0.154,1.2627984 -6e-6,0.482536 -0.05134,0.90689 -0.154,1.273066 -0.10267,0.362757 -0.248116,0.667334 -0.436333,0.913732 -0.184805,0.246401 -0.40896,0.432912 -0.672466,0.559533 -0.263515,0.1232 -0.556115,0.1848 -0.8778,0.1848 -0.201914,0 -0.385003,-0.02225 -0.549265,-0.06673 -0.16427,-0.04449 -0.313137,-0.10267 -0.446601,-0.174534 -0.130046,-0.07528 -0.246402,-0.162555 -0.349066,-0.2618 -0.09924,-0.09924 -0.188224,-0.203622 -0.2669332,-0.313133 L 10,13.454283 10,14 9,14 9,6 l 1,0 -4e-6,3.1601324 c 0.078718,-0.1197727 0.167691,-0.2309947 0.266937,-0.3336664 0.09924,-0.1026607 0.213887,-0.1916389 0.343933,-0.2669332 0.133464,-0.075283 0.28233,-0.1334609 0.4466,-0.1745332 0.164263,-0.044483 0.349063,-0.066728 0.554399,-0.066734 M 11.4322,9.0933982 c -0.273781,5.1e-6 -0.50307,0.044493 -0.687868,0.1334667 -0.181379,0.08556 -0.328535,0.2173155 -0.441466,0.3952668 -0.109516,0.1779595 -0.188223,0.4004033 -0.236133,0.6673323 -0.04449,0.266937 -0.06673,0.58007 -0.06673,0.9394 -10e-7,0.345646 0.02225,0.653646 0.06673,0.923999 0.04791,0.266935 0.12662,0.4928 0.236133,0.677599 0.112933,0.181379 0.261798,0.319979 0.446601,0.4158 0.184796,0.09239 0.415796,0.138601 0.692998,0.1386 0.461996,10e-7 0.800796,-0.18651 1.016399,-0.559533 0.219019,-0.37302 0.328529,-0.908597 0.328534,-1.606732 -5e-6,-0.711818 -0.109516,-1.2439729 -0.328534,-1.5964653 C 12.243261,9.2696478 11.901039,9.0934037 11.4322,9.0933986"
style="fill:url(#linearGradient3941);font-family:Droid Sans;-inkscape-font-specification:Droid Sans;fill-opacity:1.0"
id="path3939"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccsccccccccccccccccccscccscccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.91 r13725"
sodipodi:docname="ipynb_icon_24x24.svg"
width="24"
height="24"
inkscape:export-filename="/Users/bussonniermatthias/dev/ipython/docs/resources/ipynb_icon_24x24.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata371">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
inkscape:window-height="677"
inkscape:window-width="1280"
inkscape:pageshadow="2"
inkscape:pageopacity="0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#d9d9d9"
id="base"
inkscape:zoom="22.627417"
inkscape:cx="14.369924"
inkscape:cy="14.833103"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:current-layer="svg2"
width="210mm"
height="40mm"
units="px"
showgrid="true"
inkscape:window-maximized="0"
inkscape:snap-grids="true"
inkscape:snap-global="true"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid3926"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient4167">
<stop
id="stop4169"
offset="0"
style="stop-color:#fab434;stop-opacity:1" />
<stop
id="stop4171"
offset="1"
style="stop-color:#fb9143;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient4689-7">
<stop
style="stop-color:#5a9fd4;stop-opacity:1;"
offset="0"
id="stop4691-6" />
<stop
style="stop-color:#306998;stop-opacity:1;"
offset="1"
id="stop4693-4" />
</linearGradient>
<linearGradient
id="linearGradient4689-3">
<stop
style="stop-color:#5a9fd4;stop-opacity:1;"
offset="0"
id="stop4691-8" />
<stop
style="stop-color:#306998;stop-opacity:1;"
offset="1"
id="stop4693-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4167"
id="linearGradient3382"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.10331077,0,0,0.10331077,-4.3899917,5.891698)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4167"
id="linearGradient3385"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.0933059,0,0,0.0933059,-2.6120135,7.4686107)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
</defs>
<path
style="fill:#ffffff;stroke:none"
d="M 3,23 3,1 19,1 22,4 22,23 3,23"
id="path3956"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#7d7d7d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99363834;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="M 2,0 2,24 23,24 23.03125,3.5625 19.34375,0.03125 2,0 z m 16,1 0,4 4,0 0,18 L 3,23 3,1 18,1 z m 1,0 3,3 -3,0 0,-3 z"
id="path4338"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccc" />
<path
style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd"
d="m 9.0631018,3.9984663 0,3.9686017 -1.0252408,0 0.00592,-1.0012742 -1.0164801,-0.010113 0,2.0113872 3.0000001,0 0,-4.9686017 z"
id="_92110424"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" />
<path
d="m 11.019015,6.0000004 0,4.9999996 1,0 0,-2 2,0 0,-2.9999996 -3,0 z m 1,1 1,0 0,1 -1,0 0,-1 z"
class="fil1"
id="_92100232"
inkscape:connector-curvature="0"
style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd"
sodipodi:nodetypes="cccccccccccc" />
<path
sodipodi:nodetypes="cccccccccccc"
id="path3329"
style="font-size:12.35648537px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3385);fill-opacity:1.0;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
d="m 5.969491,13 0.9967434,0.990704 4.0032566,6.537137 0,-7.527841 L 12,12.9921 12,22.000026 10.969491,22 9.9775096,21.079451 5.969491,14.435247 l 0,7.564753 -1,0 0,-9" />
<path
sodipodi:nodetypes="csssssssccssssssscccccc"
id="path3331"
style="font-size:12.35648537px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3382);fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
d="m 19.387284,18.246903 c -7e-6,-0.904074 -0.187059,-1.612194 -0.561153,-2.124362 -0.369654,-0.51661 -0.879589,-0.774918 -1.529808,-0.774925 -0.650228,7e-6 -1.162391,0.258315 -1.536489,0.774925 -0.369651,0.512168 -0.554474,1.220288 -0.554472,2.124362 -2e-6,0.904082 0.184821,1.614428 0.554472,2.131043 0.374098,0.512164 0.886261,0.768246 1.536489,0.768244 0.650219,2e-6 1.160154,-0.25608 1.529808,-0.768244 0.374094,-0.516615 0.561146,-1.226961 0.561153,-2.131043 M 14.969491,16 c 0.258306,-0.578575 0.819288,-1.140045 1.211207,-1.353824 0.396366,-0.218219 0.868446,-0.327332 1.416242,-0.32734 0.908526,8e-6 1.645595,0.360749 2.211207,1.082222 0.570052,0.721488 0.855082,1.670102 0.85509,2.845845 -8e-6,1.175751 -0.285038,2.124364 -0.85509,2.845844 -0.565612,0.721482 -1.302681,1.082223 -2.211207,1.082223 -0.547796,0 -1.019876,-0.106881 -1.416242,-0.320658 C 15.788779,21.636086 15.227797,21.44536 14.969491,21 l 0,1 -1,0 0,-10 1,0 0,3" />
<path
style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd"
d="m 15.016466,5.983534 0,2.0072866 0,0.9927134 1,0 1,0 0,1 -2,0 0,1 1,0 1,0 1,0 0,-1 0,-1 0,-1 0,-1 0,-1 -1,0 0,2 -1,0 0,-2 -1,0 z m 2,5 0,0 z"
id="path3433"
sodipodi:nodetypes="ccccccccccccccccccccccc"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

@ -0,0 +1,311 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.91 r13725"
sodipodi:docname="icon_32x32.svg"
width="32"
height="32"
inkscape:export-filename="/Users/bussonniermatthias/dev/ipython/docs/resources/ipynb_icon_32x32@2x.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180">
<metadata
id="metadata371">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
inkscape:window-height="677"
inkscape:window-width="1280"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
inkscape:zoom="16"
inkscape:cx="1.844338"
inkscape:cy="15.013138"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:current-layer="svg2"
width="210mm"
height="40mm"
units="mm"
showgrid="true"
inkscape:window-maximized="0"
inkscape:snap-global="false">
<inkscape:grid
type="xygrid"
id="grid2928"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient4689">
<stop
style="stop-color:#5a9fd4;stop-opacity:1;"
offset="0"
id="stop4691" />
<stop
style="stop-color:#306998;stop-opacity:1;"
offset="1"
id="stop4693" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689"
id="linearGradient4355"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.94210934,0,0,0.94210934,1.2341389,2.7751101)"
x1="12.796725"
y1="13.227233"
x2="27.51895"
y2="31.016586" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689"
id="linearGradient4357"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.94210934,0,0,0.94210934,1.2341389,2.7751101)"
x1="12.796725"
y1="13.227233"
x2="27.51895"
y2="31.016586" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689"
id="linearGradient3720"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.04569196,0,0,0.04569196,1.6101549,-10.796096)"
x1="187.33029"
y1="618.22144"
x2="393.25586"
y2="867.04816" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3266"
gradientUnits="userSpaceOnUse"
x1="323.06018"
y1="147.10051"
x2="147.68851"
y2="293.00339" />
<linearGradient
id="linearGradient4689-6">
<stop
style="stop-color:#5a9fd4;stop-opacity:1"
offset="0"
id="stop4691-3" />
<stop
style="stop-color:#306998;stop-opacity:1"
offset="1"
id="stop4693-8" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3256"
gradientUnits="userSpaceOnUse"
x1="486.50031"
y1="184.54053"
x2="496.16876"
y2="248.36336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3254"
gradientUnits="userSpaceOnUse"
x1="486.50031"
y1="184.54053"
x2="496.16876"
y2="248.36336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3260"
gradientUnits="userSpaceOnUse"
x1="485.7803"
y1="185.98055"
x2="496.88876"
y2="249.08336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3258"
gradientUnits="userSpaceOnUse"
x1="485.7803"
y1="185.98055"
x2="496.88876"
y2="249.08336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3264"
gradientUnits="userSpaceOnUse"
x1="484.3403"
y1="182.38054"
x2="495.44876"
y2="243.32335" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3262"
gradientUnits="userSpaceOnUse"
x1="484.3403"
y1="182.38054"
x2="495.44876"
y2="243.32335" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3174"
gradientUnits="userSpaceOnUse"
x1="486.50031"
y1="184.54053"
x2="496.16876"
y2="248.36336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3176"
gradientUnits="userSpaceOnUse"
x1="486.50031"
y1="184.54053"
x2="496.16876"
y2="248.36336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3178"
gradientUnits="userSpaceOnUse"
x1="485.7803"
y1="185.98055"
x2="496.88876"
y2="249.08336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3180"
gradientUnits="userSpaceOnUse"
x1="485.7803"
y1="185.98055"
x2="496.88876"
y2="249.08336" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3182"
gradientUnits="userSpaceOnUse"
x1="484.3403"
y1="182.38054"
x2="495.44876"
y2="243.32335" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3184"
gradientUnits="userSpaceOnUse"
x1="484.3403"
y1="182.38054"
x2="495.44876"
y2="243.32335" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4689-6"
id="linearGradient3186"
gradientUnits="userSpaceOnUse"
x1="323.06018"
y1="147.10051"
x2="147.68851"
y2="293.00339" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4328"
id="linearGradient3314"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.14130513,0,0,0.14130513,-7.8428899,8.0251755)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
<linearGradient
id="linearGradient4328">
<stop
id="stop4330"
offset="0"
style="stop-color:#fab434;stop-opacity:1" />
<stop
id="stop4332"
offset="1"
style="stop-color:#fb9143;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4328"
id="linearGradient3316"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.14130513,0,0,0.14130513,-5.8855851,8.0251755)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
</defs>
<path
style="fill:#ffffff;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 4,31 4,1 21.957779,1.0144872 28,7 28,31 4,31 z"
id="path2939"
sodipodi:nodetypes="cccccc" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#000000;fill:#7d7d7d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99363834;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="M 3,0 3,32 29,32 29,6.5881566 22.494212,0 3,0 z m 18,1 0,7 7,0 0,23 L 4,31 4,1 21,1 z m 1,0 6,6 -6,0 0,-6 z"
id="path4338-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccccc" />
<path
inkscape:connector-curvature="0"
id="path3277"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:267.69692993px;line-height:125%;font-family:'Droid Sans';-inkscape-font-specification:'Droid Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3316);fill-opacity:1;stroke:#000000;stroke-width:0.08421666;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 5.9182142,16.390066 2.4853226,0 6.0488362,11.412381 0,-11.412381 1.790894,0 0,13.641861 -2.485322,0 -6.0488362,-11.412381 0,11.412381 -1.7908946,0 0,-13.641861" />
<path
inkscape:connector-curvature="0"
id="path3279"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:267.69692993px;line-height:125%;font-family:'Droid Sans';-inkscape-font-specification:'Droid Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3314);fill-opacity:1;stroke:#000000;stroke-width:0.08421666;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 25.232346,24.924225 c -9e-6,-1.236564 -0.255851,-2.205107 -0.767526,-2.905635 -0.505601,-0.706602 -1.203074,-1.059908 -2.092423,-1.059917 -0.889361,9e-6 -1.589879,0.353315 -2.101559,1.059917 -0.505597,0.700528 -0.758392,1.669071 -0.758389,2.905635 -3e-6,1.236573 0.252792,2.208163 0.758389,2.914771 0.51168,0.700521 1.212198,1.050781 2.101559,1.05078 0.889349,10e-7 1.586822,-0.350259 2.092423,-1.05078 0.511675,-0.706608 0.767517,-1.678198 0.767526,-2.914771 m -5.719897,-3.572652 c 0.353302,-0.609138 0.797979,-1.059907 1.334033,-1.352307 0.542137,-0.298472 1.187833,-0.447713 1.93709,-0.447724 1.242653,1.1e-5 2.250792,0.49342 3.024418,1.480229 0.779699,0.986827 1.169553,2.284311 1.169564,3.892454 -1.1e-5,1.608153 -0.389865,2.905636 -1.169564,3.892453 -0.773626,0.98682 -1.781765,1.480229 -3.024418,1.480229 -0.749257,0 -1.394953,-0.146195 -1.93709,-0.438586 -0.536054,-0.298482 -0.980731,-0.752296 -1.334033,-1.361445 l 0,1.535051 -1.690385,0 0,-14.217506 1.690385,0 0,5.537152" />
<text
sodipodi:linespacing="125%"
id="text4334"
y="13.263572"
x="5.858809"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.92461967px;line-height:125%;font-family:'Myriad Pro';-inkscape-font-specification:'Myriad Pro';letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="13.263572"
x="5.858809"
id="tspan4336"
sodipodi:role="line">Jupyter</tspan></text>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.91 r13725"
sodipodi:docname="icon_512x512.svg"
width="512"
height="512"
inkscape:export-filename="/Users/bussonniermatthias/dev/ipython/docs/resources/icon_1024x1024.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180">
<metadata
id="metadata371">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
inkscape:window-height="855"
inkscape:window-width="1440"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
inkscape:zoom="1.0730821"
inkscape:cx="-301.17043"
inkscape:cy="112.5086"
inkscape:window-x="-7"
inkscape:window-y="6"
inkscape:current-layer="svg2"
width="210mm"
height="40mm"
units="mm"
showgrid="false"
inkscape:window-maximized="0"
inkscape:snap-global="false">
<inkscape:grid
type="xygrid"
id="grid2928"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
</sodipodi:namedview>
<defs
id="defs4">
<linearGradient
id="linearGradient4328">
<stop
id="stop4330"
offset="0"
style="stop-color:#fab434;stop-opacity:1" />
<stop
id="stop4332"
offset="1"
style="stop-color:#fb9143;stop-opacity:1" />
</linearGradient>
<linearGradient
id="linearGradient3919">
<stop
style="stop-color:#e4e4e4;stop-opacity:1;"
offset="0"
id="stop3921" />
<stop
id="stop3927"
offset="1"
style="stop-color:#ffffff;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4689">
<stop
style="stop-color:#5a9fd4;stop-opacity:1;"
offset="0"
id="stop4691" />
<stop
style="stop-color:#306998;stop-opacity:1;"
offset="1"
id="stop4693" />
</linearGradient>
<filter
inkscape:collect="always"
id="filter3871"
color-interpolation-filters="sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="2.9338986"
id="feGaussianBlur3873" />
</filter>
<filter
inkscape:collect="always"
id="filter3913"
color-interpolation-filters="sRGB">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.75310748"
id="feGaussianBlur3915" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3919"
id="linearGradient3925"
x1="209.00497"
y1="-22.631527"
x2="200.9668"
y2="-2.0393581"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.2092365,0,0,2.2092365,-68.579768,91.189025)" />
<linearGradient
id="linearGradient4689-6">
<stop
style="stop-color:#5a9fd4;stop-opacity:1"
offset="0"
id="stop4691-3" />
<stop
style="stop-color:#306998;stop-opacity:1"
offset="1"
id="stop4693-8" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4328"
id="linearGradient3314"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.181239,0,0,2.181239,-111.61182,113.06547)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4328"
id="linearGradient3316"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.181239,0,0,2.181239,-81.398128,113.06547)"
x1="116.74316"
y1="62.91114"
x2="190.06432"
y2="149.74373" />
</defs>
<path
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="/Users/matthiasbussonnier/Desktop/ipython-python.png"
style="fill:#666666;fill-opacity:0.99033813;stroke:#7d7d7d;stroke-width:1.08476496;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;filter:url(#filter3871)"
d="m 20,279.27442 88.35063,0 c 33.9881,0 77.49135,12.81451 77.49135,29.1526 L 185.84198,492 20,492 Z"
id="path3077"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc"
transform="matrix(2.2092365,0,0,2.2092365,35.81527,-594.94434)" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path4338"
d="m 79.999519,21.999517 194.983301,0 c 61.89823,0 171.01766,39.636924 171.01766,64.410449 l 0,405.590514 -366.000961,0 z"
style="fill:#ffffff;fill-opacity:0.99033813;stroke:#7d7d7d;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
inkscape:export-filename="/Users/matthiasbussonnier/Desktop/ipython-python.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path3875"
d="m 172.44062,-29.205599 c 12.43851,5.333061 24.39875,11.169395 29.07043,28.60434895 8.5133,-4.59641195 30.96516,-11.88953595 31.36664,-1.19791745"
style="fill:#ffffff;stroke:#7d7d7d;stroke-width:1.08476496;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3913)"
transform="matrix(2.2092365,0,0,2.2092365,-68.579768,91.189025)" />
<path
style="fill:url(#linearGradient3925);fill-opacity:1;stroke:#7d7d7d;stroke-width:2;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 274.98635,21.999991 c 32.90208,0 87.16499,12.824171 101.61943,66.768899 18.80789,-10.154561 68.40936,-26.266797 69.29632,-2.646483 0,-40.095253 -127.94367,-64.122416 -170.91575,-64.122416 z"
id="path4384"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
inkscape:connector-curvature="0"
id="path3277"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:267.69692993px;line-height:125%;font-family:'Droid Sans';-inkscape-font-specification:'Droid Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3316);fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:1.3;stroke-miterlimit:4;stroke-dasharray:none"
d="m 100.80974,242.18906 38.36437,0 93.3721,176.16579 0,-176.16579 27.64492,0 0,210.58088 -38.36437,0 -93.3721,-176.16579 0,176.16579 -27.64492,0 0,-210.58088" />
<path
inkscape:connector-curvature="0"
id="path3279"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:267.69692993px;line-height:125%;font-family:'Droid Sans';-inkscape-font-specification:'Droid Sans';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient3314);fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:1.3;stroke-miterlimit:4;stroke-dasharray:none"
d="m 398.94991,373.92553 c -1.4e-4,-19.08806 -3.94941,-34.03886 -11.84782,-44.85247 -7.80464,-10.90738 -18.57109,-16.36113 -32.29942,-16.36127 -13.7285,1.4e-4 -24.54197,5.45389 -32.44045,16.36127 -7.80458,10.81361 -11.70683,25.76441 -11.70677,44.85247 -6e-5,19.08821 3.90219,34.08603 11.70677,44.9935 7.89848,10.81351 18.71195,16.22025 32.44045,16.22023 13.72833,2e-5 24.49478,-5.40672 32.29942,-16.22023 7.89841,-10.90747 11.84768,-25.90529 11.84782,-44.9935 m -88.29446,-55.14879 c 5.4537,-9.40288 12.3179,-16.36112 20.59263,-20.87472 8.36862,-4.60733 18.33583,-6.91107 29.90165,-6.91123 19.18205,1.6e-4 34.74406,7.61661 46.68605,22.84936 12.03572,15.23304 18.05365,35.26148 18.05382,60.08538 -1.7e-4,24.82405 -6.0181,44.85249 -18.05382,60.08537 -11.94199,15.23291 -27.504,22.84936 -46.68605,22.84936 -11.56582,0 -21.53303,-2.25672 -29.90165,-6.77017 -8.27473,-4.60748 -15.13893,-11.61273 -20.59263,-21.01578 l 0,23.69563 -26.09342,0 0,-219.46675 26.09342,0 0,85.47355" />
<text
sodipodi:linespacing="125%"
id="text4334"
y="164.01935"
x="99.892746"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:106.89102173px;line-height:125%;font-family:'Myriad Pro';-inkscape-font-specification:'Myriad Pro';letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
xml:space="preserve"><tspan
y="164.01935"
x="99.892746"
id="tspan4336"
sodipodi:role="line">Jupyter</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-919.78046"
y="540.88873"
id="text4228"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4230"
x="-919.78046"
y="540.88873" /></text>
</svg>

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save