diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bc0933ccf..7c44b8517 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,58 +1,53 @@ { - "name": "Jupyter UI", - "build": { - "dockerfile": "Dockerfile", - "context": "../", - "target": "dev" - }, - "remoteUser": "mambauser", - "postStartCommand": "yarn install", - "hostRequirements": { - "memory": "2gb", - "cpus": 2 - }, - "portsAttributes": { - "8686": { - "label": "Jupyter Server", - "onAutoForward": "notify" - }, - "3208": { - "label": "Webpack Application", - "onAutoForward": "notify" - }, - "3000": { - "label": "CRA, Docusaurus, Next.js... ", - "onAutoForward": "notify" - } - }, - "customizations": { - "vscode": { - "settings": { - "terminal.integrated.profiles.linux": { - "bash": { - "path": "/bin/bash" - } - }, - "python.defaultInterpreterPath": "/usr/local/bin/python", - "python.languageServer": "Default", - "pylinting.enabled": true, - "pylinting.pylintEnabled": true, - "autopep8.path": [ - "/usr/local/py-utils/bin/autopep8" - ], - "autopep8.blackPath": "/usr/local/py-utils/bin/black", - "autopep8.yapfPath": "/usr/local/py-utils/bin/yapf", - "pylinting.banditPath": "/usr/local/py-utils/bin/bandit", - "pylinting.flake8Path": "/usr/local/py-utils/bin/flake8", - "pylinting.mypyPath": "/usr/local/py-utils/bin/mypy", - "pylinting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", - "pylinting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", - "pylinting.pylintPath": "/usr/local/py-utils/bin/pylint" - }, - "extensions": [ - "ms-python.python", - "ms-python.vscode-pylance" - ] - } - } + "name": "Jupyter UI", + "build": { + "dockerfile": "Dockerfile", + "context": "../", + "target": "dev" + }, + "remoteUser": "mambauser", + "postStartCommand": "yarn install", + "hostRequirements": { + "memory": "2gb", + "cpus": 2 + }, + "portsAttributes": { + "8686": { + "label": "Jupyter Server", + "onAutoForward": "notify" + }, + "3208": { + "label": "Webpack Application", + "onAutoForward": "notify" + }, + "3000": { + "label": "CRA, Docusaurus, Next.js... ", + "onAutoForward": "notify" + } + }, + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.profiles.linux": { + "bash": { + "path": "/bin/bash" + } + }, + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.languageServer": "Default", + "pylinting.enabled": true, + "pylinting.pylintEnabled": true, + "autopep8.path": ["/usr/local/py-utils/bin/autopep8"], + "autopep8.blackPath": "/usr/local/py-utils/bin/black", + "autopep8.yapfPath": "/usr/local/py-utils/bin/yapf", + "pylinting.banditPath": "/usr/local/py-utils/bin/bandit", + "pylinting.flake8Path": "/usr/local/py-utils/bin/flake8", + "pylinting.mypyPath": "/usr/local/py-utils/bin/mypy", + "pylinting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "pylinting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "pylinting.pylintPath": "/usr/local/py-utils/bin/pylint" + }, + "extensions": ["ms-python.python", "ms-python.vscode-pylance"] + } + } } diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..a40d01822 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,54 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# TypeScript, JavaScript, JSX, TSX files +[*.{ts,tsx,js,jsx}] +indent_style = space +indent_size = 2 + +# JSON files +[*.json] +indent_style = space +indent_size = 2 + +# YAML files +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +# Markdown files +[*.{md,mdx}] +trim_trailing_whitespace = false + +# Python files +[*.py] +indent_style = space +indent_size = 4 + +# Makefile +[Makefile] +indent_style = tab + +# Package files +[package.json] +indent_style = space +indent_size = 2 + +# CSS files +[*.css] +indent_style = space +indent_size = 2 + +# HTML files +[*.html] +indent_style = space +indent_size = 2 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..1fcc7711d --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + env: { + browser: true, + es2020: true, + node: true, + jest: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'plugin:prettier/recommended', // This must be last + ], + plugins: ['@typescript-eslint', 'react', 'react-hooks', 'prettier'], + rules: { + // TypeScript specific rules + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-non-null-assertion': 'warn', + + // React specific rules + 'react/react-in-jsx-scope': 'off', // Not needed with React 17+ + 'react/prop-types': 'off', // We use TypeScript for type checking + 'react/display-name': 'off', + 'react/no-unescaped-entities': 'warn', + + // General rules + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'no-debugger': 'warn', + 'prefer-const': 'warn', + 'no-var': 'error', + + // Prettier integration + 'prettier/prettier': 'warn', + }, + overrides: [ + { + // Test files + files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'], + env: { + jest: true, + }, + }, + { + // JavaScript files + files: ['**/*.js', '**/*.jsx'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + }, + }, + { + // Storybook files + files: ['**/*.stories.tsx', '**/*.stories.ts'], + rules: { + 'import/no-anonymous-default-export': 'off', + }, + }, + ], + ignorePatterns: [ + 'node_modules/', + 'dist/', + 'build/', + 'lib/', + '*.min.js', + 'coverage/', + '.next/', + 'storybook-static/', + ], +}; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 19f4b97ec..8affb14f1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,144 +19,55 @@ jobs: matrix: os: [ubuntu-latest] # os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [3.11] - defaults: - run: - shell: bash -l {0} steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup conda - uses: conda-incubator/setup-miniconda@v3 - with: - mamba-version: "*" - channels: conda-forge,defaults - channel-priority: true - environment-file: environment.yml - activate-environment: datalayer - - name: Setup npm cache - uses: actions/cache@v4 - with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json', '**/package.json') }} - restore-keys: | - ${{ runner.os }}-npm- - - name: Install the npm dependencies - run: | - npm install - - name: Run the build - run: | - npm run build - - visual-test: - runs-on: ubuntu-latest - needs: build - name: Stories visual tests - timeout-minutes: 60 - env: - PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/pw-browsers - - steps: - - name: Checkout ๐Ÿ›Ž๏ธ + - name: Checkout uses: actions/checkout@v4 - - - name: Setup Node ๐Ÿ’พ + - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '18' - - + node-version: '20' + - name: Install the npm dependencies run: | - corepack enable - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT - - name: Setup yarn cache - uses: actions/cache@v4 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - yarn- - - - name: Install Dependencies ๐Ÿ“ฅ - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - run: yarn install - - - name: Set up browser cache - uses: actions/cache@v4 - with: - path: | - ${{ github.workspace }}/pw-browsers - key: ${{ runner.os }}-${{ hashFiles('ui-tests/yarn.lock') }} - - - name: Install Playwright Browsers - run: yarn run playwright install chromium --with-deps - working-directory: packages/react - - - name: Run Visual Tests ๐Ÿงช - run: yarn run test:visual - working-directory: packages/react - - - name: Update Snapshots - if: ${{ failure() }} + npm install + - name: Run the build run: | - mv test-results test-assets - yarn run test:visual -u - working-directory: packages/react - - - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: jupyter-react-test - path: | - packages/react/test-assets/ - packages/react/stories/**/*-snapshots/* + npm run build storybook-test: name: Test Storybook Stories and Docs runs-on: ubuntu-latest timeout-minutes: 30 - + steps: - name: Checkout uses: actions/checkout@v4 - + - name: Setup Node uses: actions/setup-node@v4 with: - node-version: '18' - - - name: Setup npm cache - uses: actions/cache@v4 - with: - path: ~/.npm - key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json', '**/package.json') }} - restore-keys: | - npm-${{ runner.os }}- - + node-version: '20' + - name: Install Dependencies run: npm install - + - name: Build all packages run: npm run build - + - name: Install Playwright run: npx playwright install chromium --with-deps working-directory: storybook - + - name: Build Storybook run: npm run build:storybook working-directory: storybook - + - name: Serve Storybook and run all tests run: | npx serve -l 6006 storybook-static & - sleep 5 - npm run test:all + npx wait-on http://localhost:6006 --timeout 30000 + npm run test:all:ci working-directory: storybook - + - name: Upload test results if: failure() uses: actions/upload-artifact@v4 @@ -165,53 +76,3 @@ jobs: path: | storybook/test-results/ storybook/html-report/ - - docker-dev: - # Don't run if the previous case fails - needs: build - - name: Build on dev container - runs-on: ubuntu-latest - - permissions: - contents: read - packages: write - - env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - - steps: - - - name: Checkout - uses: actions/checkout@v4 - - - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - file: ".devcontainer/Dockerfile" - target: "builder" - # For now never push - # push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 000000000..8688595f6 --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,53 @@ +name: Code Quality + +on: + push: + branches: ['main'] + pull_request: + branches: ['*'] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + code-quality: + name: Code Quality Checks + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Run Lint Checks + run: | + echo "::group::Running Lint Checks" + echo "Running ESLint..." + npm run lint + echo "โœ“ ESLint passed" + echo "" + echo "::endgroup::" + + - name: Run Format Checks + run: | + echo "::group::Running Format Checks" + npm run format:check + echo "โœ“ Prettier passed" + echo "" + echo "::endgroup::" + - name: Build project + run: npm run build + - name: Run Type Checks + run: | + echo "::group::Running Type Checks" + npm run type-check + echo "โœ“ TypeScript passed" + echo "::endgroup::" diff --git a/.github/workflows/docker.yml.disabled b/.github/workflows/docker.yml.disabled new file mode 100644 index 000000000..01ae7f77e --- /dev/null +++ b/.github/workflows/docker.yml.disabled @@ -0,0 +1,77 @@ +name: Build + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Run build on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + # os: [ubuntu-latest, windows-latest, macos-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + - name: Install the npm dependencies + run: | + npm install + - name: Run the build + run: | + npm run build + + docker-dev: + # Don't run if the previous case fails + needs: build + + name: Build on dev container + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ".devcontainer/Dockerfile" + target: "builder" + # For now never push + # push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/fix-license-header.yml b/.github/workflows/fix-license-header.yml index 10386f6be..a78c66a84 100644 --- a/.github/workflows/fix-license-header.yml +++ b/.github/workflows/fix-license-header.yml @@ -6,7 +6,7 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true - + jobs: header-license-fix: runs-on: ubuntu-latest diff --git a/.github/workflows/publish-dev.yml b/.github/workflows/publish-dev.yml index c2e57b4a8..69024d6e2 100644 --- a/.github/workflows/publish-dev.yml +++ b/.github/workflows/publish-dev.yml @@ -2,16 +2,16 @@ name: Publish package to GitHub Packages on: push: branches: - - main + - main release: types: [published] # FIXME workflow_dispatch should not be needed - workflow_dispatch: + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true - + jobs: publish: # Forbid manual execution on non-main branches @@ -23,19 +23,16 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - - uses: actions/checkout@v4 - - - # Setup .npmrc file to publish to GitHub Packages + - uses: actions/checkout@v4 + - # Setup .npmrc file to publish to GitHub Packages uses: actions/setup-node@v4 with: node-version: '20.x' registry-url: 'https://npm.pkg.github.com' # Defaults to the user or organization that owns the workflow file scope: '@datalayer' - - - run: | - corepack enable + - run: | + corepack enable - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -48,30 +45,26 @@ jobs: key: ${{ runner.os }}-yarn-${{ hashFiles('**/package.json') }} restore-keys: | ${{ runner.os }}-yarn- - - - - run: | - yarn install - yarn run build - - - if: github.event_name == 'push' + + - run: | + yarn install + yarn run build + - if: github.event_name == 'push' # Bump the version run: | - npm version --preid dev --no-git-tag-version prerelease \ - --workspace ./packages/react + npm version --preid dev --no-git-tag-version prerelease \ + --workspace ./packages/react - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git commit -a -m "Bump dev version" - - - run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git commit -a -m "Bump dev version" + - run: | npm publish --access public --tag next \ --workspace ./packages/react env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - if: github.event_name == 'push' + - if: github.event_name == 'push' # Push version bump run: | git push @@ -84,4 +77,4 @@ jobs: package-name: 'jupyter-react' package-type: 'npm' min-versions-to-keep: 3 - delete-only-pre-release-versions: "true" + delete-only-pre-release-versions: 'true' diff --git a/.gitignore b/.gitignore index 57cd2a6b6..cd1544448 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ lib *.sqlite *.tsbuildinfo *.egg-info -*-lock.json *-error.log *.log *.lock @@ -178,3 +177,4 @@ packages/react/.vscode/launch.json !dev/notebooks/.datalayer !docs/static/img !**/static/README.md +*-lock.json diff --git a/.husky/_/.gitignore b/.husky/_/.gitignore new file mode 100644 index 000000000..f59ec20aa --- /dev/null +++ b/.husky/_/.gitignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/.husky/_/applypatch-msg b/.husky/_/applypatch-msg new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/applypatch-msg @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/commit-msg b/.husky/_/commit-msg new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/commit-msg @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/h b/.husky/_/h new file mode 100644 index 000000000..bf7c89640 --- /dev/null +++ b/.husky/_/h @@ -0,0 +1,22 @@ +#!/usr/bin/env sh +[ "$HUSKY" = "2" ] && set -x +n=$(basename "$0") +s=$(dirname "$(dirname "$0")")/$n + +[ ! -f "$s" ] && exit 0 + +if [ -f "$HOME/.huskyrc" ]; then + echo "husky - '~/.huskyrc' is DEPRECATED, please move your code to ~/.config/husky/init.sh" +fi +i="${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh" +[ -f "$i" ] && . "$i" + +[ "${HUSKY-}" = "0" ] && exit 0 + +export PATH="node_modules/.bin:$PATH" +sh -e "$s" "$@" +c=$? + +[ $c != 0 ] && echo "husky - $n script failed (code $c)" +[ $c = 127 ] && echo "husky - command not found in PATH=$PATH" +exit $c diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh new file mode 100644 index 000000000..f9d063790 --- /dev/null +++ b/.husky/_/husky.sh @@ -0,0 +1,9 @@ +echo "husky - DEPRECATED + +Please remove the following two lines from $0: + +#!/usr/bin/env sh +. \"\$(dirname -- \"\$0\")/_/husky.sh\" + +They WILL FAIL in v10.0.0 +" \ No newline at end of file diff --git a/.husky/_/post-applypatch b/.husky/_/post-applypatch new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/post-applypatch @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/post-checkout b/.husky/_/post-checkout new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/post-checkout @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/post-commit b/.husky/_/post-commit new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/post-commit @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/post-merge b/.husky/_/post-merge new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/post-merge @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/post-rewrite b/.husky/_/post-rewrite new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/post-rewrite @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/pre-applypatch b/.husky/_/pre-applypatch new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/pre-applypatch @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/pre-auto-gc b/.husky/_/pre-auto-gc new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/pre-auto-gc @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/pre-commit b/.husky/_/pre-commit new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/pre-commit @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/pre-merge-commit b/.husky/_/pre-merge-commit new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/pre-merge-commit @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/pre-push b/.husky/_/pre-push new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/pre-push @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/pre-rebase b/.husky/_/pre-rebase new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/pre-rebase @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/_/prepare-commit-msg b/.husky/_/prepare-commit-msg new file mode 100755 index 000000000..16aae78f5 --- /dev/null +++ b/.husky/_/prepare-commit-msg @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +. "$(dirname "$0")/h" \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..2312dc587 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.licenserc.yaml b/.licenserc.yaml index 84595521d..842054635 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -39,5 +39,22 @@ header: - 'packages/react/third-party-license.txt' - 'patches/' - '**/robots.txt' + - '.husky/_/applypatch-msg' + - '.husky/_/commit-msg' + - '.husky/_/h' + - '.husky/_/post-applypatch' + - '.husky/_/post-checkout' + - '.husky/_/post-commit' + - '.husky/_/post-merge' + - '.husky/_/post-rewrite' + - '.husky/_/pre-applypatch' + - '.husky/_/pre-auto-gc' + - '.husky/_/pre-commit' + - '.husky/_/pre-merge-commit' + - '.husky/_/pre-push' + - '.husky/_/pre-rebase' + - '.husky/_/prepare-commit-msg' + - '.husky/pre-commit' + - '**/*.disabled' - comment: on-failure \ No newline at end of file + comment: on-failure diff --git a/.lintstagedrc.js b/.lintstagedrc.js new file mode 100644 index 000000000..a8e658e24 --- /dev/null +++ b/.lintstagedrc.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ + +module.exports = { + // TypeScript and TSX files + '*.{ts,tsx}': ['eslint --fix', 'prettier --write'], + + // JavaScript and JSX files + '*.{js,jsx}': ['eslint --fix', 'prettier --write'], + + // JSON files + '*.json': ['prettier --write'], + + // Markdown and MDX files + '*.{md,mdx}': ['prettier --write'], + + // YAML files + '*.{yml,yaml}': ['prettier --write'], + + // CSS files + '*.css': ['prettier --write'], + + // HTML files + '*.html': ['prettier --write'], +}; diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..2edeafb09 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 22e4c841f..c6bdb5b7b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,59 @@ -node_modules -**/node_modules -**/dist -**/lib -**/package.json +# Attic folder - excluded from formatting +attic/ + +# Dependencies +node_modules/ +**/node_modules/ +.pnp +.pnp.js + +# Build outputs +dist/ +**/dist/ +build/ +lib/ +**/lib/ +*.min.js +*.bundle.js +coverage/ + +# Next.js +.next/ +out/ + +# Storybook +storybook-static/ + +# Cache +.cache/ +.turbo/ + +# Logs +*.log + +# Python +__pycache__/ +*.py[cod] + +# Jupyter +.ipynb_checkpoints/ +*.ipynb **/jupyter_react/ + +# Package files +package-lock.json +yarn.lock +pnpm-lock.yaml + +# Generated files +*.generated.* + +# Patches +patches/ + +# Data files +*.csv +*.xml + +# Markdown files with specific formatting +CHANGELOG.md diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..c952fe6e3 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,9 @@ +{ + "singleQuote": true, + "printWidth": 80, + "jsxSingleQuote": false, + "bracketSpacing": true, + "arrowParens": "avoid", + "trailingComma": "all", + "endOfLine": "lf" +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 20d1c8fbe..511fa423e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,3 @@ { - "recommendations": [ - "ms-toolsai.jupyter", - "ms-python.autopep8" - ] + "recommendations": ["ms-toolsai.jupyter", "ms-python.autopep8"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 8cda1d873..f198b1446 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,17 +1,49 @@ { - "[javascript]": { - "editor.defaultFormatter": "vscode.typescript-language-features", - }, - "[javascriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - }, - "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode", - }, - "githubPullRequests.ignoredPullRequestBranches": [ - "main" - ], -} \ No newline at end of file + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "typescript.tsdk": "node_modules/typescript/lib", + "files.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/build": true, + "**/lib": true, + "**/.next": true + }, + "search.exclude": { + "**/node_modules": true, + "**/dist": true, + "**/build": true, + "**/lib": true, + "**/.next": true, + "**/package-lock.json": true, + "**/yarn.lock": true + }, + "githubPullRequests.ignoredPullRequestBranches": ["main"] +} diff --git a/.yarnrc.yml b/.yarnrc.yml deleted file mode 100644 index 05fb81872..000000000 --- a/.yarnrc.yml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) Datalayer, Inc. https://datalayer.io -# Distributed under the terms of the MIT License. - -enableImmutableInstalls: false -enableInlineBuilds: false -enableTelemetry: false -httpTimeout: 60000 -nodeLinker: node-modules -npmRegistryServer: "https://registry.yarnpkg.com" - -# This will fix the build error with @lerna/legacy-package-management -# See https://github.com/lerna/repro/pull/11 -packageExtensions: - "@lerna/legacy-package-management@*": - dependencies: - "@lerna/child-process": "*" - "js-yaml": "*" - "rimraf": "*" - peerDependencies: - "nx": "*" diff --git a/CLAUDE.md b/CLAUDE.md index 25ae2f557..67c72afb0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,58 +1,66 @@ # Jupyter UI - AI Assistant Guide ## Quick Overview -React component library for building Jupyter-compatible applications. Monorepo with Lerna managing 4 main packages and multiple examples. + +React component library for Jupyter notebooks. Monorepo with 4 packages managed by Lerna. ## Core Packages -- `@datalayer/jupyter-react` - Main React components for notebooks, cells, terminals -- `@datalayer/jupyter-lexical` - Rich text editor with Lexical framework integration -- `@datalayer/jupyter-docusaurus-plugin` - Plugin for Docusaurus sites -- `datalayer-jupyter-vscode` - VS Code extension for notebooks -## Key Commands +- `@datalayer/jupyter-react` - React components for notebooks, cells, terminals +- `@datalayer/jupyter-lexical` - Rich text editor integration +- `@datalayer/jupyter-docusaurus-plugin` - Docusaurus plugin +- `datalayer-jupyter-vscode` - VS Code extension + +## Essential Commands + ```bash -# Install dependencies -npm install +npm install # Install dependencies +npm run build # Build all packages +npm run jupyter:server # Start Jupyter server (port 8686) +npm run storybook # Start Storybook (port 6006) +npm run lint # Check errors only (--quiet) +npm run lint:fix # Auto-fix issues +npm run format # Format code +npm run type-check # TypeScript checking +npm run check # Run all checks (format, lint, type) +npm run check:fix # Auto-fix and check all +npm test # Run tests +``` -# Build all packages -npm run build +## Requirements -# Start Jupyter server (required for development) -npm run jupyter:server +- Node.js >= 20.0.0 (use .nvmrc) +- npm (not yarn) +- Server token: `60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6` -# Run specific examples -npm run jupyter:ui:vite # Vite example -npm run jupyter:ui:nextjs # Next.js example -npm run jupyter:ui:lexical # Lexical editor example +## Key Files -# Run tests -npm test +- `eslint.config.js` - ESLint v9 flat config +- `.prettierrc.json` - Formatter config +- `.prettierignore` - Excludes MDX files +- `patches/` - Third-party fixes (auto-applied) +- `packages/react/webpack.config.js` - Build config -# Lint and format -npm run lint +## Recent Fixes (2024) -# Storybook -npm run storybook -``` +- MDX comments: `{/_` โ†’ `{/** **/}` in 13 files +- Node requirement: 18 โ†’ 20+ +- Webpack warnings: 7 โ†’ 2 (source-map exclusions) +- @jupyterlite patch for missing logos +- ESLint v9 flat config migration +- React 18 deprecations fixed +- Storybook CI: Added wait-on and --url for test reliability +- Terminal component: Fixed BoxPanel initialization issue + +## Common Issues + +1. **Storybook errors**: Check MDX syntax, run `npx patch-package` +2. **Node version**: Use Node 20+ (`nvm use`) +3. **Lint errors**: Run `npm run lint:fix` +4. **Build fails**: Run `npm run type-check` + +## AI Assistant Notes -## Development Setup -1. Requires Node.js >= 18.0.0 -2. Uses port 8686 for Jupyter server, 3208 for frontend -3. Server token: `60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6` - -## Architecture Notes -- Components wrap JupyterLab functionality in React declarative API -- Supports IPyWidgets, kernels, outputs, file browser -- Server communication via ServiceManager -- Uses Lumino widgets under the hood but exposes React interface - -## Common Tasks -- Adding components: Create in `packages/react/src/components/` -- Testing examples: Use `examples/` folders with various frameworks -- Documentation: Update in `docs/` (Docusaurus site) -- Storybook: Components showcased in `storybook/` - -## Important Files -- `lerna.json` - Monorepo configuration -- `dev/config/jupyter_server_config.py` - Server settings -- `packages/react/src/jupyter/JupyterContext.tsx` - Core context provider \ No newline at end of file +- Always use npm, not yarn +- Prefer editing over creating files +- Run lint/type checks before committing diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 789e5c455..1e18df206 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,56 +1,310 @@ -# How to contribute? +# Contributing to Jupyter UI -If you are reading this file, you have some ideas to improve this code; thanks a lot for that. +Thank you for your interest in contributing to Jupyter UI! We welcome all contributions: issue reporting, documentation improvements, bug fixes, or code enhancements. -Datalayer welcomes all contributions: issue reporting, documentation improvements, bug fixes -or code enhancements. +## ๐Ÿ“‹ Prerequisites -## Development +Before you begin, ensure you have the following installed: -We will focus here on the `packages/react` folder that is the corner stone of jupyter-ui; aka -providing Jupyter React components. +- **Node.js** >= 20.0.0 (preferably the LTS version) +- **npm** >= 8.0.0 +- **Python** >= 3.9 (for running Jupyter server) +- **Git** >= 2.0.0 -### Installation +## ๐Ÿš€ Development Setup -To install it in dev mode, you first must install: -- [node.js](https://nodejs.org) (preferably the LTS version) +This is a monorepo managed by Lerna with multiple packages. Here's how to get started: -Then execute the following commands in the `packages/react` folder: +### Initial Setup -```sh -# Install npm - https://yarnpkg.com/getting-started/install -corepack enable +```bash +# Clone the repository +git clone https://github.com/datalayer/jupyter-ui.git +cd jupyter-ui + +# Install dependencies for all packages npm install + +# Build all packages npm run build + +# Start the Jupyter server (required for development) +npm run jupyter:server +``` + +The Jupyter server will run on port 8686 with a predefined token for development. + +## ๐Ÿ“ฆ Project Structure + +``` +jupyter-ui/ +โ”œโ”€โ”€ packages/ # Core library packages +โ”‚ โ”œโ”€โ”€ react/ # Main React components (@datalayer/jupyter-react) +โ”‚ โ”œโ”€โ”€ lexical/ # Rich text editor (@datalayer/jupyter-lexical) +โ”‚ โ”œโ”€โ”€ docusaurus-plugin/# Docusaurus integration +โ”‚ โ””โ”€โ”€ vscode/ # VS Code extension +โ”œโ”€โ”€ examples/ # Example implementations +โ”‚ โ”œโ”€โ”€ vite/ # Vite example +โ”‚ โ”œโ”€โ”€ next-js/ # Next.js example +โ”‚ โ”œโ”€โ”€ docusaurus/ # Docusaurus example +โ”‚ โ””โ”€โ”€ lexical/ # Lexical editor example +โ”œโ”€โ”€ storybook/ # Component showcase and testing +โ”œโ”€โ”€ docs/ # Documentation site +โ””โ”€โ”€ dev/ # Development utilities ``` -### Code changes +## ๐Ÿ› ๏ธ Development Workflow + +### 1. Working with Components -The best way to test your changes is to use the storybook - a third-party tool allowing to -interact with an isolated component. -To start it, execute +The best way to test your changes is using Storybook: -```sh +```bash +# Start Storybook (runs on http://localhost:6006) npm run storybook ``` -It should open a web browser tab pointing to `http://localhost:6006` in which you will -be able to pick a story in the left sidebar. +Storybook provides: + +- Isolated component development +- Hot module replacement +- Interactive props testing +- Visual regression testing + +### 2. Running Examples + +Test your changes with different frameworks: -You can now change the code of the component and the storybook will get updated right -away when you save the source code. +```bash +# Vite example +npm run jupyter:ui:vite -**FAQ**: +# Next.js example +npm run jupyter:ui:nextjs + +# Lexical editor example +npm run jupyter:ui:lexical +``` + +### 3. Creating New Components + +When adding a new component: + +1. Create the component in `packages/react/src/components/` +2. Export it from the appropriate index file +3. Add a story in `storybook/src/stories/` +4. Add tests if applicable +5. Update documentation + +Example story creation: + +```tsx +// storybook/src/stories/YourComponent.stories.tsx +import type { Meta, StoryObj } from '@storybook/react'; +import { YourComponent } from '@datalayer/jupyter-react'; + +const meta: Meta = { + title: 'Components/YourComponent', + component: YourComponent, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + // your props + }, +}; +``` -- *No story available* for a component? - You can create a story for a component by adding a new file `*.stories.tsx` in the folder `packages/react/stories`. - Hint: start by copying the file `Console.stories.tsx` +## ๐Ÿ“ Code Quality Standards -### Linting the code +### ESLint & Prettier -The code follows some rules for format and prevent bad practices. -To check that and apply auto fixes, you must execute: +We use ESLint (v9 flat config) and Prettier for code quality: -```sh +```bash +# Check for linting issues (errors only) npm run lint + +# Fix auto-fixable issues +npm run lint:fix + +# Format code with Prettier +npm run format + +# Check formatting without fixing +npm run format:check +``` + +### TypeScript + +Type checking is enforced: + +```bash +# Run type checking +npm run type-check +``` + +### Combined Checks + +For convenience, you can run all checks at once: + +```bash +# Run all checks (format, lint, type-check) +npm run check + +# Run all checks with auto-fix where possible +npm run check:fix +``` + +### Pre-commit Hooks + +We use Husky and lint-staged for pre-commit checks: + +- ESLint validation +- Prettier formatting +- TypeScript type checking +- Conventional commit messages + +### Commit Messages + +Follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +feat: add new notebook toolbar +fix: resolve kernel connection issue +docs: update README with examples +chore: update dependencies +test: add cell component tests +``` + +## ๐Ÿงช Testing + +### Unit Tests + +```bash +# Run all tests +npm test + +# Run tests in watch mode +npm run test:watch + +# Run tests with coverage +npm run test:coverage +``` + +### Storybook Tests + +```bash +# Test all stories +npm run test:storybook + +# Test MDX documentation +npm run test:mdx + +# Run all Storybook tests +npm run test:all ``` + +## ๐Ÿ“š Documentation + +### Adding Documentation + +1. **Component Documentation**: Add JSDoc comments to your components +2. **Storybook Stories**: Create stories with MDX documentation +3. **API Documentation**: Update TypeDoc comments +4. **User Documentation**: Update files in `docs/` folder + +### Building Documentation + +```bash +# Build documentation site +cd docs +npm run build + +# Start documentation dev server +npm run start +``` + +## ๐Ÿ› Reporting Issues + +When reporting issues, please include: + +1. **Description**: Clear description of the problem +2. **Reproduction**: Steps to reproduce the issue +3. **Expected Behavior**: What should happen +4. **Actual Behavior**: What actually happens +5. **Environment**: Node version, OS, browser +6. **Screenshots**: If applicable + +## ๐Ÿšข Submitting Pull Requests + +### Process + +1. Fork the repository +2. Create a feature branch: `git checkout -b feature/your-feature` +3. Make your changes +4. Run tests: `npm test` +5. Run code quality checks: `npm run check` +6. Commit with conventional message: `git commit -m "feat: add feature"` +7. Push to your fork: `git push origin feature/your-feature` +8. Open a Pull Request + +### PR Guidelines + +- **Title**: Use conventional commit format +- **Description**: Explain what and why (not how) +- **Tests**: Include tests for new features +- **Documentation**: Update relevant documentation +- **Screenshots**: Add for UI changes +- **Breaking Changes**: Clearly mark if any + +### Review Process + +1. CI checks must pass +2. Code review by maintainers +3. Address review feedback +4. Merge when approved + +## ๐Ÿ”ง Troubleshooting + +### Common Issues + +**Dependencies Issues** + +```bash +# Clean install +rm -rf node_modules package-lock.json +npm install +``` + +**Build Issues** + +```bash +# Clean and rebuild +npm run clean +npm run build +``` + +**Jupyter Server Issues** + +```bash +# Check server is running +curl http://localhost:8686/api + +# Restart server +npm run jupyter:server +``` + +## ๐Ÿ“ฌ Getting Help + +- **Documentation**: https://jupyter-ui.datalayer.tech +- **Issues**: https://github.com/datalayer/jupyter-ui/issues +- **Discussions**: https://github.com/datalayer/jupyter-ui/discussions +- **Storybook**: https://jupyter-ui-storybook.datalayer.tech + +## ๐Ÿ“„ License + +By contributing, you agree that your contributions will be licensed under the MIT License. diff --git a/README.md b/README.md index 5f734e83c..61df031ec 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,107 @@ # ๐Ÿช โš›๏ธ Jupyter UI +[![Build Status](https://github.com/datalayer/jupyter-ui/actions/workflows/build.yml/badge.svg)](https://github.com/datalayer/jupyter-ui/actions/workflows/build.yml) +[![npm version](https://img.shields.io/npm/v/@datalayer/jupyter-react)](https://www.npmjs.com/package/@datalayer/jupyter-react) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![TypeScript](https://img.shields.io/badge/TypeScript-5.8.3-blue)](https://www.typescriptlang.org/) +[![React](https://img.shields.io/badge/React-18.3.1-blue)](https://reactjs.org/) + > React.js components ๐Ÿ’ฏ% compatible with ๐Ÿช Jupyter. > > Documentation: https://jupyter-ui.datalayer.tech > > Storybook: https://jupyter-ui-storybook.datalayer.tech -Jupyter UI is a set of [React.js](https://react.dev) components that allow a frontend/webapp developer to build data products compatible with the [Jupyter](https://jupyter.org) ecosystem. The user interface delivers executable notebooks, cells, terminals, file browsers and allows the developer to manage a full integrated React tree instead of relying on iframes to display the Jupyter noteboks. +Jupyter UI is a set of [React.js](https://react.dev) components that allow a frontend/webapp developer to build data products compatible with the [Jupyter](https://jupyter.org) ecosystem. The user interface delivers executable notebooks, cells, terminals, file browsers and allows the developer to manage a full integrated React tree instead of relying on iframes to display the Jupyter notebooks. + +## ๐Ÿ“ฆ Packages + +| Package | Version | Description | +| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | +| [@datalayer/jupyter-react](./packages/react) | [![npm](https://img.shields.io/npm/v/@datalayer/jupyter-react)](https://www.npmjs.com/package/@datalayer/jupyter-react) | Core React components for Jupyter integration | +| [@datalayer/jupyter-lexical](./packages/lexical) | [![npm](https://img.shields.io/npm/v/@datalayer/jupyter-lexical)](https://www.npmjs.com/package/@datalayer/jupyter-lexical) | Rich text editor with Lexical framework | +| [@datalayer/jupyter-docusaurus-plugin](./packages/docusaurus-plugin) | [![npm](https://img.shields.io/npm/v/@datalayer/jupyter-docusaurus-plugin)](https://www.npmjs.com/package/@datalayer/jupyter-docusaurus-plugin) | Docusaurus plugin for Jupyter notebooks | +| [datalayer-jupyter-vscode](./packages/vscode) | [![marketplace](https://img.shields.io/visual-studio-marketplace/v/datalayer.datalayer-jupyter-vscode)](https://marketplace.visualstudio.com/items?itemName=datalayer.datalayer-jupyter-vscode) | VS Code extension | + +## ๐Ÿš€ Quick Start + +### Installation + +```bash +npm install @datalayer/jupyter-react +``` + +### Basic Usage + +```tsx +import { Jupyter, Notebook } from '@datalayer/jupyter-react'; + +function App() { + return ( + + + + ); +} +``` -
+### Development Setup As a developer start with the [setup of your environment](https://jupyter-ui.datalayer.tech/docs/develop/setup) and try [one of the examples](https://jupyter-ui.datalayer.tech/docs/category/examples). We have [documentation](https://jupyter-ui.datalayer.tech) for more details. -> You can try the CodeSanbox examples. +```bash +# Clone the repository +git clone https://github.com/datalayer/jupyter-ui.git +cd jupyter-ui + +# Install dependencies +npm install + +# Build all packages +npm run build + +# Start Jupyter server (required for development) +npm run jupyter:server + +# Run an example +npm run jupyter:ui:vite +``` + +## ๐ŸŽฎ Try It Online + +You can try the CodeSandbox examples: + +- [Notebook with Create React App](https://codesandbox.io/p/sandbox/jupyter-react-cra-notebook-66r25c-66r25c) +- [Notebook with Next.js](https://codesandbox.io/p/devbox/jupyter-react-nextjs-qzv8cz) +- [Simple Cell Component](https://codesandbox.io/p/sandbox/jupyter-react-cra-cell-te6hii-te6hii) - You may need to refresh the sandbox navigator + +We host a Storybook on โœจ https://jupyter-ui-storybook.datalayer.tech that showcases various low-level and high-level React.js components useful to build a Data Product. -- [A Notebook with CRA](https://codesandbox.io/p/sandbox/jupyter-react-cra-notebook-66r25c-66r25c). -- [A Notebook with Next JS](https://codesandbox.io/p/devbox/jupyter-react-nextjs-qzv8cz). -- [A simple Cell](https://codesandbox.io/p/sandbox/jupyter-react-cra-cell-te6hii-te6hii) - You may need to refesh the sandbox navigator. +## โœจ Features -We host a Storybook on โœจ https://jupyter-ui-storybook.datalayer.tech that showcases various low-level as high-level React.js components useful to build a Data Product. +### Core Components + +- **๐Ÿ““ Notebook** - Full notebook interface with cells, outputs, and toolbar +- **๐Ÿ“ Cell** - Individual code/markdown cells with execution +- **๐Ÿ’ป Console** - Interactive Jupyter console +- **๐Ÿ–ฅ๏ธ Terminal** - Web-based terminal interface +- **๐Ÿ“ FileBrowser** - File system navigation and management +- **โš™๏ธ Kernel Management** - Kernel lifecycle and execution control +- **๐Ÿ“Š Output Rendering** - Display of execution results, plots, and widgets + +### Advanced Features + +- **๐Ÿ”Œ IPyWidgets Support** - Full support for interactive widgets +- **๐Ÿ‘ฅ Collaborative Editing** - Real-time collaboration using Y.js +- **๐ŸŽจ Theming** - JupyterLab theme support with dark/light modes +- **๐Ÿ”ง Extensible** - Plugin system for custom functionality +- **๐Ÿš€ Performance** - Virtual scrolling, lazy loading, and optimizations +- **๐Ÿ”’ Security** - Token authentication, CORS, XSS protection
Jupyter UI Gallery @@ -42,28 +124,77 @@ IPyWidgets are supported (the Comm feature needs to be fixed). JupyterLite and P You can find more context reading this [abstract](https://fosdem.org/2022/schedule/event/lt_jupyter) of the talk given at [FOSDEM 2022](https://fosdem.org/2022) ([video recording](http://bofh.nikhef.nl/events/FOSDEM/2022/L.lightningtalks/lt_jupyter.webm)). -## Next.js Integration +## ๐Ÿ”— Framework Integrations + +### Next.js -See the [Next.js example](https://github.com/datalayer/jupyter-ui/tree/main/examples/next-js). +Full server-side rendering support with the [Next.js example](https://github.com/datalayer/jupyter-ui/tree/main/examples/next-js).
- Jupyter UI Docusaurus + Jupyter UI Next.js
-## Docusaurus Integration +### Docusaurus -We maintain a plugin for [Docusaurus](https://docusaurus.io) in the [docusaurus](https://github.com/datalayer/jupyter-ui/tree/main/packages/docusaurus-plugin) package folder (see the [Docusaurus example](https://github.com/datalayer/jupyter-ui/tree/main/examples/docusaurus)). +We maintain a plugin for [Docusaurus](https://docusaurus.io) in the [docusaurus-plugin](https://github.com/datalayer/jupyter-ui/tree/main/packages/docusaurus-plugin) package (see the [Docusaurus example](https://github.com/datalayer/jupyter-ui/tree/main/examples/docusaurus)).
Jupyter UI Docusaurus
-## Support +### Other Integrations + +- **Vite** - Modern build tool integration ([example](https://github.com/datalayer/jupyter-ui/tree/main/examples/vite)) +- **Create React App** - Classic React app setup +- **Lexical** - Rich text editing capabilities ([example](https://github.com/datalayer/jupyter-ui/tree/main/examples/lexical)) +- **VS Code** - Extension for notebook editing ([package](https://github.com/datalayer/jupyter-ui/tree/main/packages/vscode)) + +## ๐Ÿ“‹ Requirements + +- **Node.js** >= 18.0.0 +- **npm** >= 8.0.0 +- **Python** >= 3.8 (for Jupyter server) +- **JupyterLab** >= 4.0.0 + +## ๐Ÿค Contributing + +We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details. + +### Development Workflow + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Make your changes +4. Run tests (`npm test`) +5. Run code quality checks (`npm run check`) +6. Commit your changes (`git commit -m 'Add amazing feature'`) +7. Push to your branch (`git push origin feature/amazing-feature`) +8. Open a Pull Request + +### Code Quality + +- **TypeScript** - Strict type checking enabled +- **ESLint** - Modern flat config with React and TypeScript rules +- **Prettier** - Consistent code formatting +- **Husky** - Pre-commit hooks for quality checks +- **Commitlint** - Conventional commit messages + +Run all checks with a single command: + +```bash +npm run check # Run format check, lint, and type-check +npm run check:fix # Run format, lint fix, and type-check +``` + +## ๐Ÿ’ฌ Support -Please open [issues](https://github.com/datalayer/jupyter-ui/issues) for questions, feature requests, bug reports... We also welcome [pull requests](https://github.com/datalayer/jupyter-ui/pulls). +- ๐Ÿ“ [Documentation](https://jupyter-ui.datalayer.tech) +- ๐Ÿ› [Issues](https://github.com/datalayer/jupyter-ui/issues) +- ๐Ÿ’ก [Discussions](https://github.com/datalayer/jupyter-ui/discussions) +- ๐ŸŽจ [Storybook](https://jupyter-ui-storybook.datalayer.tech) ## โš–๏ธ License -Copyright (c) 2022 Datalayer, Inc. +Copyright (c) 2022-2025 Datalayer, Inc. Released under the terms of the MIT license (see [LICENSE](./LICENSE)). diff --git a/SUMMARY.md b/SUMMARY.md index 2e81301e5..1a806ae3f 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -7,6 +7,7 @@ Jupyter UI is an open-source React.js component library that bridges the gap bet ### Core Problem Solved Traditional JupyterLab uses the Lumino widget toolkit, an imperative UI framework that isn't compatible with modern declarative frameworks like React. This forces developers to either: + - Use iframes to embed Jupyter notebooks (limiting integration) - Work within the rigid extension system of JupyterLab - Build entirely from scratch @@ -44,6 +45,7 @@ jupyter-ui/ The main package providing React components for Jupyter functionality. **Key Components:** + - **Notebook Components**: Full notebook interface with cells, outputs, toolbar - **Cell Components**: Individual code/markdown cells with execution - **Console**: Interactive Jupyter console @@ -53,12 +55,14 @@ The main package providing React components for Jupyter functionality. - **Output Rendering**: Display of execution results, plots, widgets **Architecture:** + - Uses JupyterLab's underlying services (kernels, sessions, contents) - Provides React context providers for state management - Supports both local and remote Jupyter servers - Implements WebSocket communication for real-time updates **Key Files:** + - `src/jupyter/JupyterContext.tsx` - Core context provider - `src/components/notebook/Notebook.tsx` - Main notebook component - `src/providers/ServiceManagerProvider.tsx` - Service management @@ -68,6 +72,7 @@ The main package providing React components for Jupyter functionality. Integration with Meta's Lexical framework for rich text editing in notebooks. **Features:** + - Rich text editing with Jupyter cell support - Code highlighting and syntax support - Equation rendering (KaTeX) @@ -76,6 +81,7 @@ Integration with Meta's Lexical framework for rich text editing in notebooks. - Conversion between Lexical and nbformat **Components:** + - Custom Lexical nodes for Jupyter cells - Plugins for Jupyter-specific functionality - Toolbar and formatting controls @@ -86,6 +92,7 @@ Integration with Meta's Lexical framework for rich text editing in notebooks. Plugin enabling Jupyter notebook integration in Docusaurus documentation sites. **Capabilities:** + - Embed live notebooks in documentation - Interactive code execution - Syntax highlighting @@ -96,6 +103,7 @@ Plugin enabling Jupyter notebook integration in Docusaurus documentation sites. VS Code extension for notebook editing using the Jupyter UI components. **Features:** + - Custom notebook editor - Kernel management within VS Code - Runtime picker @@ -106,21 +114,49 @@ VS Code extension for notebook editing using the Jupyter UI components. ### Build System **Technologies:** + - TypeScript for type safety - Webpack for bundling - Gulp for resource management - Babel for transpilation - Lerna for monorepo management +- npm workspaces for dependency management **Build Process:** + 1. Resource copying via Gulp 2. TypeScript compilation 3. Webpack bundling 4. Package-specific builds +### Code Quality & Linting + +**Pre-commit Hooks (Husky + lint-staged):** + +- ESLint for code quality (v9 flat config) +- Prettier for code formatting +- TypeScript type checking +- Conventional commit messages (commitlint) + +**Linting Stack:** + +- ESLint with TypeScript support +- React and React Hooks plugins +- Prettier integration +- Custom rules for JupyterLab compatibility + +**Available Scripts:** + +- `npm run lint` - Check for linting issues +- `npm run lint:fix` - Auto-fix linting issues +- `npm run format` - Format all files with Prettier +- `npm run format:check` - Check formatting +- `npm run type-check` - TypeScript type checking + ### Development Server **Jupyter Server Configuration:** + - Port: 8686 - Token authentication enabled - CORS configured for development @@ -128,12 +164,29 @@ VS Code extension for notebook editing using the Jupyter UI components. - Terminal support enabled **Frontend Development:** + - Hot module replacement - Port: 3208 (varies by example) - Proxy configuration for API calls ### Testing Infrastructure +### Storybook Testing + +- **@storybook/test-runner** for automated story testing +- Custom MDX documentation testing with Playwright +- 38 component stories with smoke tests +- 13 MDX documentation files verified +- CI integration with GitHub Actions + +### Test Commands + +- `npm run test:storybook` - Test all stories +- `npm run test:mdx` - Test MDX documentation +- `npm run test:all` - Run both test suites + +### Other Testing + - Jest for unit testing - Playwright for UI testing - Coverage reporting @@ -142,6 +195,7 @@ VS Code extension for notebook editing using the Jupyter UI components. ## Key Technologies & Dependencies ### Frontend Stack + - **React 18.3.1** - UI framework - **TypeScript 5.8.3** - Type safety - **JupyterLab packages** - Core Jupyter functionality @@ -150,12 +204,14 @@ VS Code extension for notebook editing using the Jupyter UI components. - **IPyWidgets** - Interactive widget support ### Styling & Theming + - CSS modules - JupyterLab themes - Tailwind CSS (v4 in lexical package) - Custom theme providers ### Communication Layer + - WebSocket for kernel communication - REST API for server operations - Service Manager pattern @@ -176,7 +232,7 @@ function App() { }); return ( - @@ -220,13 +276,14 @@ Developers can register custom output renderers: ```typescript registerRenderer({ mimeType: 'application/custom', - renderer: CustomComponent + renderer: CustomComponent, }); ``` ### Plugin System The Lexical package supports plugins for: + - Custom cell types - Toolbar extensions - Keyboard shortcuts @@ -235,17 +292,20 @@ The Lexical package supports plugins for: ## Deployment Scenarios ### 1. Static Sites + - Build-time notebook rendering - Client-side kernel execution (Pyodide) - No server required ### 2. Server-Based + - Full Jupyter server backend - Multi-user support - Persistent storage - Real-time collaboration ### 3. Hybrid + - Static content with on-demand execution - Serverless function backends - Edge computing scenarios @@ -266,19 +326,117 @@ The Lexical package supports plugins for: - Sandboxed iframe execution - Content Security Policy support +## CI/CD Pipeline + +### GitHub Actions Workflows + +**build.yml:** + +- Runs on main branch and PRs +- Build verification across packages +- Visual testing with Playwright +- Storybook story and MDX testing +- Docker container builds +- Uses npm instead of yarn +- TypeScript type checking +- ESLint error checking (no warnings) +- Prettier formatting validation + +**Key Jobs:** + +1. **build** - Compiles all packages +2. **visual-test** - Playwright visual regression tests +3. **storybook-test** - Tests all stories and documentation +4. **docker-dev** - Dev container verification + +**Other Workflows:** + +- `fix-license-header.yml` - Automatic license header corrections +- `publish-dev.yml` - Publishes dev versions to GitHub Packages + +## Recent Improvements (2024-2025) + +### Configuration Modernization + +- **ESLint v9 Flat Config**: Migrated from deprecated .eslintignore to modern flat config format +- **Prettier JSON Config**: Replaced CommonJS config with .prettierrc.json +- **Line Endings**: Enforced LF line endings for cross-platform consistency +- **Trailing Commas**: Added support for better TypeScript compatibility +- **Node.js 20+**: Updated minimum requirement from Node 18 to Node 20 +- **NVM Support**: Added .nvmrc file for consistent Node version management + +### Code Quality Fixes + +- **React 18 Migration**: Fixed deprecated ReactDOM.render usage +- **TypeScript Strictness**: Replaced @ts-ignore with @ts-expect-error +- **React Best Practices**: Added missing key props in list renderings +- **Security**: Added rel="noreferrer" to external links +- **Hook Rules**: Fixed conditional hook calls + +### Storybook Fixes + +- **MDX Comment Syntax**: Fixed malformed comments in 13 MDX files from `{/_` to `{/** **/}` +- **Prettier Exclusion**: Added MDX files to .prettierignore to prevent comment corruption +- **Missing Logo Files**: Created patch for @jupyterlite/javascript-kernel-extension to fix missing logo references + +### Build & CI Improvements + +- **Webpack Warnings**: Reduced source map warnings from 7 to 2 by excluding problematic packages +- **Patch-Package**: Added automatic patching of third-party modules during npm install +- **GitHub Actions**: Updated all workflows to use Node 20 +- **Build Stability**: Fixed CI build failures in Storybook +- **Storybook CI Testing**: Fixed test runner connection issues with wait-on and explicit URL configuration +- **Terminal Component**: Fixed BoxPanel initialization error with proper direction setting and delayed widget addition + +### Testing Infrastructure + +- **Storybook Test Runner**: Automated testing for all component stories +- **MDX Documentation Testing**: Verification of documentation examples +- **CI Integration**: Full test suite runs on every PR + +## Latest Session Updates (Aug 15, 2025) + +### Storybook CI Test Fixes + +- **Problem**: Test runner couldn't connect to Storybook static server (127.0.0.1 vs localhost mismatch) +- **Solution**: + - Created `test:all:ci` script with explicit `--url http://localhost:6006` + - Added `npx wait-on` to ensure server is ready before tests + - Removed fixed sleep duration for more reliable CI runs + +### Terminal Component Fixes + +- **Problem**: `TypeError: Cannot read properties of null (reading 'addWidget')` in BoxPanel +- **Solution**: + - Added `direction = 'top-to-bottom'` to BoxPanel initialization + - Added defensive check in `setTheme` method + - Implemented delayed widget addition with error handling + - Added error logging for better debugging + +### Files Modified in Latest Session + +- `.github/workflows/build.yml` - Updated Storybook test runner configuration +- `storybook/package.json` - Added test:all:ci script +- `packages/react/src/components/terminal/TerminalAdapter.ts` - Fixed BoxPanel initialization + ## Notable Features ### IPyWidgets Support + Full support for interactive widgets with two-way communication between Python and JavaScript. ### Collaborative Editing + Real-time collaboration using Y.js for conflict-free replicated data types. ### Multiple Kernel Support + Simultaneous connections to different kernels (Python, R, Julia, etc.). ### Extensible Output System + Support for various output types including: + - Plain text/HTML/Markdown - Images (PNG, JPEG, SVG) - Plots (Matplotlib, Plotly, Bokeh) @@ -313,4 +471,4 @@ The repository includes several example implementations: ## Conclusion -Jupyter UI represents a significant advancement in making Jupyter notebooks accessible to modern web developers. By providing React components that wrap Jupyter functionality, it enables the creation of custom data products that leverage the full power of the Jupyter ecosystem while maintaining the flexibility and composability that React developers expect. The monorepo structure, comprehensive examples, and extensive documentation make it a robust solution for integrating computational notebooks into web applications. \ No newline at end of file +Jupyter UI represents a significant advancement in making Jupyter notebooks accessible to modern web developers. By providing React components that wrap Jupyter functionality, it enables the creation of custom data products that leverage the full power of the Jupyter ecosystem while maintaining the flexibility and composability that React developers expect. The monorepo structure, comprehensive examples, and extensive documentation make it a robust solution for integrating computational notebooks into web applications. diff --git a/dev/py/templates/error.html b/dev/py/templates/error.html index e319d5edc..c4e29928f 100644 --- a/dev/py/templates/error.html +++ b/dev/py/templates/error.html @@ -4,62 +4,58 @@ ~ MIT License --> - + + + - - + {% block title %}{{page_title | e}}{% endblock %} - {% block title %}{{page_title | e}}{% endblock %} - - {% block favicon %}{% endblock %} - - - - - -{% block stylesheet %} - -{% endblock %} -{% block site %} - -
- {% block h1_error %} -

{{status_code | e}} : {{status_message | e}}

- {% endblock h1_error %} - {% block error_detail %} - {% if message %} -

The error was:

-
-
{{message | e}}
-
- {% endif %} + {% block favicon %} + {% endblock %} - - -{% endblock %} - -{% block script %} - -{% endblock script %} - - + + + + {% block stylesheet %} + + {% endblock %} {% block site %} + +
+ {% block h1_error %} +

{{status_code | e}} : {{status_message | e}}

+ {% endblock h1_error %} {% block error_detail %} {% if message %} +

The error was:

+
+
{{message | e}}
+
+ {% endif %} {% endblock %} +
+ {% endblock %} {% block script %} + + {% endblock script %} + diff --git a/dev/py/templates/index.html b/dev/py/templates/index.html index 2102108ad..c466aa284 100644 --- a/dev/py/templates/index.html +++ b/dev/py/templates/index.html @@ -4,34 +4,34 @@ ~ MIT License --> - + - - {{page_config['appName'] | e}} - - + + {{page_config['appName'] | e}} + + + {# 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) %} - {# 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) %} - - - + + - - - + + diff --git a/docs/README.md b/docs/README.md index e0aa5ae3f..97aece7f8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,7 +19,7 @@ npm start ``` ```bash -# Build: This command generates static content into the `build` directory +# Build: This command generates static content into the `build` directory # and can be served using any static contents hosting service. npm run build ``` diff --git a/docs/_tmp/ipyreactive/_category_.yml b/docs/_tmp/ipyreactive/_category_.yml index 9f0ce4c8f..22fadd98a 100644 --- a/docs/_tmp/ipyreactive/_category_.yml +++ b/docs/_tmp/ipyreactive/_category_.yml @@ -1,2 +1,2 @@ -label: "IPyReactive" +label: 'IPyReactive' position: 12 diff --git a/docs/_tmp/ipyreactive/index.mdx b/docs/_tmp/ipyreactive/index.mdx index b62343366..b65aa725e 100644 --- a/docs/_tmp/ipyreactive/index.mdx +++ b/docs/_tmp/ipyreactive/index.mdx @@ -5,8 +5,8 @@ --> --- -description: IPyReactive. ---- + +## description: IPyReactive. # ๐Ÿช ๐Ÿ”ฅ IPyReactive diff --git a/docs/_tmp/ipyscript/_category_.yml b/docs/_tmp/ipyscript/_category_.yml index 3648f940e..056de3ce3 100644 --- a/docs/_tmp/ipyscript/_category_.yml +++ b/docs/_tmp/ipyscript/_category_.yml @@ -1,2 +1,2 @@ -label: "IPyScript" +label: 'IPyScript' position: 11 diff --git a/docs/_tmp/ipyscript/index.mdx b/docs/_tmp/ipyscript/index.mdx index fdd3a1bfa..36355277c 100644 --- a/docs/_tmp/ipyscript/index.mdx +++ b/docs/_tmp/ipyscript/index.mdx @@ -5,8 +5,8 @@ --> --- -description: IPyScript. ---- + +## description: IPyScript. # ๐Ÿช ๐Ÿ‰ IPyScript diff --git a/docs/_tmp/lumino/_category_.yml b/docs/_tmp/lumino/_category_.yml index ea2a6dbfd..7ae6defd9 100644 --- a/docs/_tmp/lumino/_category_.yml +++ b/docs/_tmp/lumino/_category_.yml @@ -1,2 +1,2 @@ -label: "Lumino" +label: 'Lumino' position: 14 diff --git a/docs/_tmp/lumino/index.mdx b/docs/_tmp/lumino/index.mdx index a7c6b710b..c7581ddd4 100644 --- a/docs/_tmp/lumino/index.mdx +++ b/docs/_tmp/lumino/index.mdx @@ -5,8 +5,8 @@ --> --- -description: Jupyter Lumino. ---- + +## description: Jupyter Lumino. # ๐Ÿช Jupyter Lumino diff --git a/docs/_tmp/native/_category_.yml b/docs/_tmp/native/_category_.yml index 8950a6c80..69fefb4e2 100644 --- a/docs/_tmp/native/_category_.yml +++ b/docs/_tmp/native/_category_.yml @@ -1,2 +1,2 @@ -label: "Native" +label: 'Native' position: 9 diff --git a/docs/_tmp/native/index.mdx b/docs/_tmp/native/index.mdx index ae05fd5d3..9990efd95 100644 --- a/docs/_tmp/native/index.mdx +++ b/docs/_tmp/native/index.mdx @@ -5,8 +5,8 @@ --> --- -description: Jupyter UI Native components for mobile platforms. ---- + +## description: Jupyter UI Native components for mobile platforms. # ๐Ÿช ๐Ÿ“ฑ Jupyter UI Native diff --git a/docs/docs/about/_category_.yml b/docs/docs/about/_category_.yml index 089109989..de68a6583 100644 --- a/docs/docs/about/_category_.yml +++ b/docs/docs/about/_category_.yml @@ -1,2 +1,2 @@ -label: "About" +label: 'About' position: 1 diff --git a/docs/docs/about/index.mdx b/docs/docs/about/index.mdx index 94c83ce59..e55b96555 100644 --- a/docs/docs/about/index.mdx +++ b/docs/docs/about/index.mdx @@ -1,4 +1,4 @@ -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; # ๐Ÿช โš›๏ธ About Jupyter UI diff --git a/docs/docs/components/_category_.yaml b/docs/docs/components/_category_.yaml index 4b1f50565..7ffc858e4 100644 --- a/docs/docs/components/_category_.yaml +++ b/docs/docs/components/_category_.yaml @@ -1,2 +1,2 @@ -label: "Components" +label: 'Components' position: 3 diff --git a/docs/docs/components/button/_category_.yml b/docs/docs/components/button/_category_.yml index e3b0cf730..3eb786066 100644 --- a/docs/docs/components/button/_category_.yml +++ b/docs/docs/components/button/_category_.yml @@ -1,2 +1,2 @@ -label: "Button" +label: 'Button' position: 5 diff --git a/docs/docs/components/button/index.mdx b/docs/docs/components/button/index.mdx index 98883ca19..d1bac38b0 100644 --- a/docs/docs/components/button/index.mdx +++ b/docs/docs/components/button/index.mdx @@ -6,6 +6,4 @@ import { StorybookView } from '@theme/StorybookView'; # ๐Ÿช Button Component - + diff --git a/docs/docs/components/cell/_category_.yml b/docs/docs/components/cell/_category_.yml index 7f536271c..11ef1c990 100644 --- a/docs/docs/components/cell/_category_.yml +++ b/docs/docs/components/cell/_category_.yml @@ -1,2 +1,2 @@ -label: "Cell" +label: 'Cell' position: 3 diff --git a/docs/docs/components/cell/index.mdx b/docs/docs/components/cell/index.mdx index 667818556..087360e13 100644 --- a/docs/docs/components/cell/index.mdx +++ b/docs/docs/components/cell/index.mdx @@ -10,14 +10,14 @@ This component displays a Jupyter Cell. ## With a Jupyter server - + ```jsx - + ``` @@ -48,17 +48,15 @@ plt.show() You can use an in-browser kernel by setting the `lite` property of `Jupyter` component: - + ```jsx - + /> ``` diff --git a/docs/docs/components/console/_category_.yml b/docs/docs/components/console/_category_.yml index 393e9c527..7ab57c2c5 100644 --- a/docs/docs/components/console/_category_.yml +++ b/docs/docs/components/console/_category_.yml @@ -1,2 +1,2 @@ -label: "Console" +label: 'Console' position: 3 diff --git a/docs/docs/components/console/index.mdx b/docs/docs/components/console/index.mdx index 64e7df3f8..5c6e61280 100644 --- a/docs/docs/components/console/index.mdx +++ b/docs/docs/components/console/index.mdx @@ -10,20 +10,19 @@ This component displays a Jupyter Console. ## With a Jupyter server - + ```jsx - + ``` You can set the following props to connect it to a running Jupyter Server: + - `jupyterServerUrl`: The server URL - `jupyterServerToken`: The server authentication token @@ -36,34 +35,29 @@ It supports in-browser kernel (using [JupyterLite kernels](https://jupyterlite.r To use the [Pyodide](https://pyodide.org/) Python [kernel](https://github.com/jupyterlite/pyodide-kernel), you can simply set `lite` to `true`: - + ```jsx - - + + ``` But you can also load your own JupyterLite kernel. For example here, the console loads the [JavaScript kernel](https://github.com/jupyterlite/jupyterlite/tree/main/py/jupyterlite-javascript-kernel). - + ```jsx - + ``` To do so, you will need to specify two props: + - `lite`: Should be set with the dynamic import of the kernel NPM package - `defaultKernelName`: Should match the name of the dynamically imported kernel diff --git a/docs/docs/components/context/_category_.yml b/docs/docs/components/context/_category_.yml index 43cc24f74..9d1735c38 100644 --- a/docs/docs/components/context/_category_.yml +++ b/docs/docs/components/context/_category_.yml @@ -1,2 +1,2 @@ -label: "Context" +label: 'Context' position: 9 diff --git a/docs/docs/components/context/index.mdx b/docs/docs/components/context/index.mdx index 65ae2c72f..c0eb8ac4c 100644 --- a/docs/docs/components/context/index.mdx +++ b/docs/docs/components/context/index.mdx @@ -19,9 +19,9 @@ If you prefer to not wrap in a Context, you can just use the JupyterLabCss compo ```tsx root.render( <> - + - + , ); ``` @@ -39,11 +39,11 @@ root.render( nbformat={notebookExample1 as INotebookContent} CellSidebar={CellSidebar} Toolbar={NotebookToolbar} - height='calc(100vh - 2.6rem)' // (Height - Toolbar Height). + height="calc(100vh - 2.6rem)" // (Height - Toolbar Height). cellSidebarMargin={120} uid="notebook-uid" /> - + , ); ``` diff --git a/docs/docs/components/file-manager/_category_.yml b/docs/docs/components/file-manager/_category_.yml index 784250afe..1c76d41cc 100644 --- a/docs/docs/components/file-manager/_category_.yml +++ b/docs/docs/components/file-manager/_category_.yml @@ -1,2 +1,2 @@ -label: "File Manager" +label: 'File Manager' position: 5 diff --git a/docs/docs/components/file-manager/index.mdx b/docs/docs/components/file-manager/index.mdx index e63ae5c94..b2e9500fd 100644 --- a/docs/docs/components/file-manager/index.mdx +++ b/docs/docs/components/file-manager/index.mdx @@ -6,6 +6,4 @@ import { StorybookView } from '@theme/StorybookView'; # ๐Ÿช File Manager Component - + diff --git a/docs/docs/components/index.mdx b/docs/docs/components/index.mdx index 62e7d9908..a733050ee 100644 --- a/docs/docs/components/index.mdx +++ b/docs/docs/components/index.mdx @@ -1,5 +1,5 @@ import DocCardList from '@theme/DocCardList'; -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; # Components @@ -36,4 +36,4 @@ The Jupyter UI components support: ::: - + diff --git a/docs/docs/components/jupyterlab-app/_category_.yml b/docs/docs/components/jupyterlab-app/_category_.yml index 3dabe0f6c..2df36cfd6 100644 --- a/docs/docs/components/jupyterlab-app/_category_.yml +++ b/docs/docs/components/jupyterlab-app/_category_.yml @@ -1,2 +1,2 @@ -label: "JupyterLab App" +label: 'JupyterLab App' position: 13 diff --git a/docs/docs/components/kernel/_category_.yml b/docs/docs/components/kernel/_category_.yml index a0c394163..6ab4626ef 100644 --- a/docs/docs/components/kernel/_category_.yml +++ b/docs/docs/components/kernel/_category_.yml @@ -1,2 +1,2 @@ -label: "Kernels" +label: 'Kernels' position: 7 diff --git a/docs/docs/components/notebook/_category_.yml b/docs/docs/components/notebook/_category_.yml index 87051aaf9..f744900f2 100644 --- a/docs/docs/components/notebook/_category_.yml +++ b/docs/docs/components/notebook/_category_.yml @@ -1,2 +1,2 @@ -label: "Notebook" +label: 'Notebook' position: 2 diff --git a/docs/docs/components/notebook/index.mdx b/docs/docs/components/notebook/index.mdx index 072b66a8c..6ce9e01c0 100644 --- a/docs/docs/components/notebook/index.mdx +++ b/docs/docs/components/notebook/index.mdx @@ -4,26 +4,25 @@ description: The Notebook component. import { StorybookView } from '@theme/StorybookView'; -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; # ๐Ÿช Notebook Component ## With a Jupyter server - + ```jsx - + ``` You can set the following props to connect it to a running Jupyter Server: + - `jupyterServerUrl`: The server URL - `jupyterServerToken`: The server authentication token @@ -40,9 +39,7 @@ It supports in-browser kernel (using [JupyterLite kernels](https://jupyterlite.r To use the [Pyodide](https://pyodide.org/) Python [kernel](https://github.com/jupyterlite/pyodide-kernel), you can simply set `lite` to `true`: - + ```jsx + diff --git a/docs/docs/components/viewer/_category_.yml b/docs/docs/components/viewer/_category_.yml index 7977d6ed4..04b077234 100644 --- a/docs/docs/components/viewer/_category_.yml +++ b/docs/docs/components/viewer/_category_.yml @@ -1,2 +1,2 @@ -label: "Viewer" +label: 'Viewer' position: 6 diff --git a/docs/docs/components/viewer/index.mdx b/docs/docs/components/viewer/index.mdx index be1901b48..3a04a8721 100644 --- a/docs/docs/components/viewer/index.mdx +++ b/docs/docs/components/viewer/index.mdx @@ -6,6 +6,4 @@ import { StorybookView } from '@theme/StorybookView'; # ๐Ÿช Viewer Component - + diff --git a/docs/docs/demos/_category_.yaml b/docs/docs/demos/_category_.yaml index 3967ba5a1..025b227fb 100644 --- a/docs/docs/demos/_category_.yaml +++ b/docs/docs/demos/_category_.yaml @@ -1,2 +1,2 @@ -label: "Demos" +label: 'Demos' position: 9 diff --git a/docs/docs/demos/cell/_category_.yml b/docs/docs/demos/cell/_category_.yml index aa85c15b8..4f927e0b7 100644 --- a/docs/docs/demos/cell/_category_.yml +++ b/docs/docs/demos/cell/_category_.yml @@ -1,2 +1,2 @@ -label: "Cell" +label: 'Cell' position: 1 diff --git a/docs/docs/demos/cell/index.mdx b/docs/docs/demos/cell/index.mdx index c3d0500f3..cfe3a3e71 100644 --- a/docs/docs/demos/cell/index.mdx +++ b/docs/docs/demos/cell/index.mdx @@ -8,12 +8,12 @@ import JupyterCell from '@theme/JupyterCell'; :::tip -โœจ Type your code in the below cell and hit `SHIFT+ENTER` to execute your code. +โœจ Type your code in the below cell and hit `SHIFT+ENTER` to execute your code. ::: + diff --git a/docs/docs/deployments/_category_.yaml b/docs/docs/deployments/_category_.yaml index 338de7018..0226d7af8 100644 --- a/docs/docs/deployments/_category_.yaml +++ b/docs/docs/deployments/_category_.yaml @@ -1,2 +1,2 @@ -label: "Deployments" +label: 'Deployments' position: 7 diff --git a/docs/docs/deployments/aws-amplify/_category_.yml b/docs/docs/deployments/aws-amplify/_category_.yml index 02ac03843..907779df0 100644 --- a/docs/docs/deployments/aws-amplify/_category_.yml +++ b/docs/docs/deployments/aws-amplify/_category_.yml @@ -1,2 +1,2 @@ -label: "AWS Amplify" +label: 'AWS Amplify' position: 3 diff --git a/docs/docs/deployments/aws-amplify/index.mdx b/docs/docs/deployments/aws-amplify/index.mdx index ed8eb89c5..942328374 100644 --- a/docs/docs/deployments/aws-amplify/index.mdx +++ b/docs/docs/deployments/aws-amplify/index.mdx @@ -1,6 +1,6 @@ -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; -# AWS Amplify +# AWS Amplify To use jupyter-react in Amplify, you need to make some changes to the build process and the Node versions. @@ -12,20 +12,21 @@ To use jupyter-react in Amplify, you need to make some changes to the build proc After setting up your repository, follow these steps: -1. In your repository's App Settings, navigate to the Build settings tab. - -2. Locate the App build specification section, which contains the amplify.yml file editor. This file manages the build settings. - -3. Click the Edit button to modify the amplify.yml file. -4. Replace the existing content with the following code to use Yarn v3 instead of npm: +1. In your repository's App Settings, navigate to the Build settings tab. + +2. Locate the App build specification section, which contains the amplify.yml file editor. This file manages the build settings. + +3. Click the Edit button to modify the amplify.yml file. +4. Replace the existing content with the following code to use Yarn v3 instead of npm: + ```yaml version: 1 frontend: @@ -46,14 +47,15 @@ frontend: paths: - node_modules/**/* ``` -5. Save the changes to the amplify.yml file. -6. Scroll down to the Edit build image settings section and click Edit. - -7. By default, it uses Node version 16.0, which may not work correctly. Set the build image to node:18.17.3 (or your preferred Node version). -8. Save the changes. + +5. Save the changes to the amplify.yml file. +6. Scroll down to the Edit build image settings section and click Edit. + +7. By default, it uses Node version 16.0, which may not work correctly. Set the build image to node:18.17.3 (or your preferred Node version). +8. Save the changes. Once you've completed these steps, the next time you push to the main branch or the branch you've configured in Amplify, it will use these updated build settings. Your Amplify setup should work as expected. diff --git a/docs/docs/deployments/index.mdx b/docs/docs/deployments/index.mdx index ae33095ae..c51d32658 100644 --- a/docs/docs/deployments/index.mdx +++ b/docs/docs/deployments/index.mdx @@ -2,4 +2,4 @@ import DocCardList from '@theme/DocCardList'; # Deployments - + diff --git a/docs/docs/deployments/jupyter-server/_category_.yml b/docs/docs/deployments/jupyter-server/_category_.yml index e789c9712..a09d40bb8 100644 --- a/docs/docs/deployments/jupyter-server/_category_.yml +++ b/docs/docs/deployments/jupyter-server/_category_.yml @@ -1,2 +1,2 @@ -label: "Jupyter Server" +label: 'Jupyter Server' position: 1 diff --git a/docs/docs/deployments/jupyter-server/index.mdx b/docs/docs/deployments/jupyter-server/index.mdx index 5597492dd..c9f060ecf 100644 --- a/docs/docs/deployments/jupyter-server/index.mdx +++ b/docs/docs/deployments/jupyter-server/index.mdx @@ -35,14 +35,14 @@ Alternatively, you can to define a `script` tag in the head section your host in ```html - - - + + + ``` @@ -98,7 +98,7 @@ Please tune the following example to fit your security requirements, this is in
- โš ๏ธ Use the following at your own risk!. +โš ๏ธ Use the following at your own risk!. ```py ################# @@ -144,19 +144,19 @@ c.IdentityProvider.cookie_options = { If you are using the [JupyterLabApp](/docs/components/jupyterlab-app) component, additional information will be needed (see [index-local.html](https://github.com/datalayer/jupyter-ui/blob/main/packages/react/public/index-local.html)) ```html - + ``` ::: diff --git a/docs/docs/deployments/jupyterhub/_category_.yml b/docs/docs/deployments/jupyterhub/_category_.yml index 9aecf3a9e..dbff9000a 100644 --- a/docs/docs/deployments/jupyterhub/_category_.yml +++ b/docs/docs/deployments/jupyterhub/_category_.yml @@ -1,2 +1,2 @@ -label: "JupyterHub" +label: 'JupyterHub' position: 2 diff --git a/docs/docs/develop/_category_.yaml b/docs/docs/develop/_category_.yaml index c379266ad..eed1d4a03 100644 --- a/docs/docs/develop/_category_.yaml +++ b/docs/docs/develop/_category_.yaml @@ -1,2 +1,2 @@ -label: "Develop" +label: 'Develop' position: 2 diff --git a/docs/docs/develop/dev-environment/_category_.yml b/docs/docs/develop/dev-environment/_category_.yml index b64ba58f9..758cecea2 100644 --- a/docs/docs/develop/dev-environment/_category_.yml +++ b/docs/docs/develop/dev-environment/_category_.yml @@ -1,2 +1,2 @@ -label: "Development Environment" +label: 'Development Environment' position: 1 diff --git a/docs/docs/develop/dev-environment/conda/_category_.yml b/docs/docs/develop/dev-environment/conda/_category_.yml index ec0d89356..716eb1831 100644 --- a/docs/docs/develop/dev-environment/conda/_category_.yml +++ b/docs/docs/develop/dev-environment/conda/_category_.yml @@ -1,2 +1,2 @@ -label: "Conda" +label: 'Conda' position: 1 diff --git a/docs/docs/develop/dev-environment/devcontainers/_category_.yml b/docs/docs/develop/dev-environment/devcontainers/_category_.yml index 7050b6538..8083dfb03 100644 --- a/docs/docs/develop/dev-environment/devcontainers/_category_.yml +++ b/docs/docs/develop/dev-environment/devcontainers/_category_.yml @@ -1,2 +1,2 @@ -label: "Devcontainers" +label: 'Devcontainers' position: 2 diff --git a/docs/docs/develop/dev-environment/index.mdx b/docs/docs/develop/dev-environment/index.mdx index fefc85604..98f14979f 100644 --- a/docs/docs/develop/dev-environment/index.mdx +++ b/docs/docs/develop/dev-environment/index.mdx @@ -12,19 +12,20 @@ enableInlineBuilds: false enableTelemetry: false httpTimeout: 60000 nodeLinker: node-modules -npmRegistryServer: "https://registry.yarnpkg.com" +npmRegistryServer: 'https://registry.yarnpkg.com' # This will fix the build error with @lerna/legacy-package-management # See https://github.com/lerna/repro/pull/11 packageExtensions: - "@lerna/legacy-package-management@*": + '@lerna/legacy-package-management@*': dependencies: - "@lerna/child-process": "*" - "js-yaml": "*" - "rimraf": "*" + '@lerna/child-process': '*' + 'js-yaml': '*' + 'rimraf': '*' peerDependencies: - "nx": "*" + 'nx': '*' ``` + ::: Choose the way you want to develop Jupyter UI: diff --git a/docs/docs/develop/extension/_category_.yml b/docs/docs/develop/extension/_category_.yml index 52f93c48d..afa3f251d 100644 --- a/docs/docs/develop/extension/_category_.yml +++ b/docs/docs/develop/extension/_category_.yml @@ -1,2 +1,2 @@ -label: "JupyterLab Extension" +label: 'JupyterLab Extension' position: 4 diff --git a/docs/docs/develop/index.mdx b/docs/docs/develop/index.mdx index 6786c004e..df840d3b1 100644 --- a/docs/docs/develop/index.mdx +++ b/docs/docs/develop/index.mdx @@ -20,4 +20,4 @@ Environment is sensible to avoid build failures. ::: - + diff --git a/docs/docs/develop/webapp/_category_.yml b/docs/docs/develop/webapp/_category_.yml index 7f599f566..3190bcf3c 100644 --- a/docs/docs/develop/webapp/_category_.yml +++ b/docs/docs/develop/webapp/_category_.yml @@ -1,2 +1,2 @@ -label: "Web App" +label: 'Web App' position: 2 diff --git a/docs/docs/integrations/_category_.yaml b/docs/docs/integrations/_category_.yaml index 288fc2997..73abd4d5d 100644 --- a/docs/docs/integrations/_category_.yaml +++ b/docs/docs/integrations/_category_.yaml @@ -1,2 +1,2 @@ -label: "Integrations" +label: 'Integrations' position: 6 diff --git a/docs/docs/integrations/cra/_category_.yml b/docs/docs/integrations/cra/_category_.yml index 172b97da6..a986805b8 100644 --- a/docs/docs/integrations/cra/_category_.yml +++ b/docs/docs/integrations/cra/_category_.yml @@ -1,2 +1,2 @@ -label: "Create React App (Deprecated)" +label: 'Create React App (Deprecated)' position: 8 diff --git a/docs/docs/integrations/cra/index.mdx b/docs/docs/integrations/cra/index.mdx index 8053a444d..c9bbd87ae 100644 --- a/docs/docs/integrations/cra/index.mdx +++ b/docs/docs/integrations/cra/index.mdx @@ -75,13 +75,13 @@ npm install You need to add in the `` section of `public/index.html` the metadata to indicate where you Jupyter server is running, as the `require` javascript. ```html - - + + ``` ## Dot Env diff --git a/docs/docs/integrations/docusaurus/_category_.yml b/docs/docs/integrations/docusaurus/_category_.yml index d8e2faa37..a82fa1b83 100644 --- a/docs/docs/integrations/docusaurus/_category_.yml +++ b/docs/docs/integrations/docusaurus/_category_.yml @@ -1,2 +1,2 @@ -label: "Docusaurus" +label: 'Docusaurus' position: 4 diff --git a/docs/docs/integrations/docusaurus/index.mdx b/docs/docs/integrations/docusaurus/index.mdx index 912f07f2f..c25e9909d 100644 --- a/docs/docs/integrations/docusaurus/index.mdx +++ b/docs/docs/integrations/docusaurus/index.mdx @@ -2,7 +2,7 @@ description: Integration with Docusaurus. --- -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; # ๐Ÿช ๐Ÿฆ• Docusaurus diff --git a/docs/docs/integrations/index.mdx b/docs/docs/integrations/index.mdx index 393866c58..e4c6aff44 100644 --- a/docs/docs/integrations/index.mdx +++ b/docs/docs/integrations/index.mdx @@ -2,4 +2,4 @@ import DocCardList from '@theme/DocCardList'; # Integrations - + diff --git a/docs/docs/integrations/next-js/_category_.yml b/docs/docs/integrations/next-js/_category_.yml index 10b6de0a5..497180e80 100644 --- a/docs/docs/integrations/next-js/_category_.yml +++ b/docs/docs/integrations/next-js/_category_.yml @@ -1,2 +1,2 @@ -label: "Next.js" +label: 'Next.js' position: 3 diff --git a/docs/docs/integrations/next-js/index.mdx b/docs/docs/integrations/next-js/index.mdx index 5eb900ceb..53202a154 100644 --- a/docs/docs/integrations/next-js/index.mdx +++ b/docs/docs/integrations/next-js/index.mdx @@ -2,7 +2,7 @@ description: Example with Next.js. --- -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; # Next.js @@ -13,28 +13,27 @@ There are a couple of configuration and requirement to run Jupyter React in Next You need to render in client-mode the Jupyter part. To achieve this, you can e.g. load your page with external components dynamically loaded. ```js -import dynamic from 'next/dynamic' +import dynamic from 'next/dynamic'; -const HomePageNoSSR = dynamic( - () => import('../components/HomeComponent'), - { ssr: false } -); +const HomePageNoSSR = dynamic(() => import('../components/HomeComponent'), { + ssr: false, +}); function Home() { return ( <> - ) + ); } -export default Home +export default Home; ``` The components should ideally reside outside of the `app` folder. Add `'use client'` at the top of your React.js components. ```js -'use client' +'use client'; import { Jupyter, Notebook, CellSidebar } from '@datalayer/jupyter-react'; diff --git a/docs/docs/integrations/pyodide/_category_.yml b/docs/docs/integrations/pyodide/_category_.yml index 29abe385f..57b3be64b 100644 --- a/docs/docs/integrations/pyodide/_category_.yml +++ b/docs/docs/integrations/pyodide/_category_.yml @@ -1,2 +1,2 @@ -label: "Pyodide" +label: 'Pyodide' position: 4 diff --git a/docs/docs/integrations/ssr/_category_.yml b/docs/docs/integrations/ssr/_category_.yml index 6bac5b04a..9b5d0f0c7 100644 --- a/docs/docs/integrations/ssr/_category_.yml +++ b/docs/docs/integrations/ssr/_category_.yml @@ -1,2 +1,2 @@ -label: "Server Side Rendering" +label: 'Server Side Rendering' position: 5 diff --git a/docs/docs/integrations/vite/_category_.yml b/docs/docs/integrations/vite/_category_.yml index 1abf6e6c7..40d1cf9f5 100644 --- a/docs/docs/integrations/vite/_category_.yml +++ b/docs/docs/integrations/vite/_category_.yml @@ -1,2 +1,2 @@ -label: "Vite" +label: 'Vite' position: 1 diff --git a/docs/docs/integrations/vscode/_category_.yml b/docs/docs/integrations/vscode/_category_.yml index 4b380f562..e643aa750 100644 --- a/docs/docs/integrations/vscode/_category_.yml +++ b/docs/docs/integrations/vscode/_category_.yml @@ -1,2 +1,2 @@ -label: "VS Code" +label: 'VS Code' position: 6 diff --git a/docs/docs/integrations/vscode/index.mdx b/docs/docs/integrations/vscode/index.mdx index 48a1d6a2c..8936b5762 100644 --- a/docs/docs/integrations/vscode/index.mdx +++ b/docs/docs/integrations/vscode/index.mdx @@ -1,4 +1,4 @@ -import ModalImage from "react-modal-image"; +import ModalImage from 'react-modal-image'; # ๐Ÿšง VS Code @@ -31,7 +31,7 @@ Styling - No connection between dark/light vs code theme and notebook theme - No resizing with the panel -- Button to select the runtime is not displayed in a toolbar that stays visible - No connection between dark/light vs code theme and notebook theme +- Button to select the runtime is not displayed in a toolbar that stays visible - No connection between dark/light vs code theme and notebook theme - No resizing with the panel - Button to select the runtime is not displayed in a toolbar that stays visible diff --git a/docs/docs/integrations/webpack/_category_.yml b/docs/docs/integrations/webpack/_category_.yml index 294a6b7a3..9db255863 100644 --- a/docs/docs/integrations/webpack/_category_.yml +++ b/docs/docs/integrations/webpack/_category_.yml @@ -1,2 +1,2 @@ -label: "Webpack" +label: 'Webpack' position: 2 diff --git a/docs/docs/license/_category_.yml b/docs/docs/license/_category_.yml index 0460bf76a..c0cfb8279 100644 --- a/docs/docs/license/_category_.yml +++ b/docs/docs/license/_category_.yml @@ -1,2 +1,2 @@ -label: "โš–๏ธ MIT License" +label: 'โš–๏ธ MIT License' position: 12 diff --git a/docs/docs/state/_category_.yml b/docs/docs/state/_category_.yml index df2ac7806..3696a5ad2 100644 --- a/docs/docs/state/_category_.yml +++ b/docs/docs/state/_category_.yml @@ -1,2 +1,2 @@ -label: "State" +label: 'State' position: 4 diff --git a/docs/docs/support/_category_.yml b/docs/docs/support/_category_.yml index d5db3837c..fbbc79485 100644 --- a/docs/docs/support/_category_.yml +++ b/docs/docs/support/_category_.yml @@ -1,2 +1,2 @@ -label: "Support" +label: 'Support' position: 11 diff --git a/docs/docs/support/index.mdx b/docs/docs/support/index.mdx index ef4ff6f00..cb17210ea 100644 --- a/docs/docs/support/index.mdx +++ b/docs/docs/support/index.mdx @@ -2,7 +2,7 @@ description: Support --- -import { InlineWidget } from "react-calendly"; +import { InlineWidget } from 'react-calendly'; # Support diff --git a/docs/docs/themes/_category_.yaml b/docs/docs/themes/_category_.yaml index 8a2c21f8d..49d98f56b 100644 --- a/docs/docs/themes/_category_.yaml +++ b/docs/docs/themes/_category_.yaml @@ -1,2 +1,2 @@ -label: "Themes" +label: 'Themes' position: 8 diff --git a/docs/docs/themes/index.mdx b/docs/docs/themes/index.mdx index 58d9f9685..f91785df4 100644 --- a/docs/docs/themes/index.mdx +++ b/docs/docs/themes/index.mdx @@ -2,4 +2,4 @@ import DocCardList from '@theme/DocCardList'; # Themes - + diff --git a/docs/docs/themes/themes/_category_.yml b/docs/docs/themes/themes/_category_.yml index b4b01767a..17daddb55 100644 --- a/docs/docs/themes/themes/_category_.yml +++ b/docs/docs/themes/themes/_category_.yml @@ -1,2 +1,2 @@ -label: "Themes" +label: 'Themes' position: 1 diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 79db26c48..f7b259dd5 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -30,161 +30,159 @@ module.exports = { tsconfig: '../tsconfig.json', }, */ - themes: [ - '@docusaurus/theme-mermaid', - ], - themeConfig: { - colorMode: { - defaultMode: 'light', - disableSwitch: true, - }, - prism: { - additionalLanguages: ['bash'], - }, - liveCodeBlock: { - playgroundPosition: 'bottom', - }, - navbar: { - title: 'Jupyter UI', - logo: { - alt: 'Datalayer Logo', - src: 'img/datalayer/logo.svg', - }, - items: [ - { - type: 'doc', - docId: 'about/index', - position: 'left', - label: 'About', - }, - { - type: 'doc', - docId: 'develop/index', - position: 'left', - label: 'Develop', - }, - { - type: 'doc', - docId: 'components/index', - position: 'left', - label: 'Components', - }, - { - type: 'doc', - docId: 'state/index', - position: 'left', - label: 'State', - }, - { - type: 'doc', - docId: 'integrations/index', - position: 'left', - label: 'Integrations', - }, - { - type: 'doc', - docId: 'deployments/index', - position: 'left', - label: 'Deployments', - }, - { - type: 'doc', - docId: 'themes/index', - position: 'left', - label: 'Themes', - }, - { - type: 'doc', - docId: 'demos/index', - position: 'left', - label: 'Demos', - }, - { - type: 'doc', - docId: 'support/index', - position: 'left', - label: 'Support', - }, - { - type: 'doc', - docId: 'license/index', - position: 'left', - label: 'License', - }, - { - href: 'https://www.linkedin.com/company/datalayer', - position: 'right', - className: 'header-linkedin-link', - 'aria-label': 'LinkedIn', - }, - { - href: 'https://bsky.app/profile/datalayer.ai', - position: 'right', - className: 'header-bluesky-link', - 'aria-label': 'Bluesky', - }, - { - href: 'https://github.com/datalayer/jupyter-ui', - position: 'right', - className: 'header-github-link', - 'aria-label': 'GitHub', - }, - { - href: 'https://datalayer.tech', - position: 'right', - className: 'header-datalayer-io-link', - 'aria-label': 'Datalayer Tech', - }, - ], - }, - footer: { - style: 'dark', - links: [ - { - title: 'Community', - items: [ - { - label: 'GitHub', - href: 'https://github.com/datalayer', - }, - { - label: 'Bluesky', - href: 'https://assets.datalayer.tech/logos-social-grey/youtube.svg', - }, - { - label: 'LinkedIn', - href: 'https://www.linkedin.com/company/datalayer', - }, - ], - }, - { - title: 'More', - items: [ - { - label: 'Datalayer', - href: 'https://datalayer.io', - }, - { - label: 'Datalayer Docs', - href: 'https://docs.datalayer.app', - }, - { - label: 'Datalayer Tech', - href: 'https://datalayer.tech', - }, - { - label: 'Datalayer Guide', - href: 'https://datalayer.guide', - }, - { - label: 'Datalayer Blog', - href: 'https://datalayer.blog', - }, - ], - } - ], - copyright: `Copyright ยฉ ${new Date().getFullYear()} Datalayer, Inc.`, + themes: ['@docusaurus/theme-mermaid'], + themeConfig: { + colorMode: { + defaultMode: 'light', + disableSwitch: true, + }, + prism: { + additionalLanguages: ['bash'], + }, + liveCodeBlock: { + playgroundPosition: 'bottom', + }, + navbar: { + title: 'Jupyter UI', + logo: { + alt: 'Datalayer Logo', + src: 'img/datalayer/logo.svg', }, + items: [ + { + type: 'doc', + docId: 'about/index', + position: 'left', + label: 'About', + }, + { + type: 'doc', + docId: 'develop/index', + position: 'left', + label: 'Develop', + }, + { + type: 'doc', + docId: 'components/index', + position: 'left', + label: 'Components', + }, + { + type: 'doc', + docId: 'state/index', + position: 'left', + label: 'State', + }, + { + type: 'doc', + docId: 'integrations/index', + position: 'left', + label: 'Integrations', + }, + { + type: 'doc', + docId: 'deployments/index', + position: 'left', + label: 'Deployments', + }, + { + type: 'doc', + docId: 'themes/index', + position: 'left', + label: 'Themes', + }, + { + type: 'doc', + docId: 'demos/index', + position: 'left', + label: 'Demos', + }, + { + type: 'doc', + docId: 'support/index', + position: 'left', + label: 'Support', + }, + { + type: 'doc', + docId: 'license/index', + position: 'left', + label: 'License', + }, + { + href: 'https://www.linkedin.com/company/datalayer', + position: 'right', + className: 'header-linkedin-link', + 'aria-label': 'LinkedIn', + }, + { + href: 'https://bsky.app/profile/datalayer.ai', + position: 'right', + className: 'header-bluesky-link', + 'aria-label': 'Bluesky', + }, + { + href: 'https://github.com/datalayer/jupyter-ui', + position: 'right', + className: 'header-github-link', + 'aria-label': 'GitHub', + }, + { + href: 'https://datalayer.tech', + position: 'right', + className: 'header-datalayer-io-link', + 'aria-label': 'Datalayer Tech', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Community', + items: [ + { + label: 'GitHub', + href: 'https://github.com/datalayer', + }, + { + label: 'Bluesky', + href: 'https://assets.datalayer.tech/logos-social-grey/youtube.svg', + }, + { + label: 'LinkedIn', + href: 'https://www.linkedin.com/company/datalayer', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'Datalayer', + href: 'https://datalayer.io', + }, + { + label: 'Datalayer Docs', + href: 'https://docs.datalayer.app', + }, + { + label: 'Datalayer Tech', + href: 'https://datalayer.tech', + }, + { + label: 'Datalayer Guide', + href: 'https://datalayer.guide', + }, + { + label: 'Datalayer Blog', + href: 'https://datalayer.blog', + }, + ], + }, + ], + copyright: `Copyright ยฉ ${new Date().getFullYear()} Datalayer, Inc.`, + }, }, presets: [ [ @@ -192,13 +190,11 @@ module.exports = { { docs: { sidebarPath: require.resolve('./sidebars.js'), - editUrl: - 'https://github.com/datalayer/jupyter-ui/edit/main', + editUrl: 'https://github.com/datalayer/jupyter-ui/edit/main', }, blog: { showReadingTime: true, - editUrl: - 'https://datalayer.blog', + editUrl: 'https://datalayer.blog', }, theme: { customCss: require.resolve('./src/css/custom.css'), diff --git a/docs/sidebars.js b/docs/sidebars.js index 4b5848685..7298e72cd 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -24,7 +24,7 @@ const sidebars = { type: 'autogenerated', dirName: '.', }, - ] + ], }; module.exports = sidebars; diff --git a/docs/src/components/HomepageFeatures.js b/docs/src/components/HomepageFeatures.js index a7eadfa8c..3f69cf7d2 100644 --- a/docs/src/components/HomepageFeatures.js +++ b/docs/src/components/HomepageFeatures.js @@ -7,20 +7,31 @@ import React from 'react'; import clsx from 'clsx'; import styles from './HomepageFeatures.module.css'; +import TargetSvg from '../../static/img/target.svg'; const FeatureList = [ { title: 'Professional support', - Svg: require('../../static/img/target.svg').default, + Svg: TargetSvg, description: ( <> - Jupyter UI lets you focus on your work, and we'll do the chores. Check the support options. + Jupyter UI lets you focus on your work, and we'll do the chores. + Check the{' '} + + support options + + . ), }, ]; -function Feature({Svg, title, description}) { +function Feature({ Svg, title, description }) { return (
diff --git a/docs/src/components/HomepageProducts.js b/docs/src/components/HomepageProducts.js index 33c35969a..ef1d6c250 100644 --- a/docs/src/components/HomepageProducts.js +++ b/docs/src/components/HomepageProducts.js @@ -7,66 +7,93 @@ import React from 'react'; import clsx from 'clsx'; import styles from './HomepageProducts.module.css'; +import ReactJsSvg from '../../static/img/react-js.svg'; +import JupyterSvg from '../../static/img/jupyter.svg'; +import BricksSvg from '../../static/img/bricks.svg'; +import MemoSvg from '../../static/img/memo.svg'; +import RocketSvg from '../../static/img/rocket.svg'; +import OpenSourceSvg from '../../static/img/open-source.svg'; const ProductList = [ { title: 'React.js components', - Svg: require('../../static/img/react-js.svg').default, + Svg: ReactJsSvg, description: ( <> - A variety of React.js components from Notebook, Cell, Output and Terminal allow you to get the best of Jupyter, with authentication and authorization. + A variety of React.js components from Notebook, Cell, Output and + Terminal allow you to get the best of Jupyter, with authentication and + authorization. ), }, { title: '100% compatible with Jupyter', - Svg: require('../../static/img/jupyter.svg').default, + Svg: JupyterSvg, description: ( <> - If you need more batteries for Jupyter, have a look to our Jupyter components. + If you need more batteries for Jupyter, have a look to our{' '} + + Jupyter components + + . ), }, { title: 'Components with a Storybook', - Svg: require('../../static/img/bricks.svg').default, + Svg: BricksSvg, description: ( <> - You build your custom Data Product with well crafted Datalayer UI components. Have a look at the Storybook. + You build your custom Data Product with well crafted Datalayer UI + components. Have a look at the{' '} + + Storybook + + . ), }, { title: 'Literate Notebook', - Svg: require('../../static/img/memo.svg').default, + Svg: MemoSvg, description: ( <> - For a truly collaborative and accessible notebook, Literate Notebook is a better single-page editor for your data analysis. + For a truly collaborative and accessible notebook, Literate Notebook is + a better single-page editor for your data analysis. ), }, { title: 'Easy to use', - Svg: require('../../static/img/rocket.svg').default, + Svg: RocketSvg, description: ( <> - Juyter UI is designed from the ground up to be easily installed, used and extended - to get your custom data analysis up and running quickly. + Juyter UI is designed from the ground up to be easily installed, used + and extended to get your custom data analysis up and running quickly. ), }, { title: 'Open source', - Svg: require('../../static/img/open-source.svg').default, + Svg: OpenSourceSvg, description: ( <> - Jupyter UI is built on top of renowed open source libraries and is also fully opensource. + Jupyter UI is built on top of renowed open source libraries and is also + fully opensource. ), }, ]; -function Product({Svg, title, description}) { +function Product({ Svg, title, description }) { return (
diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index ed679bf7d..a3371b320 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -19,19 +19,21 @@ body { background-color: white !important; } -.hero__title, .hero__subtitle { +.hero__title, +.hero__subtitle { color: white !important; } .container { color: rgb(28, 30, 33) !important; -/* color: var(--ifm-heading-color) !important; */ + /* color: var(--ifm-heading-color) !important; */ } .theme-doc-markdown a { color: var(--ifm-link-color) !important; text-decoration: var(--ifm-link-decoration) !important; - transition: color var(--ifm-transition-fast) var(--ifm-transition-timing-default) !important; + transition: color var(--ifm-transition-fast) + var(--ifm-transition-timing-default) !important; } /* You can override the default Infima variables here. */ @@ -94,7 +96,7 @@ body { width: 24px; height: 24px; display: flex; - background: url("data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%0A%20%20%20width%3D%2224%22%0A%20%20%20height%3D%2224%22%0A%20%20%20viewBox%3D%220%200%202.88%202.88%22%0A%20%20%20version%3D%221.1%22%0A%20%20%20id%3D%22svg4%22%0A%20%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Cdefs%0A%20%20%20%20%20id%3D%22defs8%22%20%2F%3E%0A%20%20%3Cpath%0A%20%20%20%20%20fill%3D%22%23959da5%22%0A%20%20%20%20%20d%3D%22M%201.44%2C1.306859%20C%201.30956%2C1.053179%200.95447995%2C0.58049901%200.62423999%2C0.34745901%200.30791999%2C0.12413901%200.18732%2C0.16277901%200.10824%2C0.19865901%200.01668%2C0.23981901%200%2C0.38045901%200%2C0.46301901%20c%200%2C0.0828%200.04536%2C0.67799999%200.07488%2C0.77747999%200.0978%2C0.32832%200.44556%2C0.4392%200.76595999%2C0.4036799%200.01632%2C-0.0024%200.033%2C-0.00468%200.0498%2C-0.00672%20-0.01656%2C0.00264%20-0.03312%2C0.0048%20-0.0498%2C0.00672%20C%200.3714%2C1.7137789%20-0.0456%2C1.884779%200.50124%2C2.493539%201.1027999%2C3.1163391%201.3256399%2C2.359979%201.44%2C1.976579%201.55436%2C2.359979%201.686%2C3.0890991%202.36796%2C2.493539%202.88%2C1.976579%202.5086%2C1.713779%202.0391599%2C1.6441789%20a%201.04892%2C1.04892%200%200%201%20-0.0498%2C-0.00672%20c%200.0168%2C0.00204%200.03348%2C0.00432%200.0498%2C0.00672%20C%202.35956%2C1.6798189%202.7073199%2C1.5688189%202.80512%2C1.240499%202.8346401%2C1.141139%202.88%2C0.54569891%202.88%2C0.46313901%20c%200%2C-0.0828%20-0.01668%2C-0.22332%20-0.10824%2C-0.26472%20-0.07908%2C-0.03576%20-0.19968%2C-0.0744%20-0.516%2C0.1488%20C%201.92552%2C0.58061901%201.5704399%2C1.053299%201.44%2C1.306859%22%0A%20%20%20%20%20style%3D%22stroke-width%3A0.12%22%0A%20%20%20%20%20id%3D%22path2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A") + background: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%0A%20%20%20width%3D%2224%22%0A%20%20%20height%3D%2224%22%0A%20%20%20viewBox%3D%220%200%202.88%202.88%22%0A%20%20%20version%3D%221.1%22%0A%20%20%20id%3D%22svg4%22%0A%20%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Cdefs%0A%20%20%20%20%20id%3D%22defs8%22%20%2F%3E%0A%20%20%3Cpath%0A%20%20%20%20%20fill%3D%22%23959da5%22%0A%20%20%20%20%20d%3D%22M%201.44%2C1.306859%20C%201.30956%2C1.053179%200.95447995%2C0.58049901%200.62423999%2C0.34745901%200.30791999%2C0.12413901%200.18732%2C0.16277901%200.10824%2C0.19865901%200.01668%2C0.23981901%200%2C0.38045901%200%2C0.46301901%20c%200%2C0.0828%200.04536%2C0.67799999%200.07488%2C0.77747999%200.0978%2C0.32832%200.44556%2C0.4392%200.76595999%2C0.4036799%200.01632%2C-0.0024%200.033%2C-0.00468%200.0498%2C-0.00672%20-0.01656%2C0.00264%20-0.03312%2C0.0048%20-0.0498%2C0.00672%20C%200.3714%2C1.7137789%20-0.0456%2C1.884779%200.50124%2C2.493539%201.1027999%2C3.1163391%201.3256399%2C2.359979%201.44%2C1.976579%201.55436%2C2.359979%201.686%2C3.0890991%202.36796%2C2.493539%202.88%2C1.976579%202.5086%2C1.713779%202.0391599%2C1.6441789%20a%201.04892%2C1.04892%200%200%201%20-0.0498%2C-0.00672%20c%200.0168%2C0.00204%200.03348%2C0.00432%200.0498%2C0.00672%20C%202.35956%2C1.6798189%202.7073199%2C1.5688189%202.80512%2C1.240499%202.8346401%2C1.141139%202.88%2C0.54569891%202.88%2C0.46313901%20c%200%2C-0.0828%20-0.01668%2C-0.22332%20-0.10824%2C-0.26472%20-0.07908%2C-0.03576%20-0.19968%2C-0.0744%20-0.516%2C0.1488%20C%201.92552%2C0.58061901%201.5704399%2C1.053299%201.44%2C1.306859%22%0A%20%20%20%20%20style%3D%22stroke-width%3A0.12%22%0A%20%20%20%20%20id%3D%22path2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A') no-repeat; } @@ -103,7 +105,7 @@ body { } [data-theme='dark'] .header-bluesky-link::before { - background: url("data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%0A%20%20%20width%3D%2224%22%0A%20%20%20height%3D%2224%22%0A%20%20%20viewBox%3D%220%200%202.88%202.88%22%0A%20%20%20version%3D%221.1%22%0A%20%20%20id%3D%22svg4%22%0A%20%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Cdefs%0A%20%20%20%20%20id%3D%22defs8%22%20%2F%3E%0A%20%20%3Cpath%0A%20%20%20%20%20fill%3D%22%23959da5%22%0A%20%20%20%20%20d%3D%22M%201.44%2C1.306859%20C%201.30956%2C1.053179%200.95447995%2C0.58049901%200.62423999%2C0.34745901%200.30791999%2C0.12413901%200.18732%2C0.16277901%200.10824%2C0.19865901%200.01668%2C0.23981901%200%2C0.38045901%200%2C0.46301901%20c%200%2C0.0828%200.04536%2C0.67799999%200.07488%2C0.77747999%200.0978%2C0.32832%200.44556%2C0.4392%200.76595999%2C0.4036799%200.01632%2C-0.0024%200.033%2C-0.00468%200.0498%2C-0.00672%20-0.01656%2C0.00264%20-0.03312%2C0.0048%20-0.0498%2C0.00672%20C%200.3714%2C1.7137789%20-0.0456%2C1.884779%200.50124%2C2.493539%201.1027999%2C3.1163391%201.3256399%2C2.359979%201.44%2C1.976579%201.55436%2C2.359979%201.686%2C3.0890991%202.36796%2C2.493539%202.88%2C1.976579%202.5086%2C1.713779%202.0391599%2C1.6441789%20a%201.04892%2C1.04892%200%200%201%20-0.0498%2C-0.00672%20c%200.0168%2C0.00204%200.03348%2C0.00432%200.0498%2C0.00672%20C%202.35956%2C1.6798189%202.7073199%2C1.5688189%202.80512%2C1.240499%202.8346401%2C1.141139%202.88%2C0.54569891%202.88%2C0.46313901%20c%200%2C-0.0828%20-0.01668%2C-0.22332%20-0.10824%2C-0.26472%20-0.07908%2C-0.03576%20-0.19968%2C-0.0744%20-0.516%2C0.1488%20C%201.92552%2C0.58061901%201.5704399%2C1.053299%201.44%2C1.306859%22%0A%20%20%20%20%20style%3D%22stroke-width%3A0.12%22%0A%20%20%20%20%20id%3D%22path2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A") + background: url('data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%20standalone%3D%22no%22%3F%3E%0A%3Csvg%0A%20%20%20width%3D%2224%22%0A%20%20%20height%3D%2224%22%0A%20%20%20viewBox%3D%220%200%202.88%202.88%22%0A%20%20%20version%3D%221.1%22%0A%20%20%20id%3D%22svg4%22%0A%20%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0A%20%20%20xmlns%3Asvg%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Cdefs%0A%20%20%20%20%20id%3D%22defs8%22%20%2F%3E%0A%20%20%3Cpath%0A%20%20%20%20%20fill%3D%22%23959da5%22%0A%20%20%20%20%20d%3D%22M%201.44%2C1.306859%20C%201.30956%2C1.053179%200.95447995%2C0.58049901%200.62423999%2C0.34745901%200.30791999%2C0.12413901%200.18732%2C0.16277901%200.10824%2C0.19865901%200.01668%2C0.23981901%200%2C0.38045901%200%2C0.46301901%20c%200%2C0.0828%200.04536%2C0.67799999%200.07488%2C0.77747999%200.0978%2C0.32832%200.44556%2C0.4392%200.76595999%2C0.4036799%200.01632%2C-0.0024%200.033%2C-0.00468%200.0498%2C-0.00672%20-0.01656%2C0.00264%20-0.03312%2C0.0048%20-0.0498%2C0.00672%20C%200.3714%2C1.7137789%20-0.0456%2C1.884779%200.50124%2C2.493539%201.1027999%2C3.1163391%201.3256399%2C2.359979%201.44%2C1.976579%201.55436%2C2.359979%201.686%2C3.0890991%202.36796%2C2.493539%202.88%2C1.976579%202.5086%2C1.713779%202.0391599%2C1.6441789%20a%201.04892%2C1.04892%200%200%201%20-0.0498%2C-0.00672%20c%200.0168%2C0.00204%200.03348%2C0.00432%200.0498%2C0.00672%20C%202.35956%2C1.6798189%202.7073199%2C1.5688189%202.80512%2C1.240499%202.8346401%2C1.141139%202.88%2C0.54569891%202.88%2C0.46313901%20c%200%2C-0.0828%20-0.01668%2C-0.22332%20-0.10824%2C-0.26472%20-0.07908%2C-0.03576%20-0.19968%2C-0.0744%20-0.516%2C0.1488%20C%201.92552%2C0.58061901%201.5704399%2C1.053299%201.44%2C1.306859%22%0A%20%20%20%20%20style%3D%22stroke-width%3A0.12%22%0A%20%20%20%20%20id%3D%22path2%22%20%2F%3E%0A%3C%2Fsvg%3E%0A') no-repeat; } diff --git a/docs/src/index.ts b/docs/src/index.ts index f47f9bd36..9ab0e630e 100644 --- a/docs/src/index.ts +++ b/docs/src/index.ts @@ -3,4 +3,3 @@ * * MIT License */ - diff --git a/docs/src/pages/index.js b/docs/src/pages/index.js index 71fead8b9..3c17e237e 100644 --- a/docs/src/pages/index.js +++ b/docs/src/pages/index.js @@ -14,7 +14,7 @@ import HomepageFeatures from '../components/HomepageFeatures'; import HomepageProducts from '../components/HomepageProducts'; function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return (
@@ -23,7 +23,8 @@ function HomepageHeader() {
+ to="/docs/about" + > ๐Ÿš€ Get Started
@@ -33,14 +34,15 @@ function HomepageHeader() { } export default function Home() { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return ( + description="Jupyter UI, by Datalayer" + >
-
+
diff --git a/docs/src/theme/ReactLiveScope/index.tsx b/docs/src/theme/ReactLiveScope/index.tsx index e1b1ff134..6d1942ec8 100644 --- a/docs/src/theme/ReactLiveScope/index.tsx +++ b/docs/src/theme/ReactLiveScope/index.tsx @@ -11,11 +11,16 @@ import { ContentLoader } from '@datalayer/primer-addons'; const Cell = (props: any) => { return ( Jupyter Cell fallback content for prerendering.
}> + fallback={
Jupyter Cell fallback content for prerendering.
} + > {() => { // Keep the import via require in the BrowserOnly code block. - const { Jupyter } = require('@datalayer/jupyter-react/lib/jupyter/Jupyter'); - const { Cell } = require('@datalayer/jupyter-react/lib/components/cell/Cell'); + const { + Jupyter, + } = require('@datalayer/jupyter-react/lib/jupyter/Jupyter'); + const { + Cell, + } = require('@datalayer/jupyter-react/lib/components/cell/Cell'); return ( <> { jupyterServerToken="60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6" disableCssLoading={true} starDefaultKernel - skeleton={} + skeleton={} > - + - ) - }} + ); + }} - ) -} + ); +}; // Add react-live imports you need here const ReactLiveScope = { diff --git a/docs/src/types.ts b/docs/src/types.ts index f47f9bd36..9ab0e630e 100644 --- a/docs/src/types.ts +++ b/docs/src/types.ts @@ -3,4 +3,3 @@ * * MIT License */ - diff --git a/docs/tsconfig.json b/docs/tsconfig.json index d67893242..bde71c78a 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -6,10 +6,7 @@ "outDir": "lib", "target": "ES2022", "module": "commonjs", - "lib": [ - "ESNext", - "DOM" - ], + "lib": ["ESNext", "DOM"], "declaration": true, "declarationMap": false, "jsx": "react", @@ -33,9 +30,5 @@ "importHelpers": true, "noEmitHelpers": true }, - "exclude": [ - "node_modules", - "**/__tests__/**/*", - "**/lib/**/*" - ] + "exclude": ["node_modules", "**/__tests__/**/*", "**/lib/**/*"] } diff --git a/environment.yml b/environment.yml index c21184c3d..fee4756bf 100644 --- a/environment.yml +++ b/environment.yml @@ -14,4 +14,4 @@ dependencies: - build - hatch - pip: - - jupyterlab==4.1.0 + - jupyterlab==4.1.0 diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..ccac8941e --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ + +const js = require('@eslint/js'); +const tseslint = require('typescript-eslint'); +const reactPlugin = require('eslint-plugin-react'); +const reactHooksPlugin = require('eslint-plugin-react-hooks'); +const prettierPlugin = require('eslint-plugin-prettier'); +const prettierConfig = require('eslint-config-prettier'); + +module.exports = tseslint.config( + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{js,jsx,ts,tsx}'], + plugins: { + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + prettier: prettierPlugin, + }, + languageOptions: { + ecmaVersion: 2020, + sourceType: 'module', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + globals: { + window: 'readonly', + document: 'readonly', + console: 'readonly', + process: 'readonly', + Buffer: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + global: 'readonly', + setImmediate: 'readonly', + clearImmediate: 'readonly', + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + rules: { + ...reactPlugin.configs.recommended.rules, + ...reactHooksPlugin.configs.recommended.rules, + ...prettierConfig.rules, + + // TypeScript specific rules + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-non-null-assertion': 'warn', + '@typescript-eslint/no-namespace': 'off', + + // React specific rules + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', + 'react/display-name': 'off', + 'react/no-unescaped-entities': 'warn', + + // General rules + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'no-debugger': 'warn', + 'prefer-const': 'warn', + 'no-var': 'error', + + // Prettier integration + 'prettier/prettier': 'warn', + }, + }, + { + // Configuration for Node.js files (config files) + files: [ + '**/*.config.js', + '**/*.config.ts', + '.commitlintrc.js', + '.eslintrc.js', + '.lintstagedrc.js', + '**/gulpfile.js', + '**/webpack.config.js', + '**/sidebars.js', + '**/docusaurus.config.js', + '**/babel.config.js', + '**/jest.config.js', + '**/prettier.config.js', + '**/test-runner-jest.config.js', + '**/test-storybook.js', + '**/copyUntypedFiles.js', + '**/helper.js', + '**/extension.js', + ], + languageOptions: { + sourceType: 'commonjs', + globals: { + module: 'writable', + exports: 'writable', + require: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + process: 'readonly', + console: 'readonly', + }, + }, + rules: { + '@typescript-eslint/no-require-imports': 'off', + }, + }, + { + // Allow require() in Docusaurus theme files for SSR compatibility + files: [ + '**/packages/docusaurus-plugin/src/theme/**/*.tsx', + '**/docs/src/theme/**/*.tsx', + ], + rules: { + '@typescript-eslint/no-require-imports': 'off', + 'no-undef': 'off', + }, + }, + { + ignores: [ + '**/node_modules/**', + '**/dist/**', + '**/build/**', + '**/lib/**', + '**/*.min.js', + '**/coverage/**', + '**/.next/**', + '**/storybook-static/**', + '**/.husky/**', + '**/patches/**', + '**/*.patch', + 'attic/**', + '**/.docusaurus/**', + '**/emptyshim.js', + '**/empty-shim.js', + 'packages/ipyreactive/**', + 'packages/ipyscript/**', + ], + }, +); diff --git a/examples/README.md b/examples/README.md index 2330cef41..a4e0b4a4e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,4 +1,3 @@ - [![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io) [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer) diff --git a/examples/docusaurus/README.md b/examples/docusaurus/README.md index 0101abb25..93b56fbbc 100644 --- a/examples/docusaurus/README.md +++ b/examples/docusaurus/README.md @@ -29,12 +29,12 @@ Add the following code in any Markdown file. ```jsx import JupyterCell from '@theme/JupyterCell'; - + jupyterServerUrl="http://localhost:8686/api/jupyter-server" + jupyterServerToken="60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6" +/>; ``` diff --git a/examples/docusaurus/docs/intro.mdx b/examples/docusaurus/docs/intro.mdx index 782ef077b..f53e80c92 100644 --- a/examples/docusaurus/docs/intro.mdx +++ b/examples/docusaurus/docs/intro.mdx @@ -5,8 +5,8 @@ --> --- -sidebar_position: 1 ---- + +## sidebar_position: 1 import JupyterCell from '@theme/JupyterCell'; @@ -15,7 +15,7 @@ import JupyterCell from '@theme/JupyterCell'; Let's discover **Docusaurus in less than 5 minutes**. + jupyterServerUrl="http://localhost:8686/api/jupyter-server" + jupyterServerToken="60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6" +/> ## Getting Started diff --git a/examples/docusaurus/docs/tutorial-basics/markdown-features.mdx b/examples/docusaurus/docs/tutorial-basics/markdown-features.mdx index e0f26bedc..ec8e044a3 100644 --- a/examples/docusaurus/docs/tutorial-basics/markdown-features.mdx +++ b/examples/docusaurus/docs/tutorial-basics/markdown-features.mdx @@ -5,8 +5,8 @@ --> --- -sidebar_position: 4 ---- + +## sidebar_position: 4 # Markdown Features @@ -129,7 +129,7 @@ This is Docusaurus green ! This is Facebook blue ! ``` -export const Highlight = ({children, color}) => ( +export const Highlight = ({ children, color }) => ( ( }} onClick={() => { alert(`You clicked the color ${color} with label ${children}`); - }}> + }} + > {children} ); diff --git a/examples/docusaurus/docusaurus.config.js b/examples/docusaurus/docusaurus.config.js index e3687f0d6..6586c8304 100644 --- a/examples/docusaurus/docusaurus.config.js +++ b/examples/docusaurus/docusaurus.config.js @@ -6,7 +6,7 @@ const lightCodeTheme = require('prism-react-renderer/themes/github'); const darkCodeTheme = require('prism-react-renderer/themes/dracula'); -const path = require('path') +const path = require('path'); /** @type {import('@docusaurus/types').DocusaurusConfig} */ module.exports = { @@ -17,9 +17,7 @@ module.exports = { onBrokenLinks: 'throw', onBrokenMarkdownLinks: 'warn', favicon: 'img/favicon.ico', - plugins: [ - '@datalayer/jupyter-docusaurus-plugin' - ], + plugins: ['@datalayer/jupyter-docusaurus-plugin'], organizationName: 'datalayer', // Usually your GitHub org/user name. projectName: 'docusaurus', // Usually your repo name. themeConfig: { @@ -36,7 +34,7 @@ module.exports = { position: 'left', label: 'Tutorial', }, - {to: '/blog', label: 'Blog', position: 'left'}, + { to: '/blog', label: 'Blog', position: 'left' }, { href: 'https://github.com/datalayer/jupyter-ui', label: 'GitHub', diff --git a/examples/docusaurus/sidebars.js b/examples/docusaurus/sidebars.js index 6ac3038ad..14af8f303 100644 --- a/examples/docusaurus/sidebars.js +++ b/examples/docusaurus/sidebars.js @@ -17,7 +17,7 @@ module.exports = { // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], // But you can create a sidebar manually /* diff --git a/examples/docusaurus/src/components/HomepageFeatures.js b/examples/docusaurus/src/components/HomepageFeatures.js index 0a3db5501..507f24155 100644 --- a/examples/docusaurus/src/components/HomepageFeatures.js +++ b/examples/docusaurus/src/components/HomepageFeatures.js @@ -7,11 +7,14 @@ import React from 'react'; import clsx from 'clsx'; import styles from './HomepageFeatures.module.css'; +import DocusaurusMountainSvg from '../../static/img/undraw_docusaurus_mountain.svg'; +import DocusaurusTreeSvg from '../../static/img/undraw_docusaurus_tree.svg'; +import DocusaurusReactSvg from '../../static/img/undraw_docusaurus_react.svg'; const FeatureList = [ { title: 'Easy to Use', - Svg: require('../../static/img/undraw_docusaurus_mountain.svg').default, + Svg: DocusaurusMountainSvg, description: ( <> Docusaurus was designed from the ground up to be easily installed and @@ -21,7 +24,7 @@ const FeatureList = [ }, { title: 'Focus on What Matters', - Svg: require('../../static/img/undraw_docusaurus_tree.svg').default, + Svg: DocusaurusTreeSvg, description: ( <> Docusaurus lets you focus on your docs, and we'll do the chores. Go @@ -31,7 +34,7 @@ const FeatureList = [ }, { title: 'Powered by React', - Svg: require('../../static/img/undraw_docusaurus_react.svg').default, + Svg: DocusaurusReactSvg, description: ( <> Extend or customize your website layout by reusing React. Docusaurus can @@ -41,7 +44,7 @@ const FeatureList = [ }, ]; -function Feature({Svg, title, description}) { +function Feature({ Svg, title, description }) { return (
diff --git a/examples/docusaurus/src/css/custom.css b/examples/docusaurus/src/css/custom.css index 024e1fa4c..bfcd16754 100644 --- a/examples/docusaurus/src/css/custom.css +++ b/examples/docusaurus/src/css/custom.css @@ -19,9 +19,15 @@ body { background-color: white !important; } -h1, h2, h3, h4, h5, h6, p { +h1, +h2, +h3, +h4, +h5, +h6, +p { color: rgb(28, 30, 33) !important; -/* color: var(--ifm-heading-color) !important; */ + /* color: var(--ifm-heading-color) !important; */ } /* You can override the default Infima variables here. */ diff --git a/examples/docusaurus/src/pages/index.js b/examples/docusaurus/src/pages/index.js index 7bc5d8f6f..05609bd32 100644 --- a/examples/docusaurus/src/pages/index.js +++ b/examples/docusaurus/src/pages/index.js @@ -13,7 +13,7 @@ import styles from './index.module.css'; import HomepageFeatures from '../components/HomepageFeatures'; function HomepageHeader() { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return (
@@ -22,7 +22,8 @@ function HomepageHeader() {
+ to="/docs/intro" + > Docusaurus Tutorial - 5min โฑ๏ธ
@@ -32,11 +33,12 @@ function HomepageHeader() { } export default function Home() { - const {siteConfig} = useDocusaurusContext(); + const { siteConfig } = useDocusaurusContext(); return ( + description="Description will go into a meta tag in " + >
diff --git a/examples/lexical/public/index.html b/examples/lexical/public/index.html index 6ca96c8ee..ad0be0e6b 100644 --- a/examples/lexical/public/index.html +++ b/examples/lexical/public/index.html @@ -4,7 +4,7 @@ ~ MIT License --> - + Jupyter UI for Lexical Example @@ -14,7 +14,11 @@ "jupyterServerToken": "60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6" } - + diff --git a/examples/lexical/src/AppNbformat.tsx b/examples/lexical/src/AppNbformat.tsx index 0129f8fc0..5e166436b 100644 --- a/examples/lexical/src/AppNbformat.tsx +++ b/examples/lexical/src/AppNbformat.tsx @@ -4,51 +4,66 @@ * MIT License */ -import { useState } from "react"; -import { $getRoot } from "lexical"; -import styled from "styled-components"; -import { lexicalToNbformat,useLexical, LexicalProvider, Editor } from "@datalayer/jupyter-lexical"; -import { useNotebookStore, Jupyter, Notebook, CellSidebar, CellSidebarExtension } from "@datalayer/jupyter-react"; -import { Box, Button, UnderlineNav } from "@primer/react"; -import { ThreeBarsIcon } from "@primer/octicons-react" -import { JSONTree } from "react-json-tree"; -import { INotebookContent } from "@jupyterlab/nbformat"; -import { INotebookModel } from "@jupyterlab/notebook"; +import { useState } from 'react'; +import { $getRoot } from 'lexical'; +import styled from 'styled-components'; +import { + lexicalToNbformat, + useLexical, + LexicalProvider, + Editor, +} from '@datalayer/jupyter-lexical'; +import { + useNotebookStore, + Jupyter, + Notebook, + CellSidebar, + CellSidebarExtension, +} from '@datalayer/jupyter-react'; +import { Box, Button, UnderlineNav } from '@primer/react'; +import { ThreeBarsIcon } from '@primer/octicons-react'; +import { JSONTree } from 'react-json-tree'; +import { INotebookContent } from '@jupyterlab/nbformat'; +import { INotebookModel } from '@jupyterlab/notebook'; import './../style/index.css'; import '@datalayer/jupyter-lexical/style/index.css'; -import INITIAL_LEXICAL_MODEL from "./content/Example.lexical.json"; +import INITIAL_LEXICAL_MODEL from './content/Example.lexical.json'; -import INITIAL_NBFORMAT_MODEL from "./content/Example.ipynb.json"; +import INITIAL_NBFORMAT_MODEL from './content/Example.ipynb.json'; const NOTEBOOK_UID = 'notebook-uid-lexical'; -type TabType = 'editor' - | 'notebook' - | 'nbformat'; +type TabType = 'editor' | 'notebook' | 'nbformat'; const StyledNotebook = styled.div` &[style] { height: 100vh !important; } -` +`; const Tabs = () => { const { editor } = useLexical(); const notebookStore = useNotebookStore(); const [tab, setTab] = useState('editor'); - const [notebookContent, setNotebookContent] = useState(INITIAL_NBFORMAT_MODEL); + const [notebookContent, setNotebookContent] = useState( + INITIAL_NBFORMAT_MODEL, + ); const notebook = notebookStore.selectNotebook(NOTEBOOK_UID); - const goToTab = (e: any, toTab: TabType, notebookModel: INotebookModel | undefined) => { + const goToTab = ( + e: any, + toTab: TabType, + notebookModel: INotebookModel | undefined, + ) => { e.preventDefault(); if (tab === 'notebook' && toTab === 'editor') { if (notebookModel && editor) { setNotebookContent(notebookModel.toJSON() as INotebookContent); } } - if (tab === 'editor' && toTab === "notebook") { + if (tab === 'editor' && toTab === 'notebook') { editor?.update(() => { const root = $getRoot(); const children = root.getChildren(); @@ -62,36 +77,51 @@ const Tabs = () => { } } setTab(toTab); - } + }; return ( - goToTab(e, 'editor', notebook?.model)}> + goToTab(e, 'editor', notebook?.model)} + > Editor - goToTab(e, 'notebook', notebook?.model)}> + goToTab(e, 'notebook', notebook?.model)} + > Notebook - goToTab(e, 'nbformat', notebook?.model)}> + goToTab(e, 'nbformat', notebook?.model)} + > NbFormat - { tab === 'editor' && + {tab === 'editor' && ( - } - { tab === 'notebook' && + )} + {tab === 'notebook' && ( { onClick={(e: React.MouseEvent) => { e.preventDefault(); setNotebookContent(INITIAL_NBFORMAT_MODEL); - }}> - Reset Nbformat + }} + > + Reset Nbformat - } - { tab === 'nbformat' && + )} + {tab === 'nbformat' && ( - ; + ; - } + )} - ) -} + ); +}; export function AppNbformat() { return ( @@ -126,25 +157,34 @@ export function AppNbformat() {
- + - ) + ); } export default AppNbformat; diff --git a/examples/lexical/src/content/Example.ipynb.json b/examples/lexical/src/content/Example.ipynb.json index aa25371c8..2bb297099 100644 --- a/examples/lexical/src/content/Example.ipynb.json +++ b/examples/lexical/src/content/Example.ipynb.json @@ -1,98 +1,89 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "3ce565a1-7756-4fee-af81-32682a8eb1b3", - "metadata": {}, - "source": [ - "# Hello Lexical" - ] - }, - { - "cell_type": "markdown", - "id": "23622f4a-62c9-4915-87b4-af1f4d6e30ec", - "metadata": {}, - "source": [ - "The Receiver Operating Characteristic \"ROC\" illustrates the performance of the binary classifier by plotting the false alarm probability ($P_{FA}$) on the horizontal axis and the detection probability ($P_D$) on the vertical axis. The area under the ROC-curve (AUC) is used to provide a single-figure quantification of the performance of the binary classifier based on the ROC. This article provides an interpretation of the AUC and connects the AUC to the Wilcoxonโ€“Mannโ€“Whitney statistic, which is a nonparametic test to compare two populations. This derivation is pretty hard to find but the connection between the AUC and the Wilcoxonโ€“Mannโ€“Whitney test is important insofar as it provides some perspective on the seemingly arbitrary use of AUC to quantify the performance of the binary classifier.\n", - "\n", - "Let's proceed with some definitions and some notation. A binary classifier can make two kinds of mistakes, usually and unhelpfully called Type I and Type II errors. The *detection probability* is \n", - "\n", - "$$P_D= \\mathbb{P}(1|x \\in C_1)$$" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "5d1c1fb7-59b6-4d52-a9e2-6294ddc9758a", - "metadata": {}, - "outputs": [ + "cells": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n" - ] - } - ], - "source": [ - "x=1\n", - "print(x)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "1fcf3d25-cb81-4977-9cc5-91855f3c1b28", - "metadata": {}, - "outputs": [ + "cell_type": "markdown", + "id": "3ce565a1-7756-4fee-af81-32682a8eb1b3", + "metadata": {}, + "source": ["# Hello Lexical"] + }, + { + "cell_type": "markdown", + "id": "23622f4a-62c9-4915-87b4-af1f4d6e30ec", + "metadata": {}, + "source": [ + "The Receiver Operating Characteristic \"ROC\" illustrates the performance of the binary classifier by plotting the false alarm probability ($P_{FA}$) on the horizontal axis and the detection probability ($P_D$) on the vertical axis. The area under the ROC-curve (AUC) is used to provide a single-figure quantification of the performance of the binary classifier based on the ROC. This article provides an interpretation of the AUC and connects the AUC to the Wilcoxonโ€“Mannโ€“Whitney statistic, which is a nonparametic test to compare two populations. This derivation is pretty hard to find but the connection between the AUC and the Wilcoxonโ€“Mannโ€“Whitney test is important insofar as it provides some perspective on the seemingly arbitrary use of AUC to quantify the performance of the binary classifier.\n", + "\n", + "Let's proceed with some definitions and some notation. A binary classifier can make two kinds of mistakes, usually and unhelpfully called Type I and Type II errors. The *detection probability* is \n", + "\n", + "$$P_D= \\mathbb{P}(1|x \\in C_1)$$" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5d1c1fb7-59b6-4d52-a9e2-6294ddc9758a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": ["1\n"] + } + ], + "source": ["x=1\n", "print(x)"] + }, { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" + "cell_type": "code", + "execution_count": 4, + "id": "1fcf3d25-cb81-4977-9cc5-91855f3c1b28", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": ["
"] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "plt.plot([1, 2, 3, 4])\n", + "plt.ylabel('y=x')\n", + "plt.show()" ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c7e5a40-5092-4ae7-98ea-e140da4f4328", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "plt.plot([1, 2, 3, 4])\n", - "plt.ylabel('y=x')\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8c7e5a40-5092-4ae7-98ea-e140da4f4328", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/examples/lexical/src/content/Example.lexical.json b/examples/lexical/src/content/Example.lexical.json index b527662dd..556e58211 100644 --- a/examples/lexical/src/content/Example.lexical.json +++ b/examples/lexical/src/content/Example.lexical.json @@ -256,4 +256,4 @@ "type": "root", "version": 1 } -} \ No newline at end of file +} diff --git a/examples/lexical/src/index.tsx b/examples/lexical/src/index.tsx index 27b65bd7f..1fb21e0c6 100644 --- a/examples/lexical/src/index.tsx +++ b/examples/lexical/src/index.tsx @@ -4,9 +4,9 @@ * MIT License */ -import { createRoot } from "react-dom/client"; -import { AppNbformat } from "./AppNbformat"; +import { createRoot } from 'react-dom/client'; +import { AppNbformat } from './AppNbformat'; -const rootElement = document.getElementById("root"); +const rootElement = document.getElementById('root'); createRoot(rootElement!).render(); diff --git a/examples/lexical/src/typings.d.ts b/examples/lexical/src/typings.d.ts index f6989cc55..4cb6d6d6d 100755 --- a/examples/lexical/src/typings.d.ts +++ b/examples/lexical/src/typings.d.ts @@ -4,22 +4,22 @@ * MIT License */ -declare module "*.jpg" { +declare module '*.jpg' { const value: any; export default value; } -declare module "*.jpeg" { +declare module '*.jpeg' { const value: any; export default value; } -declare module "*.png" { +declare module '*.png' { const value: any; export default value; } -declare module "*.svg" { +declare module '*.svg' { const value: any; export default value; } diff --git a/examples/lexical/tsconfig.json b/examples/lexical/tsconfig.json index 692dfff1e..787b2986c 100644 --- a/examples/lexical/tsconfig.json +++ b/examples/lexical/tsconfig.json @@ -1,16 +1,11 @@ { - "include": [ - "./src/**/*" - ], + "include": ["./src/**/*"], "compilerOptions": { "forceConsistentCasingInFileNames": true, "strict": true, "downlevelIteration": true, "esModuleInterop": true, - "lib": [ - "dom", - "es2021", - ], + "lib": ["dom", "es2021"], "jsx": "react-jsx", "target": "ES2022", "allowJs": true, diff --git a/examples/lexical/webpack.config.js b/examples/lexical/webpack.config.js index 2d7d34328..06832f628 100644 --- a/examples/lexical/webpack.config.js +++ b/examples/lexical/webpack.config.js @@ -4,25 +4,25 @@ * MIT License */ -const webpack = require("webpack"); -const path = require("path"); +const webpack = require('webpack'); +const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin'); -const shimJS = path.resolve(__dirname, "src", "emptyshim.js"); +const shimJS = path.resolve(__dirname, 'src', 'emptyshim.js'); function shim(regExp) { return new webpack.NormalModuleReplacementPlugin(regExp, shimJS); } const IS_PRODUCTION = process.argv.indexOf('--mode=production') > -1; -let mode = "development"; +let mode = 'development'; if (IS_PRODUCTION) { - mode = "production"; + mode = 'production'; } -let devtool = "inline-source-map"; +let devtool = 'inline-source-map'; if (IS_PRODUCTION) { devtool = false; } @@ -38,7 +38,7 @@ module.exports = { watchOptions: { aggregateTimeout: 300, poll: 5000, // Seems to stabilise HMR file change detection. - ignored: "/node_modules/" + ignored: '/node_modules/', }, devServer: { port: 3208, @@ -53,36 +53,36 @@ module.exports = { minimize, }, output: { - publicPath: "http://localhost:3208/", + publicPath: 'http://localhost:3208/', filename: '[name].[contenthash].jupyterReactLexicalExample.js', }, resolve: { - extensions: [ '.tsx', '.ts', 'jsx', '.js' ], - alias: { - "stream": "stream-browserify", + extensions: ['.tsx', '.ts', 'jsx', '.js'], + alias: { + stream: 'stream-browserify', + }, + fallback: { + assert: require.resolve('assert/'), }, - fallback: { - "assert": require.resolve("assert/"), - } }, module: { rules: [ { test: /\.tsx?$/, - loader: "babel-loader", + loader: 'babel-loader', options: { - plugins: [ - "@babel/plugin-proposal-class-properties", - ], + plugins: ['@babel/plugin-proposal-class-properties'], presets: [ - ["@babel/preset-react", { + [ + '@babel/preset-react', + { runtime: 'automatic', - importSource: 'react' + importSource: 'react', }, ], - "@babel/preset-typescript", + '@babel/preset-typescript', ], - cacheDirectory: true + cacheDirectory: true, }, exclude: /node_modules/, }, @@ -119,20 +119,20 @@ module.exports = { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, issuer: /\.js$/, use: { - loader: 'raw-loader' - } + loader: 'raw-loader', + }, }, { test: /\.m?js/, resolve: { - fullySpecified: false - } + fullySpecified: false, + }, }, { test: /\.c?js/, resolve: { - fullySpecified: false - } + fullySpecified: false, + }, }, // Special webpack rule for the JupyterLab theme style sheets. { @@ -163,16 +163,16 @@ module.exports = { filename: 'schema/[name][ext][query]', }, }, - ] + ], }, plugins: [ shim(/@fortawesome/), new webpack.ProvidePlugin({ - process: 'process/browser' + process: 'process/browser', }), new HtmlWebpackPlugin({ title: 'Jupyter UI', - template : 'public/index.html' + template: 'public/index.html', }), new HtmlWebpackTagsPlugin({ links: [ @@ -180,10 +180,10 @@ module.exports = { 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap', ], tags: [ -// 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js' + // 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js' ], - append: false, - publicPath: false + append: false, + publicPath: false, }), - ] + ], }; diff --git a/examples/next-js/README.md b/examples/next-js/README.md index 2d6db3acb..29955c06e 100644 --- a/examples/next-js/README.md +++ b/examples/next-js/README.md @@ -51,7 +51,7 @@ index 912458d..5edaa11 100644 @@ -24,4 +24,4 @@ export * from './widget_string'; export * from './widget_description'; export * from './widget_upload'; - + -export const version = (require('../package.json') as any).version; +export const version = "5.0.12"; ``` diff --git a/examples/next-js/next.config.ts b/examples/next-js/next.config.ts index 45470a600..055cac1fa 100644 --- a/examples/next-js/next.config.ts +++ b/examples/next-js/next.config.ts @@ -19,23 +19,20 @@ const nextConfig = { config.plugins.push( new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], - }) + }), ); // Fix json5 import issue for JupyterLab packages config.resolve.alias = { ...config.resolve.alias, - 'json5': require.resolve('json5/lib/index.js'), + json5: require.resolve('json5/lib/index.js'), '~': path.resolve(__dirname, 'node_modules'), }; // Add a plugin to strip `~` from import paths config.plugins.push( - new webpack.NormalModuleReplacementPlugin( - /^~(.*)/, - (resource: any) => { - resource.request = resource.request.replace(/^~/, ''); - }, - ), - ); + new webpack.NormalModuleReplacementPlugin(/^~(.*)/, (resource: any) => { + resource.request = resource.request.replace(/^~/, ''); + }), + ); config.module.rules.push( { test: /\.js.map$/, type: 'asset/resource' }, { @@ -85,9 +82,9 @@ const nextConfig = { filename: 'schema/[name][ext][query]', }, }, - ) - return config + ); + return config; }, -} +}; module.exports = nextConfig; diff --git a/examples/next-js/src/app/globals.css b/examples/next-js/src/app/globals.css index f29fc321a..63e2014e5 100644 --- a/examples/next-js/src/app/globals.css +++ b/examples/next-js/src/app/globals.css @@ -4,7 +4,7 @@ * MIT License */ -@import "tailwindcss"; +@import 'tailwindcss'; :root { --background: #ffffff; diff --git a/examples/next-js/src/app/layout.tsx b/examples/next-js/src/app/layout.tsx index b60558815..ee65e24f0 100644 --- a/examples/next-js/src/app/layout.tsx +++ b/examples/next-js/src/app/layout.tsx @@ -4,23 +4,23 @@ * MIT License */ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from 'next'; +import { Geist, Geist_Mono } from 'next/font/google'; +import './globals.css'; const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], + variable: '--font-geist-sans', + subsets: ['latin'], }); const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], + variable: '--font-geist-mono', + subsets: ['latin'], }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Create Next App', + description: 'Generated by create next app', }; export default function RootLayout({ diff --git a/examples/next-js/src/app/page.tsx b/examples/next-js/src/app/page.tsx index 7707992a5..766571057 100644 --- a/examples/next-js/src/app/page.tsx +++ b/examples/next-js/src/app/page.tsx @@ -6,21 +6,21 @@ 'use client'; -import Image from "next/image"; +import Image from 'next/image'; import dynamic from 'next/dynamic'; const JupyterComponentNoSSR = dynamic( () => import('../components/NotebookComponent'), - { + { ssr: false, - loading: () =>

Loading Jupyter Component...

- } + loading: () =>

Loading Jupyter Component...

, + }, ); export default function Home() { return ( <> - +
  1. - Get started by editing{" "} + Get started by editing{' '} src/app/page.tsx diff --git a/examples/next-js/src/components/CellComponent.tsx b/examples/next-js/src/components/CellComponent.tsx index 143629784..7d868514b 100644 --- a/examples/next-js/src/components/CellComponent.tsx +++ b/examples/next-js/src/components/CellComponent.tsx @@ -19,15 +19,16 @@ const Cell = dynamic( */ export const CellComponent = () => { const { defaultKernel } = useJupyter({ - jupyterServerUrl: "https://oss.datalayer.run/api/jupyter-server", - jupyterServerToken: "60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6", + jupyterServerUrl: 'https://oss.datalayer.run/api/jupyter-server', + jupyterServerToken: + '60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6', startDefaultKernel: true, }); return ( <> - {defaultKernel ? + {defaultKernel ? ( <> -
    Jupyter Cell in Next.js
    +
    Jupyter Cell in Next.js
    { /> - : + ) : (

    Loading Jupyter Cell...

    - } + )} - ) -} + ); +}; export default CellComponent; diff --git a/examples/next-js/src/components/NotebookComponent.tsx b/examples/next-js/src/components/NotebookComponent.tsx index ac070dab1..ed464f14d 100644 --- a/examples/next-js/src/components/NotebookComponent.tsx +++ b/examples/next-js/src/components/NotebookComponent.tsx @@ -4,9 +4,16 @@ * MIT License */ -'use client' +'use client'; -import { useJupyter, JupyterReactTheme, Notebook2, NotebookToolbar, CellSidebarExtension, CellSidebarButton } from '@datalayer/jupyter-react'; +import { + useJupyter, + JupyterReactTheme, + Notebook2, + NotebookToolbar, + CellSidebarExtension, + CellSidebarButton, +} from '@datalayer/jupyter-react'; import { Box } from '@primer/react'; import { PrimerTheme } from './PrimerTheme'; import { useMemo } from 'react'; @@ -14,59 +21,61 @@ import { useMemo } from 'react'; type INotebookComponentProps = { colorMode?: 'light' | 'dark'; theme?: PrimerTheme; -} +}; export const NotebookComponent = (props: INotebookComponentProps) => { -// const { colorMode, theme } = props; + // const { colorMode, theme } = props; const { defaultKernel, serviceManager } = useJupyter({ - jupyterServerUrl: "https://oss.datalayer.run/api/jupyter-server", - jupyterServerToken: "60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6", + jupyterServerUrl: 'https://oss.datalayer.run/api/jupyter-server', + jupyterServerToken: + '60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6', startDefaultKernel: true, }); - const extensions = useMemo(() => [ - new CellSidebarExtension({ factory: CellSidebarButton }) - ], []); + const extensions = useMemo( + () => [new CellSidebarExtension({ factory: CellSidebarButton })], + [], + ); return ( <> - { defaultKernel && serviceManager ? - <> -
    Jupyter Notebook in Next.js
    - - - - - - - : -

    Loading Jupyter Notebook...

    - } - - ) -} + {defaultKernel && serviceManager ? ( + <> +
    Jupyter Notebook in Next.js
    + + + + + + + ) : ( +

    Loading Jupyter Notebook...

    + )} + + ); +}; NotebookComponent.defaultProps = { colorMode: 'light' as 'light' | 'dark', diff --git a/examples/next-js/src/components/PrimerTheme.tsx b/examples/next-js/src/components/PrimerTheme.tsx index 609c7a826..3cc90c185 100644 --- a/examples/next-js/src/components/PrimerTheme.tsx +++ b/examples/next-js/src/components/PrimerTheme.tsx @@ -4,10 +4,10 @@ * MIT License */ -'use client' +'use client'; export type PrimerTheme = { - [key: string]: any; + [key: string]: any; }; export default PrimerTheme; diff --git a/examples/vite/.yarnrc.yml b/examples/vite/.yarnrc.yml index 05fb81872..96052a87f 100644 --- a/examples/vite/.yarnrc.yml +++ b/examples/vite/.yarnrc.yml @@ -6,15 +6,15 @@ enableInlineBuilds: false enableTelemetry: false httpTimeout: 60000 nodeLinker: node-modules -npmRegistryServer: "https://registry.yarnpkg.com" +npmRegistryServer: 'https://registry.yarnpkg.com' # This will fix the build error with @lerna/legacy-package-management # See https://github.com/lerna/repro/pull/11 packageExtensions: - "@lerna/legacy-package-management@*": + '@lerna/legacy-package-management@*': dependencies: - "@lerna/child-process": "*" - "js-yaml": "*" - "rimraf": "*" + '@lerna/child-process': '*' + 'js-yaml': '*' + 'rimraf': '*' peerDependencies: - "nx": "*" + 'nx': '*' diff --git a/examples/vite/README.md b/examples/vite/README.md index 27f4fb32d..4f037bc76 100644 --- a/examples/vite/README.md +++ b/examples/vite/README.md @@ -1,71 +1,71 @@ -[![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io) - -[![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer) - -# ๐Ÿช โš›๏ธ Jupyter React Vite Example - -> Live example on https://jupyter-react-vite-example.vercel.app - -Example to run [Jupyter React](https://github.com/datalayer/jupyter-ui/tree/main/packages/react) in a [Vite.js](https://vitejs.dev) Web application. - -```bash -npm i -npm run dev -``` - -Ensure to add the following script in the head of your HTML. - -```html - - - -``` - -To create a production build, you first need to patch `@jupyter-widgets/controls` to avoid issues with early loadings via `require.js`. - -```patch -diff --git a/node_modules/@jupyter-widgets/controls/lib/index.js b/node_modules/@jupyter-widgets/controls/lib/index.js -index 0063f69..ade0862 100644 ---- a/node_modules/@jupyter-widgets/controls/lib/index.js -+++ b/node_modules/@jupyter-widgets/controls/lib/index.js -@@ -22,5 +22,5 @@ export * from './widget_tagsinput'; - export * from './widget_string'; - export * from './widget_description'; - export * from './widget_upload'; --export const version = require('../package.json').version; -+export const version = "0.1.0"; - //# sourceMappingURL=index.js.map -\ No newline at end of file -diff --git a/node_modules/@jupyter-widgets/controls/src/index.ts b/node_modules/@jupyter-widgets/controls/src/index.ts -index 912458d..5edaa11 100644 ---- a/node_modules/@jupyter-widgets/controls/src/index.ts -+++ b/node_modules/@jupyter-widgets/controls/src/index.ts -@@ -24,4 +24,4 @@ export * from './widget_string'; - export * from './widget_description'; - export * from './widget_upload'; - --export const version = (require('../package.json') as any).version; -+export const version = "5.0.12"; -``` - -Then run the following command to build and test the artifacts in the `dist` folder. - -```bash -# make run -npm run build -cd dist -python -m http.server 8675 # Or any other local server. -open http://localhost:8675 -``` - -
    - Jupyter React Gallery -
    - -## โš–๏ธ License - -Copyright (c) 2025 Datalayer, Inc. - -Released under the terms of the MIT license (see [LICENSE](https://github.com/datalayer/jupyter-ui/blob/main/LICENSE). +[![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io) + +[![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer) + +# ๐Ÿช โš›๏ธ Jupyter React Vite Example + +> Live example on https://jupyter-react-vite-example.vercel.app + +Example to run [Jupyter React](https://github.com/datalayer/jupyter-ui/tree/main/packages/react) in a [Vite.js](https://vitejs.dev) Web application. + +```bash +npm i +npm run dev +``` + +Ensure to add the following script in the head of your HTML. + +```html + + + +``` + +To create a production build, you first need to patch `@jupyter-widgets/controls` to avoid issues with early loadings via `require.js`. + +```patch +diff --git a/node_modules/@jupyter-widgets/controls/lib/index.js b/node_modules/@jupyter-widgets/controls/lib/index.js +index 0063f69..ade0862 100644 +--- a/node_modules/@jupyter-widgets/controls/lib/index.js ++++ b/node_modules/@jupyter-widgets/controls/lib/index.js +@@ -22,5 +22,5 @@ export * from './widget_tagsinput'; + export * from './widget_string'; + export * from './widget_description'; + export * from './widget_upload'; +-export const version = require('../package.json').version; ++export const version = "0.1.0"; + //# sourceMappingURL=index.js.map +\ No newline at end of file +diff --git a/node_modules/@jupyter-widgets/controls/src/index.ts b/node_modules/@jupyter-widgets/controls/src/index.ts +index 912458d..5edaa11 100644 +--- a/node_modules/@jupyter-widgets/controls/src/index.ts ++++ b/node_modules/@jupyter-widgets/controls/src/index.ts +@@ -24,4 +24,4 @@ export * from './widget_string'; + export * from './widget_description'; + export * from './widget_upload'; + +-export const version = (require('../package.json') as any).version; ++export const version = "5.0.12"; +``` + +Then run the following command to build and test the artifacts in the `dist` folder. + +```bash +# make run +npm run build +cd dist +python -m http.server 8675 # Or any other local server. +open http://localhost:8675 +``` + +
    + Jupyter React Gallery +
    + +## โš–๏ธ License + +Copyright (c) 2025 Datalayer, Inc. + +Released under the terms of the MIT license (see [LICENSE](https://github.com/datalayer/jupyter-ui/blob/main/LICENSE). diff --git a/examples/vite/index.html b/examples/vite/index.html index 7619ca3dd..b99c50c49 100644 --- a/examples/vite/index.html +++ b/examples/vite/index.html @@ -4,7 +4,7 @@ ~ MIT License --> - + diff --git a/examples/vite/src/App.css b/examples/vite/src/App.css index 20455d1a5..96761330a 100644 --- a/examples/vite/src/App.css +++ b/examples/vite/src/App.css @@ -4,45 +4,45 @@ * MIT License */ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; -} - -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} - -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(1) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; +} + +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} + +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(1) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index ec2a78fe8..cb3567436 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -1,64 +1,70 @@ -/* - * Copyright (c) 2021-2023 Datalayer, Inc. - * - * MIT License - */ - -import { useState } from "react"; -import reactLogo from "./assets/react.svg"; -import { useJupyter, JupyterReactTheme } from '@datalayer/jupyter-react'; -import { CellExample } from "./examples/CellExample"; -import { NotebookExample } from "./examples/NotebookExample"; - -// Fix for controls version failing to load in Vite. -// import * as controls from "@jupyter-widgets/controls/lib/index"; -// const c = { ...controls } -// c.version = "0.1.0"; - -import "./App.css"; - -function App() { - const { defaultKernel, serviceManager } = useJupyter({ - jupyterServerUrl: "https://oss.datalayer.run/api/jupyter-server", - jupyterServerToken: "60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6", - startDefaultKernel: true, - }); - const [count, setCount] = useState(0); - return ( -
    - <> - - { defaultKernel && } - { defaultKernel && serviceManager && } - - - -

    React + Vite

    -

    On CodeSandbox!

    -
    - -

    - Edit src/App.tsx and save to test HMR. -

    -

    - Tip: you can use the inspector button next to address bar to click on - components in the preview and open the code in the editor! -

    -
    -

    - Click on the Vite and React logos to learn more -

    -
    - ); -} - -export default App; +/* + * Copyright (c) 2021-2023 Datalayer, Inc. + * + * MIT License + */ + +import { useState } from 'react'; +import reactLogo from './assets/react.svg'; +import { useJupyter, JupyterReactTheme } from '@datalayer/jupyter-react'; +import { CellExample } from './examples/CellExample'; +import { NotebookExample } from './examples/NotebookExample'; + +// Fix for controls version failing to load in Vite. +// import * as controls from "@jupyter-widgets/controls/lib/index"; +// const c = { ...controls } +// c.version = "0.1.0"; + +import './App.css'; + +function App() { + const { defaultKernel, serviceManager } = useJupyter({ + jupyterServerUrl: 'https://oss.datalayer.run/api/jupyter-server', + jupyterServerToken: + '60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6', + startDefaultKernel: true, + }); + const [count, setCount] = useState(0); + return ( +
    + <> + + {defaultKernel && } + {defaultKernel && serviceManager && ( + + )} + + + +

    React + Vite

    +

    On CodeSandbox!

    +
    + +

    + Edit src/App.tsx and save to test HMR. +

    +

    + Tip: you can use the inspector button next to address bar to click on + components in the preview and open the code in the editor! +

    +
    +

    + Click on the Vite and React logos to learn more +

    +
    + ); +} + +export default App; diff --git a/examples/vite/src/examples/CellExample.tsx b/examples/vite/src/examples/CellExample.tsx index 00c0abb8b..e2c76dfb9 100644 --- a/examples/vite/src/examples/CellExample.tsx +++ b/examples/vite/src/examples/CellExample.tsx @@ -5,7 +5,13 @@ */ import { Box, Button, Label } from '@primer/react'; -import { Cell, KernelIndicator, useKernelsStore, useCellsStore, Kernel } from '@datalayer/jupyter-react'; +import { + Cell, + KernelIndicator, + useKernelsStore, + useCellsStore, + Kernel, +} from '@datalayer/jupyter-react'; const CELL_ID = 'cell-example-1'; @@ -16,7 +22,7 @@ for i in range(10): type ICellExampleProps = { kernel: Kernel; -} +}; export const CellExample = (props: ICellExampleProps) => { const { kernel } = props; @@ -25,12 +31,8 @@ export const CellExample = (props: ICellExampleProps) => { return ( <> A Jupyter Cell - - Source: {cellsStore.getSource(CELL_ID)} - - - Outputs Count: {cellsStore.getOutputsCount(CELL_ID)} - + Source: {cellsStore.getSource(CELL_ID)} + Outputs Count: {cellsStore.getOutputsCount(CELL_ID)} Kernel State: @@ -38,19 +40,17 @@ export const CellExample = (props: ICellExampleProps) => { Kernel Phase: - - Kernel Indicator: - + Kernel Indicator: - + - + - ) -} + ); +}; export default CellExample; diff --git a/examples/vite/src/examples/NotebookExample.tsx b/examples/vite/src/examples/NotebookExample.tsx index e3a9387f2..167fbbcaa 100644 --- a/examples/vite/src/examples/NotebookExample.tsx +++ b/examples/vite/src/examples/NotebookExample.tsx @@ -6,7 +6,13 @@ import { useMemo } from 'react'; import { Box } from '@primer/react'; -import { Notebook2, Kernel, NotebookToolbar, CellSidebarExtension, CellSidebarButton } from '@datalayer/jupyter-react'; +import { + Notebook2, + Kernel, + NotebookToolbar, + CellSidebarExtension, + CellSidebarButton, +} from '@datalayer/jupyter-react'; import { ServiceManager } from '@jupyterlab/services'; const NOTEBOOK_ID = 'notebook-example-1'; @@ -14,13 +20,14 @@ const NOTEBOOK_ID = 'notebook-example-1'; type INotebookExampleProps = { kernel: Kernel; serviceManager: ServiceManager.IManager; -} +}; export const NotebookExample = (props: INotebookExampleProps) => { const { kernel, serviceManager } = props; - const extensions = useMemo(() => [ - new CellSidebarExtension({ factory: CellSidebarButton }) - ], []); + const extensions = useMemo( + () => [new CellSidebarExtension({ factory: CellSidebarButton })], + [], + ); return ( <> A Jupyter Notebook @@ -33,7 +40,7 @@ export const NotebookExample = (props: INotebookExampleProps) => { Toolbar={NotebookToolbar} /> - ) -} + ); +}; export default NotebookExample; diff --git a/examples/vite/src/main.tsx b/examples/vite/src/main.tsx index 2418179e2..69a55102f 100644 --- a/examples/vite/src/main.tsx +++ b/examples/vite/src/main.tsx @@ -4,14 +4,14 @@ * MIT License */ -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App"; - -import "./index.css"; - -ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - -); +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + , +); diff --git a/examples/vite/src/vite-env.d.ts b/examples/vite/src/vite-env.d.ts index 91b70208a..a143462cf 100644 --- a/examples/vite/src/vite-env.d.ts +++ b/examples/vite/src/vite-env.d.ts @@ -4,4 +4,4 @@ * MIT License */ -/// +/// diff --git a/examples/vite/tsconfig.json b/examples/vite/tsconfig.json index bf09555c3..86457a74a 100644 --- a/examples/vite/tsconfig.json +++ b/examples/vite/tsconfig.json @@ -2,11 +2,7 @@ "compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, - "lib": [ - "DOM", - "DOM.Iterable", - "ESNext" - ], + "lib": ["DOM", "DOM.Iterable", "ESNext"], "allowJs": false, "skipLibCheck": true, "esModuleInterop": false, @@ -20,12 +16,5 @@ "noEmit": true, "jsx": "react-jsx" }, - "include": [ - "src" - ], - "references": [ - { - "path": "./tsconfig.node.json" - } - ] -} \ No newline at end of file + "include": ["src"] +} diff --git a/examples/vite/tsconfig.node.json b/examples/vite/tsconfig.node.json index 842a5f345..daa004dc4 100644 --- a/examples/vite/tsconfig.node.json +++ b/examples/vite/tsconfig.node.json @@ -1,11 +1,14 @@ -{ - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true - }, - "include": [ - "vite.config.ts" - ] -} \ No newline at end of file +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "outDir": "./dist", + "rootDir": "./", + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["vite.config.ts"] +} diff --git a/examples/vite/vite.config.ts b/examples/vite/vite.config.ts index e14f7c77c..ad3474df0 100644 --- a/examples/vite/vite.config.ts +++ b/examples/vite/vite.config.ts @@ -4,9 +4,9 @@ * MIT License */ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; -import { treatAsCommonjs } from "vite-plugin-treat-umd-as-commonjs"; +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { treatAsCommonjs } from 'vite-plugin-treat-umd-as-commonjs'; export default defineConfig({ plugins: [ @@ -18,20 +18,24 @@ export default defineConfig({ async resolveId(source, importer) { if (source.endsWith('.raw.css') && !source.includes('?raw')) { // rewrite import to append ?raw query - const resolved = await this.resolve(source + '?raw', importer, { skipSelf: true }); + const resolved = await this.resolve(source + '?raw', importer, { + skipSelf: true, + }); if (resolved) return resolved.id; return null; } return null; - } + }, }, { - name: "fix-text-query", - enforce: "pre", + name: 'fix-text-query', + enforce: 'pre', async resolveId(source, importer) { - if (source.includes("?text")) { - let fixed = source.replace("?text", "?raw"); - const resolved = await this.resolve(fixed, importer, { skipSelf: true }); + if (source.includes('?text')) { + const fixed = source.replace('?text', '?raw'); + const resolved = await this.resolve(fixed, importer, { + skipSelf: true, + }); if (resolved) { return resolved.id; } @@ -41,12 +45,12 @@ export default defineConfig({ }, }, ], - assetsInclude: ["**/*.whl", "**/*.raw.css"], + assetsInclude: ['**/*.whl', '**/*.raw.css'], resolve: { alias: [ { find: /^~(.*)$/, - replacement: "$1", + replacement: '$1', }, ], }, @@ -54,14 +58,17 @@ export default defineConfig({ global: 'globalThis', __webpack_public_path__: '""', }, + worker: { + format: 'es', + }, build: { rollupOptions: { output: { - assetFileNames: (assetInfo) => { + assetFileNames: assetInfo => { if (/pypi\//.test(assetInfo.name)) { - return "pypi/[name][extname]"; + return 'pypi/[name][extname]'; } - return "assets/[name][extname]"; + return 'assets/[name][extname]'; }, }, }, @@ -69,7 +76,7 @@ export default defineConfig({ optimizeDeps: { esbuildOptions: { loader: { - ".whl": "text", + '.whl': 'text', }, }, }, diff --git a/jest.config.js b/jest.config.js index ecc55397a..c60356962 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,8 +7,6 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - testMatch: [ - "**/?(*.)+(spec|test).[jt]s?(x)" - ], - moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], }; diff --git a/package.json b/package.json index 08bbe1464..873a9c3e2 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "datalayer" ], "engines": { - "node": ">= 18.0.0" + "node": ">= 20.0.0" }, "os": [ "darwin", @@ -39,7 +39,15 @@ "jupyter:ui:slate": "lerna run --parallel --scope @datalayer/jupyter-slate-example start", "storybook": "lerna run --scope @datalayer/jupyter-ui-storybook storybook", "test": "lerna run test", - "postinstall": "patch-package" + "lint": "eslint . --ext .js,.jsx,.ts,.tsx --quiet", + "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", + "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md,mdx,yml,yaml,css,html}\"", + "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,md,mdx,yml,yaml,css,html}\"", + "type-check": "tsc --build --noEmit", + "check": "npm run format:check && npm run lint && npm run type-check", + "check:fix": "npm run format && npm run lint:fix && npm run type-check", + "postinstall": "patch-package", + "prepare": "husky" }, "workspaces": [ "docs", @@ -48,24 +56,37 @@ "storybook" ], "devDependencies": { + "@commitlint/cli": "^19.8.1", + "@commitlint/config-conventional": "^19.8.1", + "@eslint/js": "^9.33.0", + "@typescript-eslint/eslint-plugin": "^8.39.1", + "@typescript-eslint/parser": "^8.39.1", "concurrently": "^6.2.0", + "eslint": "^9.33.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", "fkill-cli": "^7.1.0", + "husky": "^9.1.7", "lerna": "^6.6.2", + "lint-staged": "^16.1.5", "npm-run-all": "^4.1.5", - "patch-package": "^8.0.0" + "patch-package": "^8.0.0", + "typescript-eslint": "^8.39.1" }, "resolutions": { - "@types/react": "18.3.20", - "@types/react-dom": "18.3.6", - "react": "18.3.1", - "react-dom": "18.3.1", + "@types/react": "^18.3.20", + "@types/react-dom": "^18.3.6", + "react": "^18.3.1", + "react-dom": "^18.3.1", "typescript": "^5.8.3" }, "overrides": { - "@types/react": "18.3.20", - "@types/react-dom": "18.3.6", - "react": "18.3.1", - "react-dom": "18.3.1", + "@types/react": "^18.3.20", + "@types/react-dom": "^18.3.6", + "react": "^18.3.1", + "react-dom": "^18.3.1", "typescript": "^5.8.3" } } diff --git a/packages/docusaurus-plugin/README.md b/packages/docusaurus-plugin/README.md index b93a0fead..91207c11f 100644 --- a/packages/docusaurus-plugin/README.md +++ b/packages/docusaurus-plugin/README.md @@ -35,14 +35,14 @@ Add the following in any Markdown file. ```jsx import JupyterCell from '@theme/JupyterCell'; - + jupyterServerUrl="http://localhost:8686/api/jupyter-server" + jupyterServerToken="60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6" +/>; ``` ## โš–๏ธ License diff --git a/packages/docusaurus-plugin/src/index.ts b/packages/docusaurus-plugin/src/index.ts index 2ff0b9e0c..77c9dccbf 100644 --- a/packages/docusaurus-plugin/src/index.ts +++ b/packages/docusaurus-plugin/src/index.ts @@ -4,47 +4,45 @@ * MIT License */ -const webpack = require("webpack"); - import { LoadContext, Plugin } from '@docusaurus/types'; import { PluginOptions } from './types'; -import { Configuration, ProvidePlugin } from 'webpack'; +import { Configuration } from 'webpack'; +import webpack from 'webpack'; import path from 'path'; export default function ( _context: LoadContext, - options: PluginOptions, + _options: PluginOptions, ): Plugin { - const isProd = process.env.NODE_ENV === 'production'; return { name: 'docusaurus-plugin-jupyter', getThemePath() { return path.resolve(__dirname, './theme'); }, - configureWebpack(_config: Configuration, isServer: boolean) { + configureWebpack(_config: Configuration, _isServer: boolean) { return { mergeStrategy: { - 'resolve': 'prepend', + resolve: 'prepend', 'module.rules': 'prepend', - 'plugins': 'prepend', + plugins: 'prepend', }, resolve: { - extensions: [ '.tsx', '.ts', 'jsx', '.js' ], - alias: { - "stream": "stream-browserify", + extensions: ['.tsx', '.ts', 'jsx', '.js'], + alias: { + stream: 'stream-browserify', + }, + fallback: { + assert: require.resolve('assert/'), }, - fallback: { - "assert": require.resolve("assert/"), - } }, module: { rules: [ { test: /\.m?js/, resolve: { - fullySpecified: false - } + fullySpecified: false, + }, }, // Ship the JupyterLite service worker. { @@ -73,14 +71,14 @@ export default function ( }, plugins: [ new webpack.DefinePlugin({ -// 'process.env': '{}', -// 'process.cwd': '() => "/"', -// 'process.argv': 'undefined' + // 'process.env': '{}', + // 'process.cwd': '() => "/"', + // 'process.argv': 'undefined' }), new webpack.ProvidePlugin({ process: 'process/browser', }), - ] + ], }; }, }; diff --git a/packages/docusaurus-plugin/src/theme/JupyterCell.tsx b/packages/docusaurus-plugin/src/theme/JupyterCell.tsx index 879af18d4..2f8b0d918 100644 --- a/packages/docusaurus-plugin/src/theme/JupyterCell.tsx +++ b/packages/docusaurus-plugin/src/theme/JupyterCell.tsx @@ -12,18 +12,23 @@ type JupyterCellProps = { jupyterServerUrl: string; jupyterServerToken: string; source: string; -} +}; const JupyterCell = (props: JupyterCellProps) => { return ( Jupyter Cell fallback content for prerendering.
}> + fallback={
Jupyter Cell fallback content for prerendering.
} + > {() => { // Keep the import via require and keep it into the BrowserOnly code block.. // const { JupyterReactTheme } = require('@datalayer/jupyter-react/lib/theme'); // const { useJupyter } = require('@datalayer/jupyter-react/lib/jupyter/JupyterContext'); - const { Jupyter } = require('@datalayer/jupyter-react/lib/jupyter/Jupyter'); - const { Cell } = require('@datalayer/jupyter-react/lib/components/cell/Cell'); + const { + Jupyter, + } = require('@datalayer/jupyter-react/lib/jupyter/Jupyter'); + const { + Cell, + } = require('@datalayer/jupyter-react/lib/components/cell/Cell'); const { jupyterServerUrl = 'https://oss.datalayer.run/api/jupyter-server', jupyterServerToken = '60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6', @@ -38,15 +43,15 @@ const JupyterCell = (props: JupyterCellProps) => { jupyterServerUrl={jupyterServerUrl} jupyterServerToken={jupyterServerToken} startDefaultKernel - skeleton={} + skeleton={} > - ) + ); }} - ) -} + ); +}; export default JupyterCell; diff --git a/packages/docusaurus-plugin/src/theme/ReactLiveScope/index.tsx b/packages/docusaurus-plugin/src/theme/ReactLiveScope/index.tsx index 4d5341458..dbf429769 100644 --- a/packages/docusaurus-plugin/src/theme/ReactLiveScope/index.tsx +++ b/packages/docusaurus-plugin/src/theme/ReactLiveScope/index.tsx @@ -11,11 +11,16 @@ import { ContentLoader } from '@datalayer/primer-addons'; const Cell = (props: any) => { return ( Jupyter Cell fallback content for prerendering.
}> + fallback={
Jupyter Cell fallback content for prerendering.
} + > {() => { // Keep the import via require in the BrowserOnly code block. - const { Jupyter } = require('@datalayer/jupyter-react/lib/jupyter/Jupyter'); - const { Cell } = require('@datalayer/jupyter-react/lib/components/cell/Cell'); + const { + Jupyter, + } = require('@datalayer/jupyter-react/lib/jupyter/Jupyter'); + const { + Cell, + } = require('@datalayer/jupyter-react/lib/components/cell/Cell'); return ( <> { jupyterServerToken="60c1661cc408f978c309d04157af55c9588ff9557c9380e4fb50785750703da6" disableCssLoading={true} startDefaultKernel - skeleton={} + skeleton={} > - + ); }} - ) -} + ); +}; // Add react-live imports you need here const ReactLiveScope = { diff --git a/packages/docusaurus-plugin/src/theme/StorybookView.tsx b/packages/docusaurus-plugin/src/theme/StorybookView.tsx index 5670c9936..3e3ab1bd0 100644 --- a/packages/docusaurus-plugin/src/theme/StorybookView.tsx +++ b/packages/docusaurus-plugin/src/theme/StorybookView.tsx @@ -4,11 +4,11 @@ * MIT License */ -import React from 'react' +import React from 'react'; const FRAME_STYLE = { border: '1px solid #aaa', -} +}; /** * Embeds a Storybook example @@ -19,9 +19,11 @@ const FRAME_STYLE = { export function StorybookView({ story = '', args }: any) { let queryArgs = ''; if (args) { - const params = Object.entries(args).map(([k, v]: any) => `${encodeURIComponent(k)}:${encodeURIComponent(v)}`).join(';') + const params = Object.entries(args) + .map(([k, v]: any) => `${encodeURIComponent(k)}:${encodeURIComponent(v)}`) + .join(';'); if (params) { - queryArgs = `&args=${params}` + queryArgs = `&args=${params}`; } } return ( @@ -31,7 +33,7 @@ export function StorybookView({ story = '', args }: any) { width="100%" height="600" style={FRAME_STYLE} - allow='clipboard-write;' + allow="clipboard-write;" /> - ) + ); } diff --git a/packages/docusaurus-plugin/tsconfig.json b/packages/docusaurus-plugin/tsconfig.json index 11a2cac20..9ce72dd8f 100644 --- a/packages/docusaurus-plugin/tsconfig.json +++ b/packages/docusaurus-plugin/tsconfig.json @@ -6,10 +6,7 @@ "outDir": "lib", "target": "ESNext", "module": "ESNext", - "lib": [ - "ESNext", - "DOM" - ], + "lib": ["ESNext", "DOM"], "declaration": true, "declarationMap": false, "jsx": "react", @@ -33,9 +30,5 @@ "importHelpers": true, "noEmitHelpers": true }, - "exclude": [ - "node_modules", - "**/__tests__/**/*", - "**/lib/**/*" - ] + "exclude": ["node_modules", "**/__tests__/**/*", "**/lib/**/*"] } diff --git a/packages/ipyreactive/.eslintrc.js b/packages/ipyreactive/.eslintrc.js index 9fb27ea5b..6c45c7955 100644 --- a/packages/ipyreactive/.eslintrc.js +++ b/packages/ipyreactive/.eslintrc.js @@ -3,12 +3,12 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended' + 'plugin:prettier/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.eslint.json', - sourceType: 'module' + sourceType: 'module', }, plugins: ['@typescript-eslint'], rules: { @@ -19,10 +19,10 @@ module.exports = { '@typescript-eslint/quotes': [ 'error', 'single', - { avoidEscape: true, allowTemplateLiterals: false } + { avoidEscape: true, allowTemplateLiterals: false }, ], curly: ['error', 'all'], eqeqeq: 'error', - 'prefer-arrow-callback': 'error' - } -}; \ No newline at end of file + 'prefer-arrow-callback': 'error', + }, +}; diff --git a/packages/ipyreactive/.yarnrc.yml b/packages/ipyreactive/.yarnrc.yml index 7126b6d6a..0d0b2c083 100644 --- a/packages/ipyreactive/.yarnrc.yml +++ b/packages/ipyreactive/.yarnrc.yml @@ -6,16 +6,16 @@ enableInlineBuilds: false enableTelemetry: false httpTimeout: 60000 nodeLinker: node-modules -npmRegistryServer: "https://registry.yarnpkg.com" +npmRegistryServer: 'https://registry.yarnpkg.com' checksumBehavior: update # This will fix the build error with @lerna/legacy-package-management # See https://github.com/lerna/repro/pull/11 packageExtensions: - "@lerna/legacy-package-management@*": + '@lerna/legacy-package-management@*': dependencies: - "@lerna/child-process": "*" - "js-yaml": "*" - "rimraf": "*" + '@lerna/child-process': '*' + 'js-yaml': '*' + 'rimraf': '*' peerDependencies: - "nx": "*" + 'nx': '*' diff --git a/packages/ipyreactive/README.md b/packages/ipyreactive/README.md index 58ecfe72b..ade7e0956 100644 --- a/packages/ipyreactive/README.md +++ b/packages/ipyreactive/README.md @@ -1,4 +1,3 @@ - [![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io) [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer) @@ -12,7 +11,6 @@ Reactive IPyWidgets https://ipyreactive.datalayer.tech for the Datalayer literat [![Build Status](https://travis-ci.org/datalayer/ipyreactive.svg?branch=master)](https://travis-ci.org/datalayer/ipyreactive) [![codecov](https://codecov.io/gh/datalayer/ipyreactive/branch/master/graph/badge.svg)](https://codecov.io/gh/datalayer/ipyreactive) - A Custom Jupyter Widget Library ## Installation @@ -25,6 +23,7 @@ pip install ipyreactive If you are using Jupyter Notebook 5.2 or earlier, you may also need to enable the nbextension: + ```bash jupyter nbextension enable --py [--sys-prefix|--user|--system] ipyreactive ``` @@ -32,12 +31,14 @@ jupyter nbextension enable --py [--sys-prefix|--user|--system] ipyreactive ## Development Installation Create a dev environment: + ```bash conda create -n ipyreactive-dev -c conda-forge nodejs npm python jupyterlab conda activate ipyreactive-dev ``` Install the python. This will also build the TS package. + ```bash pip install -e ".[test, examples]" ``` @@ -63,7 +64,9 @@ you might also need another flag instead of `--sys-prefix`, but we won't cover t of those flags here. ### How to see your changes + #### Typescript: + If you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the widget. @@ -77,4 +80,5 @@ jupyter lab After a change wait for the build to finish and then refresh your browser and the changes should take effect. #### Python: + If you make a change to the python code then you will need to restart the notebook kernel to have it take effect. diff --git a/packages/ipyreactive/codecov.yml b/packages/ipyreactive/codecov.yml index e03ae69b2..64fba2601 100644 --- a/packages/ipyreactive/codecov.yml +++ b/packages/ipyreactive/codecov.yml @@ -9,4 +9,4 @@ coverage: default: target: 0% ignore: - - "ipyreactive/tests" + - 'ipyreactive/tests' diff --git a/packages/ipyreactive/docs/source/_static/helper.js b/packages/ipyreactive/docs/source/_static/helper.js index db746877c..17f4d40a0 100644 --- a/packages/ipyreactive/docs/source/_static/helper.js +++ b/packages/ipyreactive/docs/source/_static/helper.js @@ -6,6 +6,6 @@ var cache_require = window.require; -window.addEventListener('load', function() { +window.addEventListener('load', function () { window.require = cache_require; }); diff --git a/packages/ipyreactive/ipyreactive/nbextension/extension.js b/packages/ipyreactive/ipyreactive/nbextension/extension.js index 7cf182368..80affc5a6 100644 --- a/packages/ipyreactive/ipyreactive/nbextension/extension.js +++ b/packages/ipyreactive/ipyreactive/nbextension/extension.js @@ -6,18 +6,18 @@ // Entry point for the notebook bundle containing custom model definitions. // -define(function() { - "use strict"; +define(function () { + 'use strict'; - window['requirejs'].config({ - map: { - '*': { - '@datalayer/ipyreactive': 'nbextensions/ipyreactive/index', - }, - } - }); - // Export the required load_ipython_extension function - return { - load_ipython_extension : function() {} - }; -}); \ No newline at end of file + window['requirejs'].config({ + map: { + '*': { + '@datalayer/ipyreactive': 'nbextensions/ipyreactive/index', + }, + }, + }); + // Export the required load_ipython_extension function + return { + load_ipython_extension: function () {}, + }; +}); diff --git a/packages/ipyreactive/package_tmp.json b/packages/ipyreactive/package_tmp.json index 861792fe8..a690f8f7a 100644 --- a/packages/ipyreactive/package_tmp.json +++ b/packages/ipyreactive/package_tmp.json @@ -2,17 +2,8 @@ "name": "@datalayer/ipyreactive", "version": "0.1.0", "description": "A Custom Jupyter Widget Library", - "keywords": [ - "jupyter", - "jupyterlab", - "jupyterlab-extension", - "widgets" - ], - "files": [ - "lib/**/*.js", - "dist/*.js", - "css/*.css" - ], + "keywords": ["jupyter", "jupyterlab", "jupyterlab-extension", "widgets"], + "files": ["lib/**/*.js", "dist/*.js", "css/*.css"], "homepage": "https://github.com/datalayer/ipyreactive", "bugs": { "url": "https://github.com/datalayer/ipyreactive/issues" diff --git a/packages/ipyreactive/src/__tests__/utils.ts b/packages/ipyreactive/src/__tests__/utils.ts index 5f2fe18a5..a8dd8b268 100644 --- a/packages/ipyreactive/src/__tests__/utils.ts +++ b/packages/ipyreactive/src/__tests__/utils.ts @@ -59,7 +59,7 @@ export class DummyManager extends widgets.ManagerBase { display_view( msg: services.KernelMessage.IMessage, view: widgets.DOMWidgetView, - options: any + options: any, ) { // TODO: make this a spy // TODO: return an html element @@ -73,7 +73,7 @@ export class DummyManager extends widgets.ManagerBase { protected loadClass( className: string, moduleName: string, - moduleVersion: string + moduleVersion: string, ): Promise { if (moduleName === '@jupyter-widgets/base') { if ((widgets as any)[className]) { @@ -111,7 +111,7 @@ export interface Constructor { export function createTestModel( constructor: Constructor, - attributes?: any + attributes?: any, ): T { const id = widgets.uuid(); const widget_manager = new DummyManager(); diff --git a/packages/ipyreactive/src/plugin.ts b/packages/ipyreactive/src/plugin.ts index 17f5c969a..3c72d8631 100644 --- a/packages/ipyreactive/src/plugin.ts +++ b/packages/ipyreactive/src/plugin.ts @@ -38,7 +38,7 @@ export default examplePlugin; */ function activateWidgetExtension( app: Application, - registry: IJupyterWidgetRegistry + registry: IJupyterWidgetRegistry, ): void { registry.registerWidget({ name: MODULE_NAME, diff --git a/packages/ipyreactive/tsconfig.eslint.json b/packages/ipyreactive/tsconfig.eslint.json index 737e3e680..0a76836c5 100644 --- a/packages/ipyreactive/tsconfig.eslint.json +++ b/packages/ipyreactive/tsconfig.eslint.json @@ -2,4 +2,4 @@ "extends": "./tsconfig.json", "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": [] -} \ No newline at end of file +} diff --git a/packages/ipyreactive/tsconfig.json b/packages/ipyreactive/tsconfig.json index 025054836..e77f51b83 100644 --- a/packages/ipyreactive/tsconfig.json +++ b/packages/ipyreactive/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "forceConsistentCasingInFileNames": true, "declaration": true, - "esModuleInterop":true, + "esModuleInterop": true, "lib": ["es2015", "dom"], "module": "commonjs", "moduleResolution": "node", @@ -18,9 +18,6 @@ "target": "ES2022", "types": ["jest"] }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - ], + "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": ["src/**/__tests__"] } diff --git a/packages/ipyreactive/webpack.config.js b/packages/ipyreactive/webpack.config.js index 42c88a82a..f828953e7 100644 --- a/packages/ipyreactive/webpack.config.js +++ b/packages/ipyreactive/webpack.config.js @@ -11,7 +11,7 @@ const version = require('./package.json').version; const rules = [ { test: /\.ts$/, loader: 'ts-loader' }, { test: /\.js$/, loader: 'source-map-loader' }, - { test: /\.css$/, use: ['style-loader', 'css-loader']} + { test: /\.css$/, use: ['style-loader', 'css-loader'] }, ]; // Packages that shouldn't be bundled but loaded at runtime @@ -19,7 +19,7 @@ const externals = ['@jupyter-widgets/base']; const resolve = { // Add '.ts' and '.tsx' as resolvable extensions. - extensions: [".webpack.js", ".web.js", ".ts", ".js"] + extensions: ['.webpack.js', '.web.js', '.ts', '.js'], }; module.exports = [ @@ -38,7 +38,7 @@ module.exports = [ publicPath: '', }, module: { - rules: rules + rules: rules, }, devtool: 'source-map', externals, @@ -58,21 +58,21 @@ module.exports = [ { entry: './src/index.ts', output: { - filename: 'index.js', - path: path.resolve(__dirname, 'dist'), - libraryTarget: 'amd', - library: "@datalayer/ipyreactive", - publicPath: 'https://unpkg.com/@datalayer/ipyreactive@' + version + '/dist/' + filename: 'index.js', + path: path.resolve(__dirname, 'dist'), + libraryTarget: 'amd', + library: '@datalayer/ipyreactive', + publicPath: + 'https://unpkg.com/@datalayer/ipyreactive@' + version + '/dist/', }, devtool: 'source-map', module: { - rules: rules + rules: rules, }, externals, resolve, }, - /** * Documentation widget bundle * @@ -83,15 +83,14 @@ module.exports = [ output: { filename: 'embed-bundle.js', path: path.resolve(__dirname, 'docs', 'source', '_static'), - library: "@datalayer/ipyreactive", - libraryTarget: 'amd' + library: '@datalayer/ipyreactive', + libraryTarget: 'amd', }, module: { - rules: rules + rules: rules, }, devtool: 'source-map', externals, resolve, - } - + }, ]; diff --git a/packages/ipyscript/.eslintrc.js b/packages/ipyscript/.eslintrc.js index 9fb27ea5b..6c45c7955 100644 --- a/packages/ipyscript/.eslintrc.js +++ b/packages/ipyscript/.eslintrc.js @@ -3,12 +3,12 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended' + 'plugin:prettier/recommended', ], parser: '@typescript-eslint/parser', parserOptions: { project: 'tsconfig.eslint.json', - sourceType: 'module' + sourceType: 'module', }, plugins: ['@typescript-eslint'], rules: { @@ -19,10 +19,10 @@ module.exports = { '@typescript-eslint/quotes': [ 'error', 'single', - { avoidEscape: true, allowTemplateLiterals: false } + { avoidEscape: true, allowTemplateLiterals: false }, ], curly: ['error', 'all'], eqeqeq: 'error', - 'prefer-arrow-callback': 'error' - } -}; \ No newline at end of file + 'prefer-arrow-callback': 'error', + }, +}; diff --git a/packages/ipyscript/.yarnrc.yml b/packages/ipyscript/.yarnrc.yml index 7126b6d6a..0d0b2c083 100644 --- a/packages/ipyscript/.yarnrc.yml +++ b/packages/ipyscript/.yarnrc.yml @@ -6,16 +6,16 @@ enableInlineBuilds: false enableTelemetry: false httpTimeout: 60000 nodeLinker: node-modules -npmRegistryServer: "https://registry.yarnpkg.com" +npmRegistryServer: 'https://registry.yarnpkg.com' checksumBehavior: update # This will fix the build error with @lerna/legacy-package-management # See https://github.com/lerna/repro/pull/11 packageExtensions: - "@lerna/legacy-package-management@*": + '@lerna/legacy-package-management@*': dependencies: - "@lerna/child-process": "*" - "js-yaml": "*" - "rimraf": "*" + '@lerna/child-process': '*' + 'js-yaml': '*' + 'rimraf': '*' peerDependencies: - "nx": "*" + 'nx': '*' diff --git a/packages/ipyscript/README.md b/packages/ipyscript/README.md index 01ce97876..8652f27db 100644 --- a/packages/ipyscript/README.md +++ b/packages/ipyscript/README.md @@ -28,6 +28,7 @@ pip install ipyscript If you are using Jupyter Notebook 5.2 or earlier, you may also need to enable the nbextension: + ```bash jupyter nbextension enable --py [--sys-prefix|--user|--system] ipyscript ``` @@ -35,12 +36,14 @@ jupyter nbextension enable --py [--sys-prefix|--user|--system] ipyscript ## Development Installation Create a dev environment: + ```bash conda create -n ipyscript-dev -c conda-forge nodejs npm python jupyterlab conda activate ipyscript-dev ``` Install the python. This will also build the TS package. + ```bash pip install -e ".[test, examples]" ``` @@ -66,7 +69,9 @@ you might also need another flag instead of `--sys-prefix`, but we won't cover t of those flags here. ### How to see your changes + #### Typescript: + If you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different terminals to watch for changes in the extension's source and automatically rebuild the widget. @@ -80,4 +85,5 @@ jupyter lab After a change wait for the build to finish and then refresh your browser and the changes should take effect. #### Python: + If you make a change to the python code then you will need to restart the notebook kernel to have it take effect. diff --git a/packages/ipyscript/codecov.yml b/packages/ipyscript/codecov.yml index 7984badfa..3c070ed14 100644 --- a/packages/ipyscript/codecov.yml +++ b/packages/ipyscript/codecov.yml @@ -9,4 +9,4 @@ coverage: default: target: 0% ignore: - - "ipyscript/tests" + - 'ipyscript/tests' diff --git a/packages/ipyscript/docs/source/_static/helper.js b/packages/ipyscript/docs/source/_static/helper.js index db746877c..17f4d40a0 100644 --- a/packages/ipyscript/docs/source/_static/helper.js +++ b/packages/ipyscript/docs/source/_static/helper.js @@ -6,6 +6,6 @@ var cache_require = window.require; -window.addEventListener('load', function() { +window.addEventListener('load', function () { window.require = cache_require; }); diff --git a/packages/ipyscript/ipyscript/nbextension/extension.js b/packages/ipyscript/ipyscript/nbextension/extension.js index 134bbcc05..1aa8d0aa0 100644 --- a/packages/ipyscript/ipyscript/nbextension/extension.js +++ b/packages/ipyscript/ipyscript/nbextension/extension.js @@ -6,18 +6,18 @@ // Entry point for the notebook bundle containing custom model definitions. // -define(function() { - "use strict"; +define(function () { + 'use strict'; - window['requirejs'].config({ - map: { - '*': { - '@datalayer/ipyscript': 'nbextensions/ipyscript/index', - }, - } - }); - // Export the required load_ipython_extension function - return { - load_ipython_extension : function() {} - }; -}); \ No newline at end of file + window['requirejs'].config({ + map: { + '*': { + '@datalayer/ipyscript': 'nbextensions/ipyscript/index', + }, + }, + }); + // Export the required load_ipython_extension function + return { + load_ipython_extension: function () {}, + }; +}); diff --git a/packages/ipyscript/package_tmp.json b/packages/ipyscript/package_tmp.json index 1afffc444..530ffbf6e 100644 --- a/packages/ipyscript/package_tmp.json +++ b/packages/ipyscript/package_tmp.json @@ -2,17 +2,8 @@ "name": "@datalayer/ipyscript", "version": "0.1.0", "description": "A Custom Jupyter Widget Library", - "keywords": [ - "jupyter", - "jupyterlab", - "jupyterlab-extension", - "widgets" - ], - "files": [ - "lib/**/*.js", - "dist/*.js", - "css/*.css" - ], + "keywords": ["jupyter", "jupyterlab", "jupyterlab-extension", "widgets"], + "files": ["lib/**/*.js", "dist/*.js", "css/*.css"], "homepage": "https://github.com/datalayer/ipyscript", "bugs": { "url": "https://github.com/datalayer/ipyscript/issues" diff --git a/packages/ipyscript/src/__tests__/utils.ts b/packages/ipyscript/src/__tests__/utils.ts index 5f2fe18a5..a8dd8b268 100644 --- a/packages/ipyscript/src/__tests__/utils.ts +++ b/packages/ipyscript/src/__tests__/utils.ts @@ -59,7 +59,7 @@ export class DummyManager extends widgets.ManagerBase { display_view( msg: services.KernelMessage.IMessage, view: widgets.DOMWidgetView, - options: any + options: any, ) { // TODO: make this a spy // TODO: return an html element @@ -73,7 +73,7 @@ export class DummyManager extends widgets.ManagerBase { protected loadClass( className: string, moduleName: string, - moduleVersion: string + moduleVersion: string, ): Promise { if (moduleName === '@jupyter-widgets/base') { if ((widgets as any)[className]) { @@ -111,7 +111,7 @@ export interface Constructor { export function createTestModel( constructor: Constructor, - attributes?: any + attributes?: any, ): T { const id = widgets.uuid(); const widget_manager = new DummyManager(); diff --git a/packages/ipyscript/src/plugin.ts b/packages/ipyscript/src/plugin.ts index 40818dfea..6ee18fe61 100644 --- a/packages/ipyscript/src/plugin.ts +++ b/packages/ipyscript/src/plugin.ts @@ -38,7 +38,7 @@ export default examplePlugin; */ function activateWidgetExtension( app: Application, - registry: IJupyterWidgetRegistry + registry: IJupyterWidgetRegistry, ): void { registry.registerWidget({ name: MODULE_NAME, diff --git a/packages/ipyscript/tsconfig.eslint.json b/packages/ipyscript/tsconfig.eslint.json index 737e3e680..0a76836c5 100644 --- a/packages/ipyscript/tsconfig.eslint.json +++ b/packages/ipyscript/tsconfig.eslint.json @@ -2,4 +2,4 @@ "extends": "./tsconfig.json", "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": [] -} \ No newline at end of file +} diff --git a/packages/ipyscript/tsconfig.json b/packages/ipyscript/tsconfig.json index 025054836..e77f51b83 100644 --- a/packages/ipyscript/tsconfig.json +++ b/packages/ipyscript/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "forceConsistentCasingInFileNames": true, "declaration": true, - "esModuleInterop":true, + "esModuleInterop": true, "lib": ["es2015", "dom"], "module": "commonjs", "moduleResolution": "node", @@ -18,9 +18,6 @@ "target": "ES2022", "types": ["jest"] }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - ], + "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": ["src/**/__tests__"] } diff --git a/packages/ipyscript/webpack.config.js b/packages/ipyscript/webpack.config.js index e9ecc3c0b..3add4f273 100644 --- a/packages/ipyscript/webpack.config.js +++ b/packages/ipyscript/webpack.config.js @@ -11,7 +11,7 @@ const version = require('./package.json').version; const rules = [ { test: /\.ts$/, loader: 'ts-loader' }, { test: /\.js$/, loader: 'source-map-loader' }, - { test: /\.css$/, use: ['style-loader', 'css-loader']} + { test: /\.css$/, use: ['style-loader', 'css-loader'] }, ]; // Packages that shouldn't be bundled but loaded at runtime @@ -19,7 +19,7 @@ const externals = ['@jupyter-widgets/base']; const resolve = { // Add '.ts' and '.tsx' as resolvable extensions. - extensions: [".webpack.js", ".web.js", ".ts", ".js"] + extensions: ['.webpack.js', '.web.js', '.ts', '.js'], }; module.exports = [ @@ -38,7 +38,7 @@ module.exports = [ publicPath: '', }, module: { - rules: rules + rules: rules, }, devtool: 'source-map', externals, @@ -58,21 +58,21 @@ module.exports = [ { entry: './src/index.ts', output: { - filename: 'index.js', - path: path.resolve(__dirname, 'dist'), - libraryTarget: 'amd', - library: "@datalayer/ipyscript", - publicPath: 'https://unpkg.com/@datalayer/ipyscript@' + version + '/dist/' + filename: 'index.js', + path: path.resolve(__dirname, 'dist'), + libraryTarget: 'amd', + library: '@datalayer/ipyscript', + publicPath: + 'https://unpkg.com/@datalayer/ipyscript@' + version + '/dist/', }, devtool: 'source-map', module: { - rules: rules + rules: rules, }, externals, resolve, }, - /** * Documentation widget bundle * @@ -83,15 +83,14 @@ module.exports = [ output: { filename: 'embed-bundle.js', path: path.resolve(__dirname, 'docs', 'source', '_static'), - library: "@datalayer/ipyscript", - libraryTarget: 'amd' + library: '@datalayer/ipyscript', + libraryTarget: 'amd', }, module: { - rules: rules + rules: rules, }, devtool: 'source-map', externals, resolve, - } - + }, ]; diff --git a/packages/lexical/.yarnrc.yml b/packages/lexical/.yarnrc.yml index 7126b6d6a..0d0b2c083 100644 --- a/packages/lexical/.yarnrc.yml +++ b/packages/lexical/.yarnrc.yml @@ -6,16 +6,16 @@ enableInlineBuilds: false enableTelemetry: false httpTimeout: 60000 nodeLinker: node-modules -npmRegistryServer: "https://registry.yarnpkg.com" +npmRegistryServer: 'https://registry.yarnpkg.com' checksumBehavior: update # This will fix the build error with @lerna/legacy-package-management # See https://github.com/lerna/repro/pull/11 packageExtensions: - "@lerna/legacy-package-management@*": + '@lerna/legacy-package-management@*': dependencies: - "@lerna/child-process": "*" - "js-yaml": "*" - "rimraf": "*" + '@lerna/child-process': '*' + 'js-yaml': '*' + 'rimraf': '*' peerDependencies: - "nx": "*" + 'nx': '*' diff --git a/packages/lexical/gulpfile.js b/packages/lexical/gulpfile.js index d958b0c7c..713c512c3 100755 --- a/packages/lexical/gulpfile.js +++ b/packages/lexical/gulpfile.js @@ -10,12 +10,7 @@ const watch = require('gulp-watch'); const filter = require('gulp-filter'); gulp.task('resources-to-lib-watch', function () { - const f = filter([ - '**', - '!src/**/*.js', - '!src/**/*.ts', - '!src/**/*.tsx', - ]); + const f = filter(['**', '!src/**/*.js', '!src/**/*.ts', '!src/**/*.tsx']); return ( watch('src/**/*', { ignoreInitial: false }) // .pipe(gulp.dest('build')); @@ -26,12 +21,7 @@ gulp.task('resources-to-lib-watch', function () { }); gulp.task('resources-to-lib', async function () { - const f = filter([ - '**', - '!src/**/*.js', - '!src/**/*.ts', - '!src/**/*.tsx', - ]); + const f = filter(['**', '!src/**/*.js', '!src/**/*.ts', '!src/**/*.tsx']); gulp.src('./src/**/*.*').pipe(f).pipe(gulp.dest('./lib/')); return; }); @@ -54,7 +44,7 @@ gulp.task('licenses', async function () { ========================================================= -*/`) +*/`), ) .pipe(gulp.dest('./', { overwrite: true })); @@ -75,7 +65,7 @@ gulp.task('licenses', async function () { ========================================================= --->`) +-->`), ) .pipe(gulp.dest('./', { overwrite: true })); @@ -94,7 +84,7 @@ gulp.task('licenses', async function () { * Coded by Datalayer -*/`) +*/`), ) .pipe(gulp.dest('./', { overwrite: true })); return; diff --git a/packages/lexical/jest.config.js b/packages/lexical/jest.config.js index 7e923e6e2..63850e8a8 100644 --- a/packages/lexical/jest.config.js +++ b/packages/lexical/jest.config.js @@ -21,7 +21,7 @@ const esModules = [ 'vscode\\-ws\\-jsonrpc', 'y\\-protocols', 'y\\-websocket', - 'yjs' + 'yjs', ].join('|'); const { @@ -31,11 +31,11 @@ const { setupFilesAfterEnv, setupFiles, testPathIgnorePatterns, - transform + transform, } = jlabConfig; module.exports = { -/* + /* ...jlabConfig, moduleFileExtensions, moduleNameMapper, @@ -46,12 +46,12 @@ module.exports = { transform, automock: false, */ -// collectCoverageFrom: [ -// 'src/**/*.{ts,tsx}', -// '!src/**/*.d.ts', -// '!src/**/.ipynb_checkpoints/*' -// ], -/* + // collectCoverageFrom: [ + // 'src/**/*.{ts,tsx}', + // '!src/**/*.d.ts', + // '!src/**/.ipynb_checkpoints/*' + // ], + /* coverageDirectory: 'coverage', coverageReporters: ['lcov', 'text'], globals: { @@ -62,5 +62,5 @@ module.exports = { */ testRegex: '(/src/__tests__/.*|(\\./src)(test|spec))\\.[jt]sx?$', transformIgnorePatterns: [`/node_modules/(?!${esModules}).+`], - preset: "jest-puppeteer", -} + preset: 'jest-puppeteer', +}; diff --git a/packages/lexical/jupyter_lexical/templates/index.html b/packages/lexical/jupyter_lexical/templates/index.html index 904d6cc27..81cf6eed6 100644 --- a/packages/lexical/jupyter_lexical/templates/index.html +++ b/packages/lexical/jupyter_lexical/templates/index.html @@ -4,12 +4,16 @@ ~ MIT License --> - + - + ฮž Jupyter Lexical - + - +
diff --git a/packages/lexical/public/index.html b/packages/lexical/public/index.html index 3b3bdc773..ad0be0e6b 100644 --- a/packages/lexical/public/index.html +++ b/packages/lexical/public/index.html @@ -4,17 +4,21 @@ ~ MIT License --> - + Jupyter UI for Lexical Example - + diff --git a/packages/lexical/src/components/Button.tsx b/packages/lexical/src/components/Button.tsx index a21a95f2c..4bf165539 100644 --- a/packages/lexical/src/components/Button.tsx +++ b/packages/lexical/src/components/Button.tsx @@ -4,7 +4,7 @@ * MIT License */ -import {ReactNode} from 'react'; +import { ReactNode } from 'react'; import joinClasses from '../utils/join'; export const Button = ({ @@ -36,10 +36,11 @@ export const Button = ({ onClick={onClick} title={title} aria-label={title} - {...(dataTestId && {'data-test-id': dataTestId})}> + {...(dataTestId && { 'data-test-id': dataTestId })} + > {children} ); -} +}; export default Button; diff --git a/packages/lexical/src/components/Commenting.tsx b/packages/lexical/src/components/Commenting.tsx index c7d5e9bbb..43136892d 100644 --- a/packages/lexical/src/components/Commenting.tsx +++ b/packages/lexical/src/components/Commenting.tsx @@ -4,12 +4,18 @@ * MIT License */ -import {useEffect, useState} from 'react'; -import type {LexicalEditor} from 'lexical'; -import {TOGGLE_CONNECT_COMMAND} from '@lexical/yjs'; -import {COMMAND_PRIORITY_LOW} from 'lexical'; -import {WebsocketProvider} from 'y-websocket'; -import { Array as YArray, Map as YMap, Transaction, YArrayEvent, YEvent } from 'yjs'; +import { useEffect, useState } from 'react'; +import type { LexicalEditor } from 'lexical'; +import { TOGGLE_CONNECT_COMMAND } from '@lexical/yjs'; +import { COMMAND_PRIORITY_LOW } from 'lexical'; +import { WebsocketProvider } from 'y-websocket'; +import { + Array as YArray, + Map as YMap, + Transaction, + YArrayEvent, + YEvent, +} from 'yjs'; export type Comment = { author: string; @@ -161,7 +167,7 @@ export class CommentStore { deleteCommentOrThread( commentOrThread: Comment | Thread, thread?: Thread, - ): {markedComment: Comment; index: number} | null { + ): { markedComment: Comment; index: number } | null { const nextComments = Array.from(this._comments); // The YJS types explicitly use `any` as well. // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -220,7 +226,6 @@ export class CommentStore { _withRemoteTransaction(fn: () => void): void { const provider = this._collabProvider; if (provider !== null) { - // @ts-ignore doc does exist const doc = provider.doc; doc.transact(fn, this); } @@ -240,7 +245,7 @@ export class CommentStore { const provider = this._collabProvider; if (provider !== null) { const doc = provider.doc; -// return doc.get('comments', YArray) as YArray; + // return doc.get('comments', YArray) as YArray; return doc.get('comments') as YArray; } return null; @@ -287,7 +292,7 @@ export class CommentStore { const unsubscribe = this._editor.registerCommand( TOGGLE_CONNECT_COMMAND, - (payload) => { + payload => { if (connect !== undefined && disconnect !== undefined) { const shouldConnect = payload; @@ -330,7 +335,7 @@ export class CommentStore { target === sharedCommentsArray ? undefined : parent instanceof YMap && - (this._comments.find((t) => t.id === parent.get('id')) as + (this._comments.find(t => t.id === parent.get('id')) as | Thread | undefined); diff --git a/packages/lexical/src/components/ContentEditable.tsx b/packages/lexical/src/components/ContentEditable.tsx index 0420569a7..b208bdf8b 100644 --- a/packages/lexical/src/components/ContentEditable.tsx +++ b/packages/lexical/src/components/ContentEditable.tsx @@ -4,7 +4,7 @@ * MIT License */ -import {ContentEditable} from '@lexical/react/LexicalContentEditable'; +import { ContentEditable } from '@lexical/react/LexicalContentEditable'; export const LexicalContentEditable = ({ className, @@ -12,6 +12,6 @@ export const LexicalContentEditable = ({ className?: string; }): JSX.Element => { return ; -} +}; export default LexicalContentEditable; diff --git a/packages/lexical/src/components/CopyButton.tsx b/packages/lexical/src/components/CopyButton.tsx index d4d1b3959..b1878036d 100644 --- a/packages/lexical/src/components/CopyButton.tsx +++ b/packages/lexical/src/components/CopyButton.tsx @@ -4,22 +4,22 @@ * MIT License */ -import {useState} from 'react'; +import { useState } from 'react'; import { $getNearestNodeFromDOMNode, $getSelection, $setSelection, LexicalEditor, } from 'lexical'; -import {$isCodeNode} from '@lexical/code'; -import {useDebounce} from '../hooks'; +import { $isCodeNode } from '@lexical/code'; +import { useDebounce } from '../hooks'; interface Props { editor: LexicalEditor; getCodeDOMNode: () => HTMLElement | null; } -export function CopyButton({editor, getCodeDOMNode}: Props) { +export function CopyButton({ editor, getCodeDOMNode }: Props) { const [isCopyCompleted, setCopyCompleted] = useState(false); const removeSuccessIcon = useDebounce(() => { diff --git a/packages/lexical/src/components/Dialog.tsx b/packages/lexical/src/components/Dialog.tsx index 120b46804..6c41ff77a 100644 --- a/packages/lexical/src/components/Dialog.tsx +++ b/packages/lexical/src/components/Dialog.tsx @@ -4,14 +4,14 @@ * MIT License */ -import {ReactNode} from 'react'; +import { ReactNode } from 'react'; type Props = Readonly<{ 'data-test-id'?: string; children: ReactNode; }>; -export function DialogButtonsList({children}: Props): JSX.Element { +export function DialogButtonsList({ children }: Props): JSX.Element { return
{children}
; } diff --git a/packages/lexical/src/components/DropDown.tsx b/packages/lexical/src/components/DropDown.tsx index 7a11ba32f..173e374de 100644 --- a/packages/lexical/src/components/DropDown.tsx +++ b/packages/lexical/src/components/DropDown.tsx @@ -13,7 +13,7 @@ import { useState, } from 'react'; import * as React from 'react'; -import {createPortal} from 'react-dom'; +import { createPortal } from 'react-dom'; type DropDownContextType = { registerItem: (ref: React.RefObject) => void; @@ -40,7 +40,7 @@ export function DropDownItem({ throw new Error('DropDownItem must be used within a DropDown'); } - const {registerItem} = dropDownContext; + const { registerItem } = dropDownContext; useEffect(() => { if (ref && ref.current) { @@ -69,7 +69,7 @@ function DropDownItems({ useState>(); const registerItem = useCallback( (itemRef: React.RefObject) => { - setItems((prev) => (prev ? [...prev, itemRef] : [itemRef])); + setItems(prev => (prev ? [...prev, itemRef] : [itemRef])); }, [setItems], ); @@ -83,13 +83,13 @@ function DropDownItems({ if (key === 'Escape' || key === 'Tab') { onClose(); } else if (key === 'ArrowUp') { - setHighlightedItem((prev) => { + setHighlightedItem(prev => { if (!prev) return items[0]; const index = items.indexOf(prev) - 1; return items[index === -1 ? items.length - 1 : index]; }); } else if (key === 'ArrowDown') { - setHighlightedItem((prev) => { + setHighlightedItem(prev => { if (!prev) return items[0]; return items[items.indexOf(prev) + 1]; }); @@ -149,7 +149,7 @@ export const DropDown = ({ const dropDown = dropDownRef.current; if (showDropDown && button !== null && dropDown !== null) { - const {top, left} = button.getBoundingClientRect(); + const { top, left } = button.getBoundingClientRect(); dropDown.style.top = `${top + 40}px`; dropDown.style.left = `${Math.min( left, @@ -189,7 +189,8 @@ export const DropDown = ({ aria-label={buttonAriaLabel || buttonLabel} className={buttonClassName} onClick={() => setShowDropDown(!showDropDown)} - ref={buttonRef}> + ref={buttonRef} + > {buttonIconClassName && } {buttonLabel && ( {buttonLabel} @@ -206,6 +207,6 @@ export const DropDown = ({ )} ); -} +}; export default DropDown; diff --git a/packages/lexical/src/components/EquationEditor.tsx b/packages/lexical/src/components/EquationEditor.tsx index 9f01acab4..73685ef95 100644 --- a/packages/lexical/src/components/EquationEditor.tsx +++ b/packages/lexical/src/components/EquationEditor.tsx @@ -4,13 +4,12 @@ * MIT License */ - -import {ChangeEvent, RefObject} from 'react'; +import { ChangeEvent, RefObject } from 'react'; type BaseEquationEditorProps = { equation: string; inline: boolean; - inputRef: {current: null | HTMLInputElement | HTMLTextAreaElement}; + inputRef: { current: null | HTMLInputElement | HTMLTextAreaElement }; setEquation: (equation: string) => void; }; @@ -45,7 +44,7 @@ export default function EquationEditor({ type EquationEditorImplProps = { equation: string; - inputRef: {current: null | HTMLInputElement}; + inputRef: { current: null | HTMLInputElement }; onChange: (event: ChangeEvent) => void; }; @@ -71,7 +70,7 @@ function InlineEquationEditor({ type BlockEquationEditorImplProps = { equation: string; - inputRef: {current: null | HTMLTextAreaElement}; + inputRef: { current: null | HTMLTextAreaElement }; onChange: (event: ChangeEvent) => void; }; diff --git a/packages/lexical/src/components/FileInput.tsx b/packages/lexical/src/components/FileInput.tsx index 8e55b583e..c71c9b36d 100644 --- a/packages/lexical/src/components/FileInput.tsx +++ b/packages/lexical/src/components/FileInput.tsx @@ -4,7 +4,6 @@ * MIT License */ - type Props = Readonly<{ 'data-test-id'?: string; accept?: string; @@ -25,11 +24,11 @@ export const FileInput = ({ type="file" accept={accept} className="Input__input" - onChange={(e) => onChange(e.target.files)} + onChange={e => onChange(e.target.files)} data-test-id={dataTestId} />
); -} +}; export default FileInput; diff --git a/packages/lexical/src/components/ImageResizer.tsx b/packages/lexical/src/components/ImageResizer.tsx index 56967ad5e..9ae18fc22 100644 --- a/packages/lexical/src/components/ImageResizer.tsx +++ b/packages/lexical/src/components/ImageResizer.tsx @@ -5,8 +5,8 @@ */ import * as React from 'react'; -import {useRef} from 'react'; -import type {LexicalEditor} from 'lexical'; +import { useRef } from 'react'; +import type { LexicalEditor } from 'lexical'; function clamp(value: number, min: number, max: number) { return Math.min(Math.max(value, min), max); @@ -30,8 +30,8 @@ export const ImageResizer = ({ setShowCaption, }: { editor: LexicalEditor; - buttonRef: {current: null | HTMLButtonElement}; - imageRef: {current: null | HTMLElement}; + buttonRef: { current: null | HTMLButtonElement }; + imageRef: { current: null | HTMLElement }; maxWidth?: number; onResizeEnd: (width: 'inherit' | number, height: 'inherit' | number) => void; onResizeStart: () => void; @@ -69,8 +69,8 @@ export const ImageResizer = ({ const maxWidthContainer = maxWidth ? maxWidth : editorRootElement !== null - ? editorRootElement.getBoundingClientRect().width - 20 - : 100; + ? editorRootElement.getBoundingClientRect().width - 20 + : 100; const maxHeightContainer = editorRootElement !== null ? editorRootElement.getBoundingClientRect().height - 20 @@ -137,7 +137,7 @@ export const ImageResizer = ({ const controlWrapper = controlWrapperRef.current; if (image !== null && controlWrapper !== null) { - const {width, height} = image.getBoundingClientRect(); + const { width, height } = image.getBoundingClientRect(); const positioning = positioningRef.current; positioning.startWidth = width; positioning.startHeight = height; @@ -246,60 +246,61 @@ export const ImageResizer = ({ ref={buttonRef} onClick={() => { setShowCaption(!showCaption); - }}> + }} + > Add Caption )}
{ + onPointerDown={event => { handlePointerDown(event, Direction.north); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.north | Direction.east); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.east); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.south | Direction.east); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.south); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.south | Direction.west); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.west); }} />
{ + onPointerDown={event => { handlePointerDown(event, Direction.north | Direction.west); }} />
); -} +}; export default ImageResizer; diff --git a/packages/lexical/src/components/InsertEquationDialog.tsx b/packages/lexical/src/components/InsertEquationDialog.tsx index b087d1264..ae42f90ed 100644 --- a/packages/lexical/src/components/InsertEquationDialog.tsx +++ b/packages/lexical/src/components/InsertEquationDialog.tsx @@ -4,8 +4,8 @@ * MIT License */ -import { useCallback } from "react"; -import { LexicalEditor } from "lexical"; +import { useCallback } from 'react'; +import { LexicalEditor } from 'lexical'; import { KatexEquationAlterer } from './../components/KatexEquationAlterer'; import { INSERT_EQUATION_COMMAND } from './../plugins/EquationsPlugin'; @@ -18,7 +18,10 @@ export function InsertEquationDialog({ }): JSX.Element { const onEquationConfirm = useCallback( (equation: string, inline: boolean) => { - activeEditor.dispatchCommand(INSERT_EQUATION_COMMAND, {equation, inline}); + activeEditor.dispatchCommand(INSERT_EQUATION_COMMAND, { + equation, + inline, + }); onClose(); }, [activeEditor, onClose], diff --git a/packages/lexical/src/components/InsertImageDialog.tsx b/packages/lexical/src/components/InsertImageDialog.tsx index ccb152fd5..7bdb20de7 100644 --- a/packages/lexical/src/components/InsertImageDialog.tsx +++ b/packages/lexical/src/components/InsertImageDialog.tsx @@ -4,8 +4,8 @@ * MIT License */ -import { useState } from "react"; -import { LexicalEditor } from "lexical"; +import { useState } from 'react'; +import { LexicalEditor } from 'lexical'; import { Button } from './../components/Button'; import { TextInput } from './../components/TextInput'; import { FileInput } from './../components/FileInput'; @@ -41,7 +41,8 @@ export function InsertImageUriDialogBody({
@@ -91,7 +92,8 @@ export function InsertImageUploadedDialogBody({
@@ -122,17 +124,20 @@ export function InsertImageDialog({ altText: 'Yellow flower in tilt shift lens', src: yellowFlowerImage, }) - }> + } + > Sample
diff --git a/packages/lexical/src/components/JupyterCellComponent.tsx b/packages/lexical/src/components/JupyterCellComponent.tsx index 4ae838754..bb116c61b 100644 --- a/packages/lexical/src/components/JupyterCellComponent.tsx +++ b/packages/lexical/src/components/JupyterCellComponent.tsx @@ -12,18 +12,18 @@ type IJupyterCellComponentProps = { outputs: IOutput[]; loading: string; autoStart: boolean; -} +}; export const JupyterCellComponent = (props: IJupyterCellComponentProps) => { const { autoStart, code, outputs } = props; return ( - - ) -} + + ); +}; export default JupyterCellComponent; diff --git a/packages/lexical/src/components/JupyterOutputComponent.tsx b/packages/lexical/src/components/JupyterOutputComponent.tsx index a37ca3b9d..59a36e065 100644 --- a/packages/lexical/src/components/JupyterOutputComponent.tsx +++ b/packages/lexical/src/components/JupyterOutputComponent.tsx @@ -4,8 +4,8 @@ * MIT License */ -import { IOutput } from "@jupyterlab/nbformat"; -import { Output } from "@datalayer/jupyter-react/lib/components/output/Output"; +import { IOutput } from '@jupyterlab/nbformat'; +import { Output } from '@datalayer/jupyter-react/lib/components/output/Output'; import { OutputAdapter } from '@datalayer/jupyter-react'; type Props = { @@ -16,10 +16,17 @@ type Props = { codeNodeUuid: string; outputNodeUuid: string; executeTrigger: number; -} +}; export const JupyterOutputComponent = (props: Props) => { - const { outputNodeUuid, code, outputs, executeTrigger, autoRun, outputAdapter } = props; + const { + outputNodeUuid, + code, + outputs, + executeTrigger, + autoRun, + outputAdapter, + } = props; return ( { id={outputNodeUuid} executeTrigger={executeTrigger} lumino={false} - /> - ) -} + /> + ); +}; export default JupyterOutputComponent; diff --git a/packages/lexical/src/components/KatexEquationAlterer.tsx b/packages/lexical/src/components/KatexEquationAlterer.tsx index 61b494d96..7176f39d3 100644 --- a/packages/lexical/src/components/KatexEquationAlterer.tsx +++ b/packages/lexical/src/components/KatexEquationAlterer.tsx @@ -4,11 +4,10 @@ * MIT License */ -import {useCallback, useState} from 'react'; +import { useCallback, useState } from 'react'; import Button from '../components/Button'; import KatexRenderer from './KatexRenderer'; - type Props = { initialEquation?: string; onConfirm: (equation: string, inline: boolean) => void; @@ -36,7 +35,7 @@ export const KatexEquationAlterer = ({
{inline ? ( { + onChange={event => { setEquation(event.target.value); }} value={equation} @@ -44,7 +43,7 @@ export const KatexEquationAlterer = ({ /> ) : (