diff --git a/.copier-answers.yml b/.copier-answers.yml index be9827a..5f59784 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -6,9 +6,9 @@ author_name: Mars Liu has_binder: true has_settings: true kind: frontend -labextension_name: lichi -project_short_description: Lichi is a ai client for jupyter lab -python_name: lichi +labextension_name: litchi +project_short_description: litchi is a ai client for jupyter lab +python_name: litchi repository: https://github.com/MarchLiu/litchi test: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a582684..e2d9b53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: python -m pip install .[test] jupyter labextension list - jupyter labextension list 2>&1 | grep -ie "lichi.*OK" + jupyter labextension list 2>&1 | grep -ie "litchi.*OK" python -m jupyterlab.browser_check - name: Package the extension @@ -50,13 +50,13 @@ jobs: pip install build python -m build - pip uninstall -y "lichi" jupyterlab + pip uninstall -y "litchi" jupyterlab - name: Upload extension packages uses: actions/upload-artifact@v4 with: name: extension-artifacts - path: dist/lichi* + path: dist/litchi* if-no-files-found: error test_isolated: @@ -79,11 +79,11 @@ jobs: sudo rm -rf $(which node) sudo rm -rf $(which node) - pip install "jupyterlab>=4.0.0,<5" lichi*.whl + pip install "jupyterlab>=4.0.0,<5" litchi*.whl jupyter labextension list - jupyter labextension list 2>&1 | grep -ie "lichi.*OK" + jupyter labextension list 2>&1 | grep -ie "litchi.*OK" python -m jupyterlab.browser_check --no-browser-test integration-tests: @@ -109,7 +109,7 @@ jobs: - name: Install the extension run: | set -eux - python -m pip install "jupyterlab>=4.0.0,<5" lichi*.whl + python -m pip install "jupyterlab>=4.0.0,<5" litchi*.whl - name: Install dependencies working-directory: ui-tests @@ -138,7 +138,7 @@ jobs: if: always() uses: actions/upload-artifact@v4 with: - name: lichi-playwright-tests + name: litchi-playwright-tests path: | ui-tests/test-results ui-tests/playwright-report diff --git a/.github/workflows/check-release.yml b/.github/workflows/check-release.yml index fae4c86..95ec8a1 100644 --- a/.github/workflows/check-release.yml +++ b/.github/workflows/check-release.yml @@ -26,5 +26,5 @@ jobs: - name: Upload Distributions uses: actions/upload-artifact@v4 with: - name: lichi-releaser-dist-${{ github.run_number }} + name: litchi-releaser-dist-${{ github.run_number }} path: .jupyter_releaser_checkout/dist diff --git a/.gitignore b/.gitignore index df4dcdf..1c71268 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,9 @@ node_modules/ *.egg-info/ .ipynb_checkpoints *.tsbuildinfo -lichi/labextension +litchi/labextension # Version file is handled by hatchling -lichi/_version.py +litchi/_version.py # Integration tests ui-tests/test-results/ @@ -123,3 +123,6 @@ dmypy.json # Yarn cache .yarn/ + +package-lock.json +yarn.lock \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index 6523791..80618aa 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,4 +3,4 @@ node_modules **/lib **/package.json !/package.json -lichi +litchi diff --git a/README.md b/README.md index 14ad2f8..4b808d1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# lichi +# litchi [![Github Actions Status](https://github.com/MarchLiu/litchi/workflows/Build/badge.svg)](https://github.com/MarchLiu/litchi/actions/workflows/build.yml) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/MarchLiu/litchi/main?urlpath=lab) -Lichi is a ai client for jupyter lab +litchi is a ai client for jupyter lab ## Requirements @@ -15,7 +15,7 @@ Lichi is a ai client for jupyter lab To install the extension, execute: ```bash -pip install lichi +pip install litchi ``` ## Uninstall @@ -23,7 +23,7 @@ pip install lichi To remove the extension, execute: ```bash -pip uninstall lichi +pip uninstall litchi ``` ## Contributing @@ -38,7 +38,7 @@ The `jlpm` command is JupyterLab's pinned version of ```bash # Clone the repo to your local environment -# Change directory to the lichi directory +# Change directory to the litchi directory # Install package in development mode pip install -e "." # Link your development version of the extension with JupyterLab @@ -67,12 +67,12 @@ jupyter lab build --minimize=False ### Development uninstall ```bash -pip uninstall lichi +pip uninstall litchi ``` In development mode, you will also need to remove the symlink created by `jupyter labextension develop` command. To find its location, you can run `jupyter labextension list` to figure out where the `labextensions` -folder is located. Then you can remove the symlink named `lichi` within that folder. +folder is located. Then you can remove the symlink named `litchi` within that folder. ### Testing the extension diff --git a/RELEASE.md b/RELEASE.md index 47ec736..26e95a8 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,4 @@ -# Making a new release of lichi +# Making a new release of litchi The extension can be published to `PyPI` and `npm` manually or using the [Jupyter Releaser](https://github.com/jupyter-server/jupyter_releaser). diff --git a/binder/environment.yml b/binder/environment.yml index b7c7e0d..fc0b71f 100644 --- a/binder/environment.yml +++ b/binder/environment.yml @@ -1,10 +1,10 @@ -# a mybinder.org-ready environment for demoing lichi +# a mybinder.org-ready environment for demoing litchi # this environment may also be used locally on Linux/MacOS/Windows, e.g. # # conda env update --file binder/environment.yml -# conda activate lichi-demo +# conda activate litchi-demo # -name: lichi-demo +name: litchi-demo channels: - conda-forge diff --git a/binder/postBuild b/binder/postBuild index c5b2f16..dbcc9e0 100755 --- a/binder/postBuild +++ b/binder/postBuild @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -""" perform a development install of lichi +""" perform a development install of litchi On Binder, this will run _after_ the environment has been fully created from the environment.yml in this directory. @@ -43,5 +43,5 @@ _("jupyter", "server", "extension", "list") _("jupyter", "labextension", "list") -print("JupyterLab with lichi is ready to run with:\n") +print("JupyterLab with litchi is ready to run with:\n") print("\tjupyter lab\n") diff --git a/install.json b/install.json index 08198f4..e9125c3 100644 --- a/install.json +++ b/install.json @@ -1,5 +1,5 @@ { "packageManager": "python", - "packageName": "lichi", - "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package lichi" + "packageName": "litchi", + "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package litchi" } diff --git a/lichi/__init__.py b/litchi/__init__.py similarity index 83% rename from lichi/__init__.py rename to litchi/__init__.py index 99d4228..64ed00d 100644 --- a/lichi/__init__.py +++ b/litchi/__init__.py @@ -5,12 +5,12 @@ # in editable mode with pip. It is highly recommended to install # the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs import warnings - warnings.warn("Importing 'lichi' outside a proper installation.") + warnings.warn("Importing 'litchi' outside a proper installation.") __version__ = "dev" def _jupyter_labextension_paths(): return [{ "src": "labextension", - "dest": "lichi" + "dest": "litchi" }] diff --git a/package.json b/package.json index f8cb947..73e39c2 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { - "name": "lichi", + "name": "litchi", "version": "0.1.0", - "description": "Lichi is a ai client for jupyter lab", + "description": "litchi is a ai client for jupyter lab", "keywords": [ "jupyter", "jupyterlab", - "jupyterlab-extension" + "jupyterlab-extension", + "ai", + "ollama" ], "homepage": "https://github.com/MarchLiu/litchi", "bugs": { @@ -39,7 +41,7 @@ "clean": "jlpm clean:lib", "clean:lib": "rimraf lib tsconfig.tsbuildinfo", "clean:lintcache": "rimraf .eslintcache .stylelintcache", - "clean:labextension": "rimraf lichi/labextension lichi/_version.py", + "clean:labextension": "rimraf litchi/labextension litchi/_version.py", "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache", "eslint": "jlpm eslint:check --fix", "eslint:check": "eslint . --cache --ext .ts,.tsx", @@ -57,12 +59,18 @@ "watch:labextension": "jupyter labextension watch ." }, "dependencies": { - "@jupyterlab/application": "^4.0.0", - "@jupyterlab/settingregistry": "^4.0.0" + "@jupyterlab/application": "^4.2.4", + "@jupyterlab/apputils": "^4.3.4", + "@jupyterlab/settingregistry": "^4.0.0", + "@lumino/widgets": "^2.5.0" }, "devDependencies": { "@jupyterlab/builder": "^4.0.0", + "@jupyterlab/docregistry": "^4.2.4", + "@jupyterlab/notebook": "^4.2.4", "@jupyterlab/testutils": "^4.0.0", + "@jupyterlab/ui-components": "^4.2.4", + "@lumino/disposable": "^2.1.3", "@types/jest": "^29.2.0", "@types/json-schema": "^7.0.11", "@types/react": "^18.0.26", @@ -97,7 +105,7 @@ }, "jupyterlab": { "extension": true, - "outputDir": "lichi/labextension", + "outputDir": "litchi/labextension", "schemaDir": "schema" }, "eslintIgnore": [ diff --git a/pyproject.toml b/pyproject.toml index 21e40dc..73238c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version>=0 build-backend = "hatchling.build" [project] -name = "lichi" +name = "litchi" readme = "README.md" license = { file = "LICENSE" } requires-python = ">=3.8" @@ -33,24 +33,24 @@ source = "nodejs" fields = ["description", "authors", "urls", "keywords"] [tool.hatch.build.targets.sdist] -artifacts = ["lichi/labextension"] +artifacts = ["litchi/labextension"] exclude = [".github", "binder"] [tool.hatch.build.targets.wheel.shared-data] -"lichi/labextension" = "share/jupyter/labextensions/lichi" -"install.json" = "share/jupyter/labextensions/lichi/install.json" +"litchi/labextension" = "share/jupyter/labextensions/litchi" +"install.json" = "share/jupyter/labextensions/litchi/install.json" [tool.hatch.build.hooks.version] -path = "lichi/_version.py" +path = "litchi/_version.py" [tool.hatch.build.hooks.jupyter-builder] dependencies = ["hatch-jupyter-builder>=0.5"] build-function = "hatch_jupyter_builder.npm_builder" ensured-targets = [ - "lichi/labextension/static/style.js", - "lichi/labextension/package.json", + "litchi/labextension/static/style.js", + "litchi/labextension/package.json", ] -skip-if-exists = ["lichi/labextension/static/style.js"] +skip-if-exists = ["litchi/labextension/static/style.js"] [tool.hatch.build.hooks.jupyter-builder.build-kwargs] build_cmd = "build:prod" @@ -60,7 +60,7 @@ npm = ["jlpm"] build_cmd = "install:extension" npm = ["jlpm"] source_dir = "src" -build_dir = "lichi/labextension" +build_dir = "litchi/labextension" [tool.jupyter-releaser.options] version_cmd = "hatch version" diff --git a/schema/plugin.json b/schema/plugin.json index fd5051a..b5069bc 100644 --- a/schema/plugin.json +++ b/schema/plugin.json @@ -1,7 +1,7 @@ { "jupyter.lab.shortcuts": [], - "title": "lichi", - "description": "lichi settings.", + "title": "litchi", + "description": "litchi settings.", "type": "object", "properties": {}, "additionalProperties": false diff --git a/src/__tests__/lichi.spec.ts b/src/__tests__/litchi.spec.ts similarity index 85% rename from src/__tests__/lichi.spec.ts rename to src/__tests__/litchi.spec.ts index 5bc45a6..ead67cf 100644 --- a/src/__tests__/lichi.spec.ts +++ b/src/__tests__/litchi.spec.ts @@ -2,7 +2,7 @@ * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests */ -describe('lichi', () => { +describe('litchi', () => { it('should be tested', () => { expect(1 + 1).toEqual(2); }); diff --git a/src/index.ts b/src/index.ts index c8881e5..2cf7f96 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,32 +1,171 @@ import { JupyterFrontEnd, - JupyterFrontEndPlugin + JupyterFrontEndPlugin, + ILayoutRestorer } from '@jupyterlab/application'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; -/** - * Initialization data for the lichi extension. - */ +import { + ICommandPalette, + MainAreaWidget, + WidgetTracker +} from '@jupyterlab/apputils'; +import { Widget } from '@lumino/widgets'; + +class APODWidget extends Widget { + /** + * Construct a new APOD widget. + */ + constructor() { + super(); + + this.addClass('litchiWidget'); + + // Add an image element to the panel + this.img = document.createElement('img'); + this.node.appendChild(this.img); + + // Add a summary element to the panel + this.summary = document.createElement('p'); + this.node.appendChild(this.summary); + } + + /** + * The image element associated with the widget. + */ + readonly img: HTMLImageElement; + + /** + * The summary text element associated with the widget. + */ + readonly summary: HTMLParagraphElement; + + /** + * Handle update requests for the widget. + */ + async updateAPODiImage(): Promise { + + const response = await fetch(`https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&date=${this.randomDate()}`); + + if (!response.ok) { + const data = await response.json(); + if (data.error) { + this.summary.innerText = data.error.message; + } else { + this.summary.innerText = response.statusText; + } + return; + } + + const data = (await response.json()) as APODResponse; + + if (data.media_type === 'image') { + // Populate the image + this.img.src = data.url; + this.img.title = data.title; + this.summary.innerText = data.title; + if (data.copyright) { + this.summary.innerText += ` (Copyright ${data.copyright})`; + } + } else { + this.summary.innerText = 'Random APOD fetched was not an image.'; + } + } + + /** + * Get a random date string in YYYY-MM-DD format. + */ + randomDate(): string { + const start = new Date(2010, 1, 1); + const end = new Date(); + const randomDate = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); + return randomDate.toISOString().slice(0, 10); + } +} + +interface APODResponse { + copyright: string; + date: string; + explanation: string; + media_type: 'video' | 'image'; + title: string; + url: string; +} + const plugin: JupyterFrontEndPlugin = { - id: 'lichi:plugin', - description: 'Lichi is a ai client for jupyter lab', + id: 'litchi-apod', autoStart: true, - optional: [ISettingRegistry], - activate: (app: JupyterFrontEnd, settingRegistry: ISettingRegistry | null) => { - console.log('JupyterLab extension lichi is activated!'); - - if (settingRegistry) { - settingRegistry - .load(plugin.id) - .then(settings => { - console.log('lichi settings loaded:', settings.composite); - }) - .catch(reason => { - console.error('Failed to load settings for lichi.', reason); - }); + requires: [ICommandPalette], + optional: [ILayoutRestorer, ISettingRegistry], + activate: activate +}; + +/** + * Initialization data for the litchi extension. + */ +function activate( + app: JupyterFrontEnd, + palette: ICommandPalette, + restorer: ILayoutRestorer | null, + settingRegistry: ISettingRegistry | null +) { + console.log('JupyterLab extension litchi is activated!'); + console.log('ICommandPalette:', palette); + + const newWidget = () => { + const content = new APODWidget(); + const widget = new MainAreaWidget({ content }); + widget.id = 'litchi-settings'; + widget.title.label = 'Litchi Settings'; + widget.title.closable = true; + return widget; + }; + + let widget = newWidget(); + // Add an application command + const command: string = 'litchi:settings'; + app.commands.addCommand(command, { + label: 'litchi:settings', + execute: async () => { + // Regenerate the widget if disposed + if (widget.isDisposed) { + widget = await newWidget(); + } + if (!widget.isAttached) { + // Attach the widget to the main work area if it's not there + app.shell.add(widget, 'main'); + } + // Refresh the picture in the widget + widget.content.updateAPODiImage(); + // Activate the widget + app.shell.activateById(widget.id); } + }); + // Add the command to the palette. + palette.addItem({ command, category: 'Litchi' }); + + // if (settingRegistry) { + // settingRegistry + // .load(plugin.id) + // .then(settings => { + // console.log('litchi settings loaded:', settings.composite); + // }) + // .catch(reason => { + // console.error('Failed to load settings for litchi.', reason); + // }); + // } + + // Track and restore the widget state + const tracker = new WidgetTracker>({ + namespace: 'litchi' + }); + if (restorer) { + restorer.restore(tracker, { + command, + name: () => 'litchi' + }); } -}; +} export default plugin; diff --git a/style/base.css b/style/base.css index e11f457..9af7489 100644 --- a/style/base.css +++ b/style/base.css @@ -3,3 +3,9 @@ https://jupyterlab.readthedocs.io/en/stable/developer/css.html */ +.litchiWidget { + display: flex; + flex-direction: column; + align-items: center; + overflow: auto; +} \ No newline at end of file diff --git a/ui-tests/package.json b/ui-tests/package.json index aaf992c..5c203dc 100644 --- a/ui-tests/package.json +++ b/ui-tests/package.json @@ -1,7 +1,7 @@ { - "name": "lichi-ui-tests", + "name": "litchi-ui-tests", "version": "1.0.0", - "description": "JupyterLab lichi Integration Tests", + "description": "JupyterLab litchi Integration Tests", "private": true, "scripts": { "start": "jupyter lab --config jupyter_server_test_config.py", diff --git a/ui-tests/tests/lichi.spec.ts b/ui-tests/tests/litchi.spec.ts similarity index 85% rename from ui-tests/tests/lichi.spec.ts rename to ui-tests/tests/litchi.spec.ts index 82edbba..8b6ae15 100644 --- a/ui-tests/tests/lichi.spec.ts +++ b/ui-tests/tests/litchi.spec.ts @@ -16,6 +16,6 @@ test('should emit an activation console message', async ({ page }) => { await page.goto(); expect( - logs.filter(s => s === 'JupyterLab extension lichi is activated!') + logs.filter(s => s === 'JupyterLab extension litchi is activated!') ).toHaveLength(1); });