diff --git a/package.json b/package.json index 4574006..b5ecd66 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,13 @@ "url": "https://github.com/jupyterlab/jupyter-chat.git" }, "workspaces": [ - "packages/*" + "packages/*", + "python/jupyterlab-collaborative-chat" ], "scripts": { "build": "lerna run build --stream", "build:core": "lerna run build --stream --scope \"@jupyter/chat\"", - "build:collaborative": "lerna run build --scope=jupyterlab-collaborative-chat --include-filtered-dependencies", + "build:collaborative": "lerna run build --scope=jupyterlab-collaborative-chat-extension --include-filtered-dependencies", "build:ws": "lerna run build --scope=jupyterlab-ws-chat --include-filtered-dependencies", "build:prod": "lerna run build:prod --stream", "clean": "lerna run clean", diff --git a/packages/jupyter-chat/package.json b/packages/jupyter-chat/package.json index cb68689..8a546d3 100644 --- a/packages/jupyter-chat/package.json +++ b/packages/jupyter-chat/package.json @@ -102,20 +102,6 @@ "publishConfig": { "access": "public" }, - "jupyterlab": { - "discovery": { - "server": { - "managers": [ - "pip" - ], - "base": { - "name": "jupyter_chat" - } - } - }, - "extension": false, - "schemaDir": "schema" - }, "eslintIgnore": [ "node_modules", "dist", diff --git a/packages/jupyterlab-collaborative-chat/package.json b/packages/jupyterlab-collaborative-chat/package.json index b300d0d..e0eb228 100644 --- a/packages/jupyterlab-collaborative-chat/package.json +++ b/packages/jupyterlab-collaborative-chat/package.json @@ -1,7 +1,7 @@ { "name": "jupyterlab-collaborative-chat", "version": "0.3.1", - "description": "A chat extension based on shared documents", + "description": "The library to build a chat based on shared document", "keywords": [ "jupyter", "jupyterlab", @@ -19,8 +19,7 @@ "files": [ "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}", - "src/**/*.{ts,tsx}", - "schema/*.json" + "src/**/*.{ts,tsx}" ], "main": "lib/index.js", "types": "lib/index.d.ts", @@ -30,17 +29,14 @@ "url": "https://github.com/jupyterlab/jupyter-chat.git" }, "scripts": { - "build": "jlpm build:lib && jlpm build:labextension:dev", - "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension", - "build:labextension": "jupyter labextension build .", - "build:labextension:dev": "jupyter labextension build --development True .", + "build": "jlpm build:lib", + "build:prod": "jlpm clean && jlpm build:lib:prod", "build:lib": "tsc --sourceMap", "build:lib:prod": "tsc", "clean": "jlpm clean:lib", "clean:lib": "rimraf lib tsconfig.tsbuildinfo", "clean:lintcache": "rimraf .eslintcache .stylelintcache", - "clean:labextension": "rimraf jupyterlab_collaborative_chat/labextension jupyterlab_collaborative_chat/_version.py", - "clean:all": "jlpm clean:lib && jlpm clean:labextension && jlpm clean:lintcache", + "clean:all": "jlpm clean:lib && jlpm clean:lintcache", "eslint": "jlpm eslint:check --fix", "eslint:check": "eslint . --cache --ext .ts,.tsx", "install:extension": "jlpm build", @@ -52,9 +48,7 @@ "stylelint": "jlpm stylelint:check --fix", "stylelint:check": "stylelint --cache \"style/**/*.css\"", "test": "jest --coverage", - "watch": "run-p watch:src watch:labextension", - "watch:src": "tsc -w --sourceMap", - "watch:labextension": "jupyter labextension watch ." + "watch:src": "tsc -w --sourceMap" }, "dependencies": { "@jupyter/chat": "^0.3.1", @@ -81,8 +75,6 @@ "yjs": "^13.5.40" }, "devDependencies": { - "@jupyterlab/builder": "^4.2.0", - "@jupyterlab/testutils": "^4.2.0", "@types/jest": "^29.2.0", "@types/json-schema": "^7.0.11", "@types/react": "^18.2.0", @@ -116,27 +108,6 @@ "publishConfig": { "access": "public" }, - "jupyterlab": { - "discovery": { - "server": { - "managers": [ - "pip" - ], - "base": { - "name": "jupyterlab_collaborative_chat" - } - } - }, - "extension": true, - "outputDir": "jupyterlab_collaborative_chat/labextension", - "schemaDir": "schema", - "sharedPackages": { - "@jupyter/docprovider": { - "bundled": true, - "singleton": true - } - } - }, "eslintIgnore": [ "node_modules", "dist", diff --git a/packages/jupyterlab-collaborative-chat/src/index.ts b/packages/jupyterlab-collaborative-chat/src/index.ts index bbefa6e..9f4b455 100644 --- a/packages/jupyterlab-collaborative-chat/src/index.ts +++ b/packages/jupyterlab-collaborative-chat/src/index.ts @@ -1,642 +1,5 @@ -/* - * Copyright (c) Jupyter Development Team. - * Distributed under the terms of the Modified BSD License. - */ - -import { - ActiveCellManager, - AutocompletionRegistry, - IActiveCellManager, - IAutocompletionRegistry, - chatIcon, - readIcon -} from '@jupyter/chat'; -import { - ICollaborativeDrive, - SharedDocumentFactory -} from '@jupyter/docprovider'; -import { - ILayoutRestorer, - JupyterFrontEnd, - JupyterFrontEndPlugin -} from '@jupyterlab/application'; -import { - ICommandPalette, - IThemeManager, - IToolbarWidgetRegistry, - InputDialog, - WidgetTracker, - createToolbarFactory, - showErrorMessage -} from '@jupyterlab/apputils'; -import { PathExt } from '@jupyterlab/coreutils'; -import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { ILauncher } from '@jupyterlab/launcher'; -import { INotebookTracker } from '@jupyterlab/notebook'; -import { IObservableList } from '@jupyterlab/observables'; -import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; -import { Contents } from '@jupyterlab/services'; -import { ISettingRegistry } from '@jupyterlab/settingregistry'; -import { ITranslator, nullTranslator } from '@jupyterlab/translation'; -import { launchIcon } from '@jupyterlab/ui-components'; - -import { - WidgetConfig, - ChatWidgetFactory, - CollaborativeChatModelFactory -} from './factory'; -import { CollaborativeChatModel } from './model'; -import { - chatFileType, - CommandIDs, - IChatPanel, - IChatFactory, - IActiveCellManagerToken -} from './token'; -import { ChatPanel, CollaborativeChatPanel } from './widget'; -import { YChat } from './ychat'; - -const FACTORY = 'Chat'; - -const pluginIds = { - activeCellManager: 'jupyterlab-collaborative-chat:activeCellManager', - autocompletionRegistry: - 'jupyterlab-collaborative-chat:autocompletionRegistry', - chatCommands: 'jupyterlab-collaborative-chat:commands', - docFactories: 'jupyterlab-collaborative-chat:factory', - chatPanel: 'jupyterlab-collaborative-chat:chat-panel' -}; - -/** - * Extension providing the autocompletion registry. - */ -const autocompletionPlugin: JupyterFrontEndPlugin = { - id: pluginIds.autocompletionRegistry, - description: 'An autocompletion registry', - autoStart: true, - provides: IAutocompletionRegistry, - activate: (app: JupyterFrontEnd): IAutocompletionRegistry => { - return new AutocompletionRegistry(); - } -}; - -/** - * Extension registering the chat file type. - */ -const docFactories: JupyterFrontEndPlugin = { - id: pluginIds.docFactories, - description: 'A document factories for collaborative chat', - autoStart: true, - requires: [IRenderMimeRegistry], - optional: [ - IActiveCellManagerToken, - IAutocompletionRegistry, - ICollaborativeDrive, - ILayoutRestorer, - ISettingRegistry, - IThemeManager, - IToolbarWidgetRegistry, - ITranslator - ], - provides: IChatFactory, - activate: ( - app: JupyterFrontEnd, - rmRegistry: IRenderMimeRegistry, - activeCellManager: IActiveCellManager | null, - autocompletionRegistry: IAutocompletionRegistry, - drive: ICollaborativeDrive | null, - restorer: ILayoutRestorer | null, - settingRegistry: ISettingRegistry | null, - themeManager: IThemeManager | null, - toolbarRegistry: IToolbarWidgetRegistry | null, - translator_: ITranslator | null - ): IChatFactory => { - const translator = translator_ ?? nullTranslator; - - // Declare the toolbar factory. - let toolbarFactory: - | (( - widget: CollaborativeChatPanel - ) => - | DocumentRegistry.IToolbarItem[] - | IObservableList) - | undefined; - - /** - * Load the settings for the chat widgets. - */ - let sendWithShiftEnter = false; - let stackMessages = true; - let unreadNotifications = true; - let enableCodeToolbar = true; - function loadSetting(setting: ISettingRegistry.ISettings): void { - // Read the settings and convert to the correct type - sendWithShiftEnter = setting.get('sendWithShiftEnter') - .composite as boolean; - stackMessages = setting.get('stackMessages').composite as boolean; - unreadNotifications = setting.get('unreadNotifications') - .composite as boolean; - enableCodeToolbar = setting.get('enableCodeToolbar').composite as boolean; - widgetConfig.config = { - sendWithShiftEnter, - stackMessages, - unreadNotifications, - enableCodeToolbar - }; - } - - if (settingRegistry) { - // Create the main area widget toolbar factory. - if (toolbarRegistry) { - toolbarFactory = createToolbarFactory( - toolbarRegistry, - settingRegistry, - FACTORY, - pluginIds.docFactories, - translator - ); - } - - // Wait for the application to be restored and - // for the settings to be loaded - Promise.all([app.restored, settingRegistry.load(pluginIds.docFactories)]) - .then(([, setting]) => { - // Read the settings - loadSetting(setting); - - // Listen for the plugin setting changes - setting.changed.connect(loadSetting); - }) - .catch(reason => { - console.error( - `Something went wrong when reading the settings.\n${reason}` - ); - }); - } - - /** - * The chat config object. - */ - const widgetConfig = new WidgetConfig({ - sendWithShiftEnter, - stackMessages, - unreadNotifications, - enableCodeToolbar - }); - - // Namespace for the tracker - const namespace = 'chat'; - - // Creating the tracker for the document - const tracker = new WidgetTracker({ namespace }); - - app.docRegistry.addFileType(chatFileType); - - if (drive) { - const chatFactory: SharedDocumentFactory = () => { - return YChat.create(); - }; - drive.sharedModelFactory.registerDocumentFactory('chat', chatFactory); - } - - app.serviceManager.ready - .then(() => { - const user = app.serviceManager.user.identity; - // Creating and registering the model factory for our custom DocumentModel - const modelFactory = new CollaborativeChatModelFactory({ - user, - widgetConfig, - commands: app.commands, - activeCellManager - }); - app.docRegistry.addModelFactory(modelFactory); - }) - .catch(e => - console.error( - 'The collaborative chat model factory is not initialized', - e - ) - ); - - // Creating the widget factory to register it so the document manager knows about - // our new DocumentWidget - const widgetFactory = new ChatWidgetFactory({ - name: FACTORY, - label: 'Chat', - modelName: 'chat', - fileTypes: ['chat'], - defaultFor: ['chat'], - themeManager, - rmRegistry, - toolbarFactory, - translator, - autocompletionRegistry - }); - - // Add the widget to the tracker when it's created - widgetFactory.widgetCreated.connect((sender, widget) => { - // Notify the instance tracker if restore data needs to update. - widget.context.pathChanged.connect(() => { - tracker.save(widget); - }); - tracker.add(widget); - - // Update the 'markAsRead' command status when the unread changed. - widget.model.unreadChanged.connect(() => - app.commands.notifyCommandChanged(CommandIDs.markAsRead) - ); - }); - - // Registering the widget factory - app.docRegistry.addWidgetFactory(widgetFactory); - - // Handle state restoration. - if (restorer) { - void restorer.restore(tracker, { - command: 'docmanager:open', - args: panel => ({ path: panel.context.path, factory: FACTORY }), - name: panel => panel.context.path, - when: app.serviceManager.ready - }); - } - - return { widgetConfig, tracker }; - } -}; - -/** - * Extension providing the commands, menu and laucher. - */ -const chatCommands: JupyterFrontEndPlugin = { - id: pluginIds.chatCommands, - description: 'The commands to create or open a chat', - autoStart: true, - requires: [ICollaborativeDrive, IChatFactory], - optional: [IActiveCellManagerToken, IChatPanel, ICommandPalette, ILauncher], - activate: ( - app: JupyterFrontEnd, - drive: ICollaborativeDrive, - factory: IChatFactory, - activeCellManager: IActiveCellManager | null, - chatPanel: ChatPanel | null, - commandPalette: ICommandPalette | null, - launcher: ILauncher | null - ) => { - const { commands } = app; - const { tracker, widgetConfig } = factory; - /** - * Command to create a new chat. - * - * args: - * name - optional, the name of the chat to create. - * Open a dialog if not provided. - * inSidePanel - optional (default to false). - * Whether to open the chat in side panel or in main area. - * isPalette - optional (default to false). - * Whether the command is in commands palette or not. - */ - commands.addCommand(CommandIDs.createChat, { - label: args => (args.isPalette ? 'Create a new chat' : 'Chat'), - caption: 'Create a chat', - icon: args => (args.isPalette ? undefined : chatIcon), - execute: async args => { - const inSidePanel: boolean = (args.inSidePanel as boolean) ?? false; - let name: string | null = (args.name as string) ?? null; - let filepath = ''; - if (!name) { - name = ( - await InputDialog.getText({ - label: 'Name', - placeholder: 'untitled', - title: 'Create a new chat' - }) - ).value; - } - // no-op if the dialog has been cancelled. - // Fill the filepath if the dialog has been validated with content, - // otherwise create a new untitled chat (empty filepath). - if (name === null) { - return; - } else if (name) { - if (name.endsWith(chatFileType.extensions[0])) { - filepath = name; - } else { - filepath = `${name}${chatFileType.extensions[0]}`; - } - } - - let fileExist = true; - if (filepath) { - await drive.get(filepath, { content: false }).catch(() => { - fileExist = false; - }); - } else { - fileExist = false; - } - - // Create a new file if it does not exists - if (!fileExist) { - // Create a new untitled chat. - let model: Contents.IModel | null = await drive.newUntitled({ - type: 'file', - ext: chatFileType.extensions[0] - }); - // Rename it if a name has been provided. - if (filepath) { - model = await drive.rename(model.path, filepath); - } - - if (!model) { - showErrorMessage( - 'Error creating a chat', - 'An error occured while creating the chat' - ); - return ''; - } - filepath = model.path; - } - - if (commands.hasCommand(CommandIDs.openChat)) { - return commands.execute(CommandIDs.openChat, { - filepath, - inSidePanel - }); - } else { - commands.execute('docmanager:open', { - path: `RTC:${filepath}`, - factory: FACTORY - }); - } - } - }); - - // Add the command to the palette - if (commandPalette) { - commandPalette.addItem({ - category: 'Chat', - command: CommandIDs.createChat, - args: { isPalette: true } - }); - } - - // Add the create command to the launcher - if (launcher) { - launcher.add({ - command: CommandIDs.createChat, - category: 'Chat', - rank: 1 - }); - } - - // The command to mark the chat as read. - commands.addCommand(CommandIDs.markAsRead, { - caption: 'Mark chat as read', - icon: readIcon, - isEnabled: () => - tracker.currentWidget !== null && - tracker.currentWidget === app.shell.currentWidget && - tracker.currentWidget.model.unreadMessages.length > 0, - execute: async args => { - const widget = app.shell.currentWidget; - // Ensure widget is a CollaborativeChatPanel and is in main area - if ( - !widget || - !(widget instanceof CollaborativeChatPanel) || - !Array.from(app.shell.widgets('main')).includes(widget) - ) { - console.error( - `The command '${CommandIDs.markAsRead}' should be executed from the toolbar button only` - ); - return; - } - widget.model.unreadMessages = []; - } - }); - - // Update the 'markAsRead' command status when the current widget changes. - tracker.currentChanged.connect(() => { - commands.notifyCommandChanged(CommandIDs.markAsRead); - }); - - app.serviceManager.ready - .then(() => { - const user = app.serviceManager.user.identity; - /* - * Command to open a chat. - * - * args: - * filepath - the chat file to open. - */ - commands.addCommand(CommandIDs.openChat, { - label: 'Open a chat', - execute: async args => { - const inSidePanel: boolean = (args.inSidePanel as boolean) ?? false; - let filepath: string | null = (args.filepath as string) ?? null; - if (filepath === null) { - filepath = ( - await InputDialog.getText({ - label: 'File path', - placeholder: '/path/to/the/chat/file', - title: 'Path of the chat' - }) - ).value; - } - - if (!filepath) { - return; - } - - let fileExist = true; - await drive.get(filepath, { content: false }).catch(() => { - fileExist = false; - }); - - if (!fileExist) { - showErrorMessage( - 'Error opening chat', - `'${filepath}' is not a valid path` - ); - return; - } - - if (inSidePanel && chatPanel) { - // The chat is opened in the chat panel. - app.shell.activateById(chatPanel.id); - const model = await drive.get(filepath); - - /** - * Create a share model from the chat file - */ - const sharedModel = drive.sharedModelFactory.createNew({ - path: model.path, - format: model.format, - contentType: chatFileType.contentType, - collaborative: true - }) as YChat; - - /** - * Initialize the chat model with the share model - */ - const chat = new CollaborativeChatModel({ - user, - sharedModel, - widgetConfig, - commands: app.commands, - activeCellManager - }); - - /** - * Add a chat widget to the side panel. - */ - chatPanel.addChat( - chat, - PathExt.basename(model.name, chatFileType.extensions[0]) - ); - } else { - // The chat is opened in the main area - commands.execute('docmanager:open', { - path: `RTC:${filepath}`, - factory: FACTORY - }); - } - } - }); - - // Add the command to the palette - if (commandPalette) { - commandPalette.addItem({ - category: 'Chat', - command: CommandIDs.openChat - }); - } - }) - .catch(e => - console.error('The command to open a chat is not initialized\n', e) - ); - } -}; - -/* - * Extension providing a chat panel. - */ -const chatPanel: JupyterFrontEndPlugin = { - id: pluginIds.chatPanel, - description: 'A chat extension for Jupyter', - autoStart: true, - provides: IChatPanel, - requires: [ICollaborativeDrive, IRenderMimeRegistry], - optional: [ - IAutocompletionRegistry, - ILayoutRestorer, - INotebookTracker, - IThemeManager - ], - activate: ( - app: JupyterFrontEnd, - drive: ICollaborativeDrive, - rmRegistry: IRenderMimeRegistry, - autocompletionRegistry: IAutocompletionRegistry, - restorer: ILayoutRestorer | null, - notebookTracker: INotebookTracker, - themeManager: IThemeManager | null - ): ChatPanel => { - const { commands } = app; - - /** - * Add Chat widget to left sidebar - */ - const chatPanel = new ChatPanel({ - commands, - drive, - rmRegistry, - themeManager, - autocompletionRegistry - }); - chatPanel.id = 'JupyterCollaborationChat:sidepanel'; - chatPanel.title.icon = chatIcon; - chatPanel.title.caption = 'Jupyter Chat'; // TODO: i18n/ - - app.shell.add(chatPanel, 'left', { - rank: 2000 - }); - - if (restorer) { - restorer.add(chatPanel, 'jupyter-chat'); - } - - // Use events system to watch changes on files. - const schemaID = - 'https://events.jupyter.org/jupyter_server/contents_service/v1'; - const actions = ['create', 'delete', 'rename']; - app.serviceManager.events.stream.connect((_, emission) => { - if (emission.schema_id === schemaID) { - const action = emission.action as string; - if (actions.includes(action)) { - chatPanel.updateChatNames(); - } - } - }); - - /* - * Command to move a chat from the main area to the side panel. - * - */ - commands.addCommand(CommandIDs.moveToSide, { - label: 'Move the chat to the side panel', - caption: 'Move the chat to the side panel', - icon: launchIcon, - isEnabled: () => commands.hasCommand(CommandIDs.openChat), - execute: async () => { - const widget = app.shell.currentWidget; - // Ensure widget is a CollaborativeChatPanel and is in main area - if ( - !widget || - !(widget instanceof CollaborativeChatPanel) || - !Array.from(app.shell.widgets('main')).includes(widget) - ) { - console.error( - `The command '${CommandIDs.moveToSide}' should be executed from the toolbar button only` - ); - return; - } - // Remove potential drive prefix - const filepath = widget.context.path.split(':').pop(); - commands.execute(CommandIDs.openChat, { - filepath, - inSidePanel: true - }); - widget.dispose(); - } - }); - - return chatPanel; - } -}; - -/** - * Extension providing the active cell manager. - */ -const activeCellManager: JupyterFrontEndPlugin = { - id: pluginIds.activeCellManager, - description: 'the active cell manager plugin', - autoStart: true, - requires: [INotebookTracker], - provides: IActiveCellManagerToken, - activate: ( - app: JupyterFrontEnd, - notebookTracker: INotebookTracker - ): IActiveCellManager => { - return new ActiveCellManager({ - tracker: notebookTracker, - shell: app.shell - }); - } -}; - -export default [ - activeCellManager, - autocompletionPlugin, - chatCommands, - docFactories, - chatPanel -]; +export * from './factory'; +export * from './model'; +export * from './token'; +export * from './widget'; +export * from './ychat'; diff --git a/pyproject.toml b/pyproject.toml index 7d806ad..89c896c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ fields = ["description", "authors", "urls"] [tool.jupyter-releaser.options] version-cmd = "cd ../.. && python scripts/bump_version.py --force --skip-if-dirty" python_packages = [ - "packages/jupyterlab-collaborative-chat", + "python/jupyterlab-collaborative-chat", "packages/jupyterlab-ws-chat" ] diff --git a/packages/jupyterlab-collaborative-chat/.copier-answers.yml b/python/jupyterlab-collaborative-chat/.copier-answers.yml similarity index 90% rename from packages/jupyterlab-collaborative-chat/.copier-answers.yml rename to python/jupyterlab-collaborative-chat/.copier-answers.yml index 2a2965e..bbfa1c0 100644 --- a/packages/jupyterlab-collaborative-chat/.copier-answers.yml +++ b/python/jupyterlab-collaborative-chat/.copier-answers.yml @@ -8,7 +8,7 @@ file_extension: '' has_binder: true has_settings: true kind: server -labextension_name: jupyterlab-collaborative-chat +labextension_name: jupyterlab-collaborative-chat-extension mimetype: '' mimetype_name: '' project_short_description: A chat extension based on shared documents diff --git a/python/jupyterlab-collaborative-chat/.gitignore b/python/jupyterlab-collaborative-chat/.gitignore new file mode 100644 index 0000000..6f5b435 --- /dev/null +++ b/python/jupyterlab-collaborative-chat/.gitignore @@ -0,0 +1,125 @@ +*.bundle.* +lib/ +node_modules/ +*.log +.eslintcache +.stylelintcache +*.egg-info/ +.ipynb_checkpoints +*.tsbuildinfo +jupyterlab_collaborative_chat/labextension +# Version file is handled by hatchling +jupyterlab_collaborative_chat/_version.py + +# Integration tests +ui-tests/test-results/ +ui-tests/playwright-report/ + +# Created by https://www.gitignore.io/api/python +# Edit at https://www.gitignore.io/?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage/ +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# End of https://www.gitignore.io/api/python + +# OSX files +.DS_Store + +# Yarn cache +.yarn/ diff --git a/packages/jupyterlab-collaborative-chat/.prettierignore b/python/jupyterlab-collaborative-chat/.prettierignore similarity index 100% rename from packages/jupyterlab-collaborative-chat/.prettierignore rename to python/jupyterlab-collaborative-chat/.prettierignore diff --git a/packages/jupyterlab-collaborative-chat/CHANGELOG.md b/python/jupyterlab-collaborative-chat/CHANGELOG.md similarity index 100% rename from packages/jupyterlab-collaborative-chat/CHANGELOG.md rename to python/jupyterlab-collaborative-chat/CHANGELOG.md diff --git a/packages/jupyterlab-collaborative-chat/LICENSE b/python/jupyterlab-collaborative-chat/LICENSE similarity index 100% rename from packages/jupyterlab-collaborative-chat/LICENSE rename to python/jupyterlab-collaborative-chat/LICENSE diff --git a/packages/jupyterlab-collaborative-chat/README.md b/python/jupyterlab-collaborative-chat/README.md similarity index 99% rename from packages/jupyterlab-collaborative-chat/README.md rename to python/jupyterlab-collaborative-chat/README.md index 62eba39..a1f9da5 100644 --- a/packages/jupyterlab-collaborative-chat/README.md +++ b/python/jupyterlab-collaborative-chat/README.md @@ -4,7 +4,7 @@ A chat extension based on shared documents This extension is composed of a Python package named `jupyterlab_collaborative_chat` -for the server extension and a NPM package named `jupyterlab-collaborative-chat` +for the server extension and a NPM package named `jupyterlab-collaborative-chat-extension` for the frontend extension. This extension registers a `YChat` shared document, and associate the document to a diff --git a/packages/jupyterlab-collaborative-chat/RELEASE.md b/python/jupyterlab-collaborative-chat/RELEASE.md similarity index 100% rename from packages/jupyterlab-collaborative-chat/RELEASE.md rename to python/jupyterlab-collaborative-chat/RELEASE.md diff --git a/packages/jupyterlab-collaborative-chat/binder/environment.yml b/python/jupyterlab-collaborative-chat/binder/environment.yml similarity index 100% rename from packages/jupyterlab-collaborative-chat/binder/environment.yml rename to python/jupyterlab-collaborative-chat/binder/environment.yml diff --git a/packages/jupyterlab-collaborative-chat/binder/postBuild b/python/jupyterlab-collaborative-chat/binder/postBuild similarity index 100% rename from packages/jupyterlab-collaborative-chat/binder/postBuild rename to python/jupyterlab-collaborative-chat/binder/postBuild diff --git a/packages/jupyterlab-collaborative-chat/conftest.py b/python/jupyterlab-collaborative-chat/conftest.py similarity index 100% rename from packages/jupyterlab-collaborative-chat/conftest.py rename to python/jupyterlab-collaborative-chat/conftest.py diff --git a/packages/jupyterlab-collaborative-chat/install.json b/python/jupyterlab-collaborative-chat/install.json similarity index 100% rename from packages/jupyterlab-collaborative-chat/install.json rename to python/jupyterlab-collaborative-chat/install.json diff --git a/packages/jupyterlab-collaborative-chat/jupyter-config/server-config/jupyterlab_collaborative_chat.json b/python/jupyterlab-collaborative-chat/jupyter-config/server-config/jupyterlab_collaborative_chat.json similarity index 100% rename from packages/jupyterlab-collaborative-chat/jupyter-config/server-config/jupyterlab_collaborative_chat.json rename to python/jupyterlab-collaborative-chat/jupyter-config/server-config/jupyterlab_collaborative_chat.json diff --git a/packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/__init__.py b/python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/__init__.py similarity index 95% rename from packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/__init__.py rename to python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/__init__.py index 0418343..cd3f1cf 100644 --- a/packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/__init__.py +++ b/python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/__init__.py @@ -15,7 +15,7 @@ def _jupyter_labextension_paths(): return [{ "src": "labextension", - "dest": "jupyterlab-collaborative-chat" + "dest": "jupyterlab-collaborative-chat-extension" }] diff --git a/packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/__init__.py b/python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/__init__.py similarity index 100% rename from packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/__init__.py rename to python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/__init__.py diff --git a/packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/test_handlers.py b/python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/test_handlers.py similarity index 100% rename from packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/test_handlers.py rename to python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/tests/test_handlers.py diff --git a/packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/ychat.py b/python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/ychat.py similarity index 100% rename from packages/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/ychat.py rename to python/jupyterlab-collaborative-chat/jupyterlab_collaborative_chat/ychat.py diff --git a/python/jupyterlab-collaborative-chat/package.json b/python/jupyterlab-collaborative-chat/package.json new file mode 100644 index 0000000..0c01973 --- /dev/null +++ b/python/jupyterlab-collaborative-chat/package.json @@ -0,0 +1,238 @@ +{ + "name": "jupyterlab-collaborative-chat-extension", + "version": "0.3.1", + "description": "A chat extension based on shared documents", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/jupyterlab/jupyter-chat", + "bugs": { + "url": "https://github.com/jupyterlab/jupyter-chat/issues" + }, + "license": "BSD-3-Clause", + "author": { + "name": "Jupyter Development Team", + "email": "jupyter@googlegroups.com" + }, + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "src/**/*.{ts,tsx}", + "schema/*.json" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "style": "style/index.css", + "repository": { + "type": "git", + "url": "https://github.com/jupyterlab/jupyter-chat.git" + }, + "scripts": { + "build": "jlpm build:lib && jlpm build:labextension:dev", + "build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension", + "build:labextension": "jupyter labextension build .", + "build:labextension:dev": "jupyter labextension build --development True .", + "build:lib": "tsc --sourceMap", + "build:lib:prod": "tsc", + "clean": "jlpm clean:lib", + "clean:lib": "rimraf lib tsconfig.tsbuildinfo", + "clean:lintcache": "rimraf .eslintcache .stylelintcache", + "clean:labextension": "rimraf jupyterlab_collaborative_chat/labextension jupyterlab_collaborative_chat/_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", + "install:extension": "jlpm build", + "lint": "jlpm stylelint && jlpm prettier && jlpm eslint", + "lint:check": "jlpm stylelint:check && jlpm prettier:check && jlpm eslint:check", + "prettier": "jlpm prettier:base --write --list-different", + "prettier:base": "prettier \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md}\"", + "prettier:check": "jlpm prettier:base --check", + "stylelint": "jlpm stylelint:check --fix", + "stylelint:check": "stylelint --cache \"style/**/*.css\"", + "test": "jest --coverage", + "watch": "run-p watch:src watch:labextension", + "watch:src": "tsc -w --sourceMap", + "watch:labextension": "jupyter labextension watch ." + }, + "dependencies": { + "@jupyter/collaboration": "^2.1.0", + "@jupyter/docprovider": "^2.1.0", + "@jupyter/ydoc": "^1.1.1", + "@jupyterlab/application": "^4.2.0", + "@jupyterlab/apputils": "^4.3.0", + "@jupyterlab/coreutils": "^6.2.0", + "@jupyterlab/docregistry": "^4.2.0", + "@jupyterlab/launcher": "^4.2.0", + "@jupyterlab/notebook": "^4.2.0", + "@jupyterlab/rendermime": "^4.2.0", + "@jupyterlab/services": "^7.2.0", + "@jupyterlab/settingregistry": "^4.2.0", + "@jupyterlab/translation": "^4.2.0", + "@jupyterlab/ui-components": "^4.2.0", + "@lumino/commands": "^2.0.0", + "@lumino/coreutils": "^2.0.0", + "@lumino/signaling": "^2.0.0", + "@lumino/widgets": "^2.0.0", + "jupyterlab-collaborative-chat": "^0.3.1", + "react": "^18.2.0", + "y-protocols": "^1.0.5", + "yjs": "^13.5.40" + }, + "devDependencies": { + "@jupyterlab/builder": "^4.2.0", + "@jupyterlab/testutils": "^4.2.0", + "@types/jest": "^29.2.0", + "@types/json-schema": "^7.0.11", + "@types/react": "^18.2.0", + "@types/react-addons-linked-state-mixin": "^0.14.22", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", + "css-loader": "^6.7.1", + "eslint": "^8.36.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0", + "jest": "^29.2.0", + "mkdirp": "^1.0.3", + "npm-run-all": "^4.1.5", + "prettier": "^3.0.0", + "rimraf": "^5.0.1", + "source-map-loader": "^1.0.2", + "style-loader": "^3.3.1", + "stylelint": "^15.10.1", + "stylelint-config-recommended": "^13.0.0", + "stylelint-config-standard": "^34.0.0", + "stylelint-csstree-validator": "^3.0.0", + "stylelint-prettier": "^4.0.0", + "typescript": "~5.0.2", + "yjs": "^13.5.0" + }, + "sideEffects": [ + "style/*.css", + "style/index.js" + ], + "styleModule": "style/index.js", + "publishConfig": { + "access": "public" + }, + "jupyterlab": { + "discovery": { + "server": { + "managers": [ + "pip" + ], + "base": { + "name": "jupyterlab_collaborative_chat" + } + } + }, + "extension": true, + "outputDir": "jupyterlab_collaborative_chat/labextension", + "schemaDir": "schema", + "sharedPackages": { + "@jupyter/docprovider": { + "bundled": true, + "singleton": true + }, + "jupyterlab-collaborative-chat": { + "bundled": true, + "singleton": true + } + } + }, + "eslintIgnore": [ + "node_modules", + "dist", + "coverage", + "**/*.d.ts", + "tests", + "**/__tests__", + "ui-tests" + ], + "eslintConfig": { + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "interface", + "format": [ + "PascalCase" + ], + "custom": { + "regex": "^I[A-Z]", + "match": true + } + } + ], + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "none" + } + ], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/quotes": [ + "error", + "single", + { + "avoidEscape": true, + "allowTemplateLiterals": false + } + ], + "curly": [ + "error", + "all" + ], + "eqeqeq": "error", + "prefer-arrow-callback": "error" + } + }, + "prettier": { + "singleQuote": true, + "trailingComma": "none", + "arrowParens": "avoid", + "endOfLine": "auto", + "overrides": [ + { + "files": "package.json", + "options": { + "tabWidth": 2 + } + } + ] + }, + "stylelint": { + "extends": [ + "stylelint-config-recommended", + "stylelint-config-standard", + "stylelint-prettier/recommended" + ], + "plugins": [ + "stylelint-csstree-validator" + ], + "rules": { + "csstree/validator": true, + "property-no-vendor-prefix": null, + "selector-class-pattern": "^([a-z][A-z\\d]*)(-[A-z\\d]+)*$", + "selector-no-vendor-prefix": null, + "value-no-vendor-prefix": null + } + } +} diff --git a/packages/jupyterlab-collaborative-chat/pyproject.toml b/python/jupyterlab-collaborative-chat/pyproject.toml similarity index 98% rename from packages/jupyterlab-collaborative-chat/pyproject.toml rename to python/jupyterlab-collaborative-chat/pyproject.toml index 8a59677..f5c3e0d 100644 --- a/packages/jupyterlab-collaborative-chat/pyproject.toml +++ b/python/jupyterlab-collaborative-chat/pyproject.toml @@ -54,7 +54,7 @@ artifacts = ["jupyterlab_collaborative_chat/labextension"] exclude = [".github", "binder"] [tool.hatch.build.targets.wheel.shared-data] -"jupyterlab_collaborative_chat/labextension" = "share/jupyter/labextensions/jupyterlab-collaborative-chat" +"jupyterlab_collaborative_chat/labextension" = "share/jupyter/labextensions/jupyterlab-collaborative-chat-extension" "install.json" = "share/jupyter/labextensions/jupyterlab-collaborative-chat/install.json" "jupyter-config/server-config" = "etc/jupyter/jupyter_server_config.d" diff --git a/packages/jupyterlab-collaborative-chat/schema/chat-panel.json b/python/jupyterlab-collaborative-chat/schema/chat-panel.json similarity index 100% rename from packages/jupyterlab-collaborative-chat/schema/chat-panel.json rename to python/jupyterlab-collaborative-chat/schema/chat-panel.json diff --git a/packages/jupyterlab-collaborative-chat/schema/commands.json b/python/jupyterlab-collaborative-chat/schema/commands.json similarity index 100% rename from packages/jupyterlab-collaborative-chat/schema/commands.json rename to python/jupyterlab-collaborative-chat/schema/commands.json diff --git a/packages/jupyterlab-collaborative-chat/schema/factory.json b/python/jupyterlab-collaborative-chat/schema/factory.json similarity index 100% rename from packages/jupyterlab-collaborative-chat/schema/factory.json rename to python/jupyterlab-collaborative-chat/schema/factory.json diff --git a/packages/jupyterlab-collaborative-chat/screenshot.gif b/python/jupyterlab-collaborative-chat/screenshot.gif similarity index 100% rename from packages/jupyterlab-collaborative-chat/screenshot.gif rename to python/jupyterlab-collaborative-chat/screenshot.gif diff --git a/packages/jupyterlab-collaborative-chat/setup.py b/python/jupyterlab-collaborative-chat/setup.py similarity index 100% rename from packages/jupyterlab-collaborative-chat/setup.py rename to python/jupyterlab-collaborative-chat/setup.py diff --git a/python/jupyterlab-collaborative-chat/src/index.ts b/python/jupyterlab-collaborative-chat/src/index.ts new file mode 100644 index 0000000..5d88d99 --- /dev/null +++ b/python/jupyterlab-collaborative-chat/src/index.ts @@ -0,0 +1,641 @@ +/* + * Copyright (c) Jupyter Development Team. + * Distributed under the terms of the Modified BSD License. + */ + +import { + ActiveCellManager, + AutocompletionRegistry, + IActiveCellManager, + IAutocompletionRegistry, + chatIcon, + readIcon +} from '@jupyter/chat'; +import { + ICollaborativeDrive, + SharedDocumentFactory +} from '@jupyter/docprovider'; +import { + ILayoutRestorer, + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; +import { + ICommandPalette, + IThemeManager, + IToolbarWidgetRegistry, + InputDialog, + WidgetTracker, + createToolbarFactory, + showErrorMessage +} from '@jupyterlab/apputils'; +import { PathExt } from '@jupyterlab/coreutils'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { ILauncher } from '@jupyterlab/launcher'; +import { INotebookTracker } from '@jupyterlab/notebook'; +import { IObservableList } from '@jupyterlab/observables'; +import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; +import { Contents } from '@jupyterlab/services'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; +import { ITranslator, nullTranslator } from '@jupyterlab/translation'; +import { launchIcon } from '@jupyterlab/ui-components'; +import { + IActiveCellManagerToken, + chatFileType, + ChatPanel, + ChatWidgetFactory, + CollaborativeChatModel, + CollaborativeChatModelFactory, + CollaborativeChatPanel, + CommandIDs, + IChatFactory, + IChatPanel, + WidgetConfig, + YChat +} from 'jupyterlab-collaborative-chat'; + +const FACTORY = 'Chat'; + +const pluginIds = { + activeCellManager: + 'jupyterlab-collaborative-chat-extension:activeCellManager', + autocompletionRegistry: + 'jupyterlab-collaborative-chat-extension:autocompletionRegistry', + chatCommands: 'jupyterlab-collaborative-chat-extension:commands', + docFactories: 'jupyterlab-collaborative-chat-extension:factory', + chatPanel: 'jupyterlab-collaborative-chat-extension:chat-panel' +}; + +/** + * Extension providing the autocompletion registry. + */ +const autocompletionPlugin: JupyterFrontEndPlugin = { + id: pluginIds.autocompletionRegistry, + description: 'An autocompletion registry', + autoStart: true, + provides: IAutocompletionRegistry, + activate: (app: JupyterFrontEnd): IAutocompletionRegistry => { + return new AutocompletionRegistry(); + } +}; + +/** + * Extension registering the chat file type. + */ +const docFactories: JupyterFrontEndPlugin = { + id: pluginIds.docFactories, + description: 'A document factories for collaborative chat', + autoStart: true, + requires: [IRenderMimeRegistry], + optional: [ + IActiveCellManagerToken, + IAutocompletionRegistry, + ICollaborativeDrive, + ILayoutRestorer, + ISettingRegistry, + IThemeManager, + IToolbarWidgetRegistry, + ITranslator + ], + provides: IChatFactory, + activate: ( + app: JupyterFrontEnd, + rmRegistry: IRenderMimeRegistry, + activeCellManager: IActiveCellManager | null, + autocompletionRegistry: IAutocompletionRegistry, + drive: ICollaborativeDrive | null, + restorer: ILayoutRestorer | null, + settingRegistry: ISettingRegistry | null, + themeManager: IThemeManager | null, + toolbarRegistry: IToolbarWidgetRegistry | null, + translator_: ITranslator | null + ): IChatFactory => { + const translator = translator_ ?? nullTranslator; + + // Declare the toolbar factory. + let toolbarFactory: + | (( + widget: CollaborativeChatPanel + ) => + | DocumentRegistry.IToolbarItem[] + | IObservableList) + | undefined; + + /** + * Load the settings for the chat widgets. + */ + let sendWithShiftEnter = false; + let stackMessages = true; + let unreadNotifications = true; + let enableCodeToolbar = true; + function loadSetting(setting: ISettingRegistry.ISettings): void { + // Read the settings and convert to the correct type + sendWithShiftEnter = setting.get('sendWithShiftEnter') + .composite as boolean; + stackMessages = setting.get('stackMessages').composite as boolean; + unreadNotifications = setting.get('unreadNotifications') + .composite as boolean; + enableCodeToolbar = setting.get('enableCodeToolbar').composite as boolean; + widgetConfig.config = { + sendWithShiftEnter, + stackMessages, + unreadNotifications, + enableCodeToolbar + }; + } + + if (settingRegistry) { + // Create the main area widget toolbar factory. + if (toolbarRegistry) { + toolbarFactory = createToolbarFactory( + toolbarRegistry, + settingRegistry, + FACTORY, + pluginIds.docFactories, + translator + ); + } + + // Wait for the application to be restored and + // for the settings to be loaded + Promise.all([app.restored, settingRegistry.load(pluginIds.docFactories)]) + .then(([, setting]) => { + // Read the settings + loadSetting(setting); + + // Listen for the plugin setting changes + setting.changed.connect(loadSetting); + }) + .catch(reason => { + console.error( + `Something went wrong when reading the settings.\n${reason}` + ); + }); + } + + /** + * The chat config object. + */ + const widgetConfig = new WidgetConfig({ + sendWithShiftEnter, + stackMessages, + unreadNotifications, + enableCodeToolbar + }); + + // Namespace for the tracker + const namespace = 'chat'; + + // Creating the tracker for the document + const tracker = new WidgetTracker({ namespace }); + + app.docRegistry.addFileType(chatFileType); + + if (drive) { + const chatFactory: SharedDocumentFactory = () => { + return YChat.create(); + }; + drive.sharedModelFactory.registerDocumentFactory('chat', chatFactory); + } + + app.serviceManager.ready + .then(() => { + const user = app.serviceManager.user.identity; + // Creating and registering the model factory for our custom DocumentModel + const modelFactory = new CollaborativeChatModelFactory({ + user, + widgetConfig, + commands: app.commands, + activeCellManager + }); + app.docRegistry.addModelFactory(modelFactory); + }) + .catch(e => + console.error( + 'The collaborative chat model factory is not initialized', + e + ) + ); + + // Creating the widget factory to register it so the document manager knows about + // our new DocumentWidget + const widgetFactory = new ChatWidgetFactory({ + name: FACTORY, + label: 'Chat', + modelName: 'chat', + fileTypes: ['chat'], + defaultFor: ['chat'], + themeManager, + rmRegistry, + toolbarFactory, + translator, + autocompletionRegistry + }); + + // Add the widget to the tracker when it's created + widgetFactory.widgetCreated.connect((sender, widget) => { + // Notify the instance tracker if restore data needs to update. + widget.context.pathChanged.connect(() => { + tracker.save(widget); + }); + tracker.add(widget); + + // Update the 'markAsRead' command status when the unread changed. + widget.model.unreadChanged.connect(() => + app.commands.notifyCommandChanged(CommandIDs.markAsRead) + ); + }); + + // Registering the widget factory + app.docRegistry.addWidgetFactory(widgetFactory); + + // Handle state restoration. + if (restorer) { + void restorer.restore(tracker, { + command: 'docmanager:open', + args: panel => ({ path: panel.context.path, factory: FACTORY }), + name: panel => panel.context.path, + when: app.serviceManager.ready + }); + } + + return { widgetConfig, tracker }; + } +}; + +/** + * Extension providing the commands, menu and laucher. + */ +const chatCommands: JupyterFrontEndPlugin = { + id: pluginIds.chatCommands, + description: 'The commands to create or open a chat', + autoStart: true, + requires: [ICollaborativeDrive, IChatFactory], + optional: [IActiveCellManagerToken, IChatPanel, ICommandPalette, ILauncher], + activate: ( + app: JupyterFrontEnd, + drive: ICollaborativeDrive, + factory: IChatFactory, + activeCellManager: IActiveCellManager | null, + chatPanel: ChatPanel | null, + commandPalette: ICommandPalette | null, + launcher: ILauncher | null + ) => { + const { commands } = app; + const { tracker, widgetConfig } = factory; + /** + * Command to create a new chat. + * + * args: + * name - optional, the name of the chat to create. + * Open a dialog if not provided. + * inSidePanel - optional (default to false). + * Whether to open the chat in side panel or in main area. + * isPalette - optional (default to false). + * Whether the command is in commands palette or not. + */ + commands.addCommand(CommandIDs.createChat, { + label: args => (args.isPalette ? 'Create a new chat' : 'Chat'), + caption: 'Create a chat', + icon: args => (args.isPalette ? undefined : chatIcon), + execute: async args => { + const inSidePanel: boolean = (args.inSidePanel as boolean) ?? false; + let name: string | null = (args.name as string) ?? null; + let filepath = ''; + if (!name) { + name = ( + await InputDialog.getText({ + label: 'Name', + placeholder: 'untitled', + title: 'Create a new chat' + }) + ).value; + } + // no-op if the dialog has been cancelled. + // Fill the filepath if the dialog has been validated with content, + // otherwise create a new untitled chat (empty filepath). + if (name === null) { + return; + } else if (name) { + if (name.endsWith(chatFileType.extensions[0])) { + filepath = name; + } else { + filepath = `${name}${chatFileType.extensions[0]}`; + } + } + + let fileExist = true; + if (filepath) { + await drive.get(filepath, { content: false }).catch(() => { + fileExist = false; + }); + } else { + fileExist = false; + } + + // Create a new file if it does not exists + if (!fileExist) { + // Create a new untitled chat. + let model: Contents.IModel | null = await drive.newUntitled({ + type: 'file', + ext: chatFileType.extensions[0] + }); + // Rename it if a name has been provided. + if (filepath) { + model = await drive.rename(model.path, filepath); + } + + if (!model) { + showErrorMessage( + 'Error creating a chat', + 'An error occured while creating the chat' + ); + return ''; + } + filepath = model.path; + } + + if (commands.hasCommand(CommandIDs.openChat)) { + return commands.execute(CommandIDs.openChat, { + filepath, + inSidePanel + }); + } else { + commands.execute('docmanager:open', { + path: `RTC:${filepath}`, + factory: FACTORY + }); + } + } + }); + + // Add the command to the palette + if (commandPalette) { + commandPalette.addItem({ + category: 'Chat', + command: CommandIDs.createChat, + args: { isPalette: true } + }); + } + + // Add the create command to the launcher + if (launcher) { + launcher.add({ + command: CommandIDs.createChat, + category: 'Chat', + rank: 1 + }); + } + + // The command to mark the chat as read. + commands.addCommand(CommandIDs.markAsRead, { + caption: 'Mark chat as read', + icon: readIcon, + isEnabled: () => + tracker.currentWidget !== null && + tracker.currentWidget === app.shell.currentWidget && + tracker.currentWidget.model.unreadMessages.length > 0, + execute: async args => { + const widget = app.shell.currentWidget; + // Ensure widget is a CollaborativeChatPanel and is in main area + if ( + !widget || + !(widget instanceof CollaborativeChatPanel) || + !Array.from(app.shell.widgets('main')).includes(widget) + ) { + console.error( + `The command '${CommandIDs.markAsRead}' should be executed from the toolbar button only` + ); + return; + } + widget.model.unreadMessages = []; + } + }); + + // Update the 'markAsRead' command status when the current widget changes. + tracker.currentChanged.connect(() => { + commands.notifyCommandChanged(CommandIDs.markAsRead); + }); + + app.serviceManager.ready + .then(() => { + const user = app.serviceManager.user.identity; + /* + * Command to open a chat. + * + * args: + * filepath - the chat file to open. + */ + commands.addCommand(CommandIDs.openChat, { + label: 'Open a chat', + execute: async args => { + const inSidePanel: boolean = (args.inSidePanel as boolean) ?? false; + let filepath: string | null = (args.filepath as string) ?? null; + if (filepath === null) { + filepath = ( + await InputDialog.getText({ + label: 'File path', + placeholder: '/path/to/the/chat/file', + title: 'Path of the chat' + }) + ).value; + } + + if (!filepath) { + return; + } + + let fileExist = true; + await drive.get(filepath, { content: false }).catch(() => { + fileExist = false; + }); + + if (!fileExist) { + showErrorMessage( + 'Error opening chat', + `'${filepath}' is not a valid path` + ); + return; + } + + if (inSidePanel && chatPanel) { + // The chat is opened in the chat panel. + app.shell.activateById(chatPanel.id); + const model = await drive.get(filepath); + + /** + * Create a share model from the chat file + */ + const sharedModel = drive.sharedModelFactory.createNew({ + path: model.path, + format: model.format, + contentType: chatFileType.contentType, + collaborative: true + }) as YChat; + + /** + * Initialize the chat model with the share model + */ + const chat = new CollaborativeChatModel({ + user, + sharedModel, + widgetConfig, + commands: app.commands, + activeCellManager + }); + + /** + * Add a chat widget to the side panel. + */ + chatPanel.addChat( + chat, + PathExt.basename(model.name, chatFileType.extensions[0]) + ); + } else { + // The chat is opened in the main area + commands.execute('docmanager:open', { + path: `RTC:${filepath}`, + factory: FACTORY + }); + } + } + }); + + // Add the command to the palette + if (commandPalette) { + commandPalette.addItem({ + category: 'Chat', + command: CommandIDs.openChat + }); + } + }) + .catch(e => + console.error('The command to open a chat is not initialized\n', e) + ); + } +}; + +/* + * Extension providing a chat panel. + */ +const chatPanel: JupyterFrontEndPlugin = { + id: pluginIds.chatPanel, + description: 'A chat extension for Jupyter', + autoStart: true, + provides: IChatPanel, + requires: [ICollaborativeDrive, IRenderMimeRegistry], + optional: [ + IAutocompletionRegistry, + ILayoutRestorer, + INotebookTracker, + IThemeManager + ], + activate: ( + app: JupyterFrontEnd, + drive: ICollaborativeDrive, + rmRegistry: IRenderMimeRegistry, + autocompletionRegistry: IAutocompletionRegistry, + restorer: ILayoutRestorer | null, + notebookTracker: INotebookTracker, + themeManager: IThemeManager | null + ): ChatPanel => { + const { commands } = app; + + /** + * Add Chat widget to left sidebar + */ + const chatPanel = new ChatPanel({ + commands, + drive, + rmRegistry, + themeManager, + autocompletionRegistry + }); + chatPanel.id = 'JupyterCollaborationChat:sidepanel'; + chatPanel.title.icon = chatIcon; + chatPanel.title.caption = 'Jupyter Chat'; // TODO: i18n/ + + app.shell.add(chatPanel, 'left', { + rank: 2000 + }); + + if (restorer) { + restorer.add(chatPanel, 'jupyter-chat'); + } + + // Use events system to watch changes on files. + const schemaID = + 'https://events.jupyter.org/jupyter_server/contents_service/v1'; + const actions = ['create', 'delete', 'rename']; + app.serviceManager.events.stream.connect((_, emission) => { + if (emission.schema_id === schemaID) { + const action = emission.action as string; + if (actions.includes(action)) { + chatPanel.updateChatNames(); + } + } + }); + + /* + * Command to move a chat from the main area to the side panel. + * + */ + commands.addCommand(CommandIDs.moveToSide, { + label: 'Move the chat to the side panel', + caption: 'Move the chat to the side panel', + icon: launchIcon, + isEnabled: () => commands.hasCommand(CommandIDs.openChat), + execute: async () => { + const widget = app.shell.currentWidget; + // Ensure widget is a CollaborativeChatPanel and is in main area + if ( + !widget || + !(widget instanceof CollaborativeChatPanel) || + !Array.from(app.shell.widgets('main')).includes(widget) + ) { + console.error( + `The command '${CommandIDs.moveToSide}' should be executed from the toolbar button only` + ); + return; + } + // Remove potential drive prefix + const filepath = widget.context.path.split(':').pop(); + commands.execute(CommandIDs.openChat, { + filepath, + inSidePanel: true + }); + widget.dispose(); + } + }); + + return chatPanel; + } +}; + +/** + * Extension providing the active cell manager. + */ +const activeCellManager: JupyterFrontEndPlugin = { + id: pluginIds.activeCellManager, + description: 'the active cell manager plugin', + autoStart: true, + requires: [INotebookTracker], + provides: IActiveCellManagerToken, + activate: ( + app: JupyterFrontEnd, + notebookTracker: INotebookTracker + ): IActiveCellManager => { + return new ActiveCellManager({ + tracker: notebookTracker, + shell: app.shell + }); + } +}; + +export default [ + activeCellManager, + autocompletionPlugin, + chatCommands, + docFactories, + chatPanel +]; diff --git a/python/jupyterlab-collaborative-chat/style/base.css b/python/jupyterlab-collaborative-chat/style/base.css new file mode 100644 index 0000000..8ea586d --- /dev/null +++ b/python/jupyterlab-collaborative-chat/style/base.css @@ -0,0 +1,13 @@ +/* + * Copyright (c) Jupyter Development Team. + * Distributed under the terms of the Modified BSD License. + */ + +/* + See the JupyterLab Developer Guide for useful CSS Patterns: + + https://jupyterlab.readthedocs.io/en/stable/developer/css.html +*/ + +@import url('~jupyterlab-collaborative-chat/style/index.css'); + diff --git a/python/jupyterlab-collaborative-chat/style/index.css b/python/jupyterlab-collaborative-chat/style/index.css new file mode 100644 index 0000000..c236c5e --- /dev/null +++ b/python/jupyterlab-collaborative-chat/style/index.css @@ -0,0 +1,6 @@ +/* + * Copyright (c) Jupyter Development Team. + * Distributed under the terms of the Modified BSD License. + */ + +@import url('base.css'); diff --git a/python/jupyterlab-collaborative-chat/style/index.js b/python/jupyterlab-collaborative-chat/style/index.js new file mode 100644 index 0000000..71b038b --- /dev/null +++ b/python/jupyterlab-collaborative-chat/style/index.js @@ -0,0 +1,6 @@ +/* + * Copyright (c) Jupyter Development Team. + * Distributed under the terms of the Modified BSD License. + */ + +import './base.css'; diff --git a/python/jupyterlab-collaborative-chat/tsconfig.json b/python/jupyterlab-collaborative-chat/tsconfig.json new file mode 100644 index 0000000..92d6e92 --- /dev/null +++ b/python/jupyterlab-collaborative-chat/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/**/*"] +} diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/README.md b/python/jupyterlab-collaborative-chat/ui-tests/README.md similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/README.md rename to python/jupyterlab-collaborative-chat/ui-tests/README.md diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/jupyter_server_test_config.py b/python/jupyterlab-collaborative-chat/ui-tests/jupyter_server_test_config.py similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/jupyter_server_test_config.py rename to python/jupyterlab-collaborative-chat/ui-tests/jupyter_server_test_config.py diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/package.json b/python/jupyterlab-collaborative-chat/ui-tests/package.json similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/package.json rename to python/jupyterlab-collaborative-chat/ui-tests/package.json diff --git a/python/jupyterlab-collaborative-chat/ui-tests/playwright-report/data/0160206369d027b585a0a1764c806541db6b0012.webm b/python/jupyterlab-collaborative-chat/ui-tests/playwright-report/data/0160206369d027b585a0a1764c806541db6b0012.webm new file mode 100644 index 0000000..1cd0af6 Binary files /dev/null and b/python/jupyterlab-collaborative-chat/ui-tests/playwright-report/data/0160206369d027b585a0a1764c806541db6b0012.webm differ diff --git a/python/jupyterlab-collaborative-chat/ui-tests/playwright-report/index.html b/python/jupyterlab-collaborative-chat/ui-tests/playwright-report/index.html new file mode 100644 index 0000000..b909736 --- /dev/null +++ b/python/jupyterlab-collaborative-chat/ui-tests/playwright-report/index.html @@ -0,0 +1,68 @@ + + + + + + + + + Playwright Test Report + + + + +
+ + + \ No newline at end of file diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/playwright.config.js b/python/jupyterlab-collaborative-chat/ui-tests/playwright.config.js similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/playwright.config.js rename to python/jupyterlab-collaborative-chat/ui-tests/playwright.config.js diff --git a/python/jupyterlab-collaborative-chat/ui-tests/test-results/.last-run.json b/python/jupyterlab-collaborative-chat/ui-tests/test-results/.last-run.json new file mode 100644 index 0000000..25b4670 --- /dev/null +++ b/python/jupyterlab-collaborative-chat/ui-tests/test-results/.last-run.json @@ -0,0 +1,7 @@ +{ + "status": "failed", + "failedTests": [ + "86659aada0458a2dec90-ed575443018384c7f71c", + "e45d9e38adc663a001d3-2a5641d1d85d64c528b1" + ] +} \ No newline at end of file diff --git a/python/jupyterlab-collaborative-chat/ui-tests/test-results/tests-jupyterlab_collabora-4aa4a-nread-icon-for-new-messages/video.webm b/python/jupyterlab-collaborative-chat/ui-tests/test-results/tests-jupyterlab_collabora-4aa4a-nread-icon-for-new-messages/video.webm new file mode 100644 index 0000000..1cd0af6 Binary files /dev/null and b/python/jupyterlab-collaborative-chat/ui-tests/test-results/tests-jupyterlab_collabora-4aa4a-nread-icon-for-new-messages/video.webm differ diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/autocompletion.spec.ts b/python/jupyterlab-collaborative-chat/ui-tests/tests/autocompletion.spec.ts similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/autocompletion.spec.ts rename to python/jupyterlab-collaborative-chat/ui-tests/tests/autocompletion.spec.ts diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/code-toolbar.spec.ts b/python/jupyterlab-collaborative-chat/ui-tests/tests/code-toolbar.spec.ts similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/code-toolbar.spec.ts rename to python/jupyterlab-collaborative-chat/ui-tests/tests/code-toolbar.spec.ts diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/chat-icon-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/chat-icon-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/chat-icon-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/chat-icon-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/launcher-tile-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/launcher-tile-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/launcher-tile-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/launcher-tile-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/menu-new-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/menu-new-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/menu-new-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/menu-new-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToMain-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToMain-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToMain-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToMain-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToSide-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToSide-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToSide-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/moveToSide-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-unread-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-unread-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-unread-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-bottom-unread-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-top-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-top-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-top-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/navigation-top-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/not-stacked-messages-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/not-stacked-messages-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/not-stacked-messages-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/not-stacked-messages-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/stacked-messages-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/stacked-messages-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/stacked-messages-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/stacked-messages-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-with-unread-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-with-unread-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-with-unread-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-with-unread-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-without-unread-linux.png b/python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-without-unread-linux.png similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-without-unread-linux.png rename to python/jupyterlab-collaborative-chat/ui-tests/tests/jupyterlab_collaborative_chat.spec.ts-snapshots/tab-without-unread-linux.png diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/tests/test-utils.ts b/python/jupyterlab-collaborative-chat/ui-tests/tests/test-utils.ts similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/tests/test-utils.ts rename to python/jupyterlab-collaborative-chat/ui-tests/tests/test-utils.ts diff --git a/packages/jupyterlab-collaborative-chat/ui-tests/yarn.lock b/python/jupyterlab-collaborative-chat/ui-tests/yarn.lock similarity index 100% rename from packages/jupyterlab-collaborative-chat/ui-tests/yarn.lock rename to python/jupyterlab-collaborative-chat/ui-tests/yarn.lock diff --git a/scripts/install.sh b/scripts/install.sh index ffd0949..4ba1b62 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -11,12 +11,12 @@ jlpm install if [ -z "${EXTENSION}" ]; then jlpm build # install the collaborative chat extension - pip install -e packages/jupyterlab-collaborative-chat[test] + pip install -e python/jupyterlab-collaborative-chat[test] # install websocket chat extension pip install -e packages/jupyterlab-ws-chat[test] else PACKAGE="jupyterlab-${EXTENSION}-chat" jlpm build:${EXTENSION} - pip install -e packages/${PACKAGE}[test] + pip install -e python/${PACKAGE}[test] fi diff --git a/yarn.lock b/yarn.lock index 42c8db3..bfce94c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9824,11 +9824,10 @@ __metadata: languageName: unknown linkType: soft -"jupyterlab-collaborative-chat@workspace:packages/jupyterlab-collaborative-chat": +"jupyterlab-collaborative-chat-extension@workspace:python/jupyterlab-collaborative-chat": version: 0.0.0-use.local - resolution: "jupyterlab-collaborative-chat@workspace:packages/jupyterlab-collaborative-chat" + resolution: "jupyterlab-collaborative-chat-extension@workspace:python/jupyterlab-collaborative-chat" dependencies: - "@jupyter/chat": ^0.3.1 "@jupyter/collaboration": ^2.1.0 "@jupyter/docprovider": ^2.1.0 "@jupyter/ydoc": ^1.1.1 @@ -9860,6 +9859,59 @@ __metadata: eslint-config-prettier: ^8.8.0 eslint-plugin-prettier: ^5.0.0 jest: ^29.2.0 + jupyterlab-collaborative-chat: ^0.3.1 + mkdirp: ^1.0.3 + npm-run-all: ^4.1.5 + prettier: ^3.0.0 + react: ^18.2.0 + rimraf: ^5.0.1 + source-map-loader: ^1.0.2 + style-loader: ^3.3.1 + stylelint: ^15.10.1 + stylelint-config-recommended: ^13.0.0 + stylelint-config-standard: ^34.0.0 + stylelint-csstree-validator: ^3.0.0 + stylelint-prettier: ^4.0.0 + typescript: ~5.0.2 + y-protocols: ^1.0.5 + yjs: ^13.5.0 + languageName: unknown + linkType: soft + +"jupyterlab-collaborative-chat@^0.3.1, jupyterlab-collaborative-chat@workspace:packages/jupyterlab-collaborative-chat": + version: 0.0.0-use.local + resolution: "jupyterlab-collaborative-chat@workspace:packages/jupyterlab-collaborative-chat" + dependencies: + "@jupyter/chat": ^0.3.1 + "@jupyter/collaboration": ^2.1.0 + "@jupyter/docprovider": ^2.1.0 + "@jupyter/ydoc": ^1.1.1 + "@jupyterlab/application": ^4.2.0 + "@jupyterlab/apputils": ^4.3.0 + "@jupyterlab/coreutils": ^6.2.0 + "@jupyterlab/docregistry": ^4.2.0 + "@jupyterlab/launcher": ^4.2.0 + "@jupyterlab/notebook": ^4.2.0 + "@jupyterlab/rendermime": ^4.2.0 + "@jupyterlab/services": ^7.2.0 + "@jupyterlab/settingregistry": ^4.2.0 + "@jupyterlab/translation": ^4.2.0 + "@jupyterlab/ui-components": ^4.2.0 + "@lumino/commands": ^2.0.0 + "@lumino/coreutils": ^2.0.0 + "@lumino/signaling": ^2.0.0 + "@lumino/widgets": ^2.0.0 + "@types/jest": ^29.2.0 + "@types/json-schema": ^7.0.11 + "@types/react": ^18.2.0 + "@types/react-addons-linked-state-mixin": ^0.14.22 + "@typescript-eslint/eslint-plugin": ^6.1.0 + "@typescript-eslint/parser": ^6.1.0 + css-loader: ^6.7.1 + eslint: ^8.36.0 + eslint-config-prettier: ^8.8.0 + eslint-plugin-prettier: ^5.0.0 + jest: ^29.2.0 mkdirp: ^1.0.3 npm-run-all: ^4.1.5 prettier: ^3.0.0