diff --git a/docs/remove-notebooks-plan.md b/docs/remove-notebooks-plan.md index 9b6104509d..f2108425aa 100644 --- a/docs/remove-notebooks-plan.md +++ b/docs/remove-notebooks-plan.md @@ -111,7 +111,10 @@ run: `npm run compile`, `npm run compile:strict`, `npm run lint`, `npm run forma `npm test`, and a webpack build (`npm run build:ci`); manually verify the four shells still open. -### Phase 1 — Decouple database shells to CloudShell-only +### Phase 1 — Decouple database shells to CloudShell-only ✅ COMPLETED +> **Status:** Completed and merged in `d19c7e0c` ("Remove Phoenix & Notebooks — Phase 1: +> Decouple database shells to CloudShell (#2513)"). + Remove the legacy Phoenix notebook-server terminal path so shells no longer depend on notebook provisioning. - Rewire `TerminalTab` to always use `CloudShellTerminalComponentAdapter`; delete the @@ -124,29 +127,77 @@ notebook provisioning. CloudShell. If unused, remove it and `src/Terminal/`; otherwise keep. - **Outcome:** Shells run purely on CloudShell. Phoenix no longer needed for terminals. -### Phase 2 — Remove the in-app notebook authoring & rendering experience -Delete the notebook tabs, the nteract rendering engine, panes, and the read-only viewer, -and remove all UI entry points that open notebooks. -- Delete: `src/Explorer/Notebook/NotebookComponent/`, +### Phase 2 — Remove the in-app notebook authoring & rendering experience ✅ COMPLETED +> **Status:** Implemented on branch `users/jawelton/removenotebooks-phase2-061526`. Full +> verification sweep is green: `compile`, `compile:strict`, `lint` (0 errors), `format:check`, +> `test` (1945 passing), and `build:ci` (webpack) all pass. +> +> **Implementation notes / deviations:** +> - `src/Explorer/Panes/StringInputPane/` was also deleted — it became unused once +> `Explorer.renameNotebook` / `onCreateDirectory` were removed. +> - Removing notebook-only npm deps stripped several **phantom (transitively-hoisted)** +> packages that surviving code still imports, so these were re-added as explicit direct +> dependencies: `xterm@4.19.0` + `xterm-addon-fit@0.5.0` (CloudShell), `d3-collection@1.0.7` +> (Graph `D3ForceGraph`), and `@nteract/myths@0.1.9` (needed by kept `@nteract/core`). +> - Jest snapshots regenerated: `SettingsComponent`, `AddGlobalSecondaryIndexPanel`, +> `GitHubReposPanel` (all just dropped the removed `copyNotebook` function from the +> serialized Explorer); deleted-component snapshots removed with their dirs. +> - `CommandBarComponentButtonFactory` / `ContextMenuButtonFactory` required no notebook-button +> removal — they only contained shell entries (kept). + +Delete the notebook tabs, the nteract rendering engine, panes, the read-only viewer, and +Schema Analyzer (see decision below), and remove all UI entry points that open notebooks. + +> **Cross-phase coupling — revised approach (confirmed):** Several files the original plan +> deferred to later phases are hard-coupled to the deletions above and must be handled here: +> - **Schema Analyzer is pulled forward from Phase 3 into Phase 2.** `SchemaAnalyzer.tsx` +> renders through the nteract engine (`NotebookComponent/loadTransform`, +> `NotebookRenderer/outputs/SandboxOutputs`) and `SchemaAnalyzerTab extends NotebookTabBase`, +> so it cannot compile once the engine is deleted. +> - **`NotebookContentClient.ts` and `FileSystemUtil.ts` are deleted here** (they depend on +> the deleted `@nteract` content providers and only serve removed authoring paths). +> - **`NotebookManager.tsx` / `useNotebook.ts` get minimal edits** (full deletion stays in +> Phase 5): strip the content-provider / content-client / CopyNotebookPane / SchemaAnalyzer +> usages, keeping the GitHub / Juno / `NotebookContainerClient` wiring later phases need. +> - **`NotebookContentItem.ts` and `NotebookUtil.ts` are DEFERRED, not deleted here.** They +> are still used by the surviving `useNotebook` store and the "My Notebooks" tree +> (`ResourceTreeAdapter`), and `NotebookUtil.getName`/`isNotebookFile` are still used by the +> GitHub client/content provider. They are removed in Phase 4/5 with their consumers. + +- Delete (engine/content): `src/Explorer/Notebook/NotebookComponent/`, `src/Explorer/Notebook/NotebookRenderer/`, `src/Explorer/Notebook/SecurityWarningBar/`, `NotebookClientV2.ts`, `notebookClientV2.test.ts`, `NotebookContentClient.ts`, - `NTeractUtil.ts`, `NotebookContentItem.ts`, `NotebookUtil.ts` (+ test), - `FileSystemUtil.ts`. -- Delete tabs/panes/viewer: `src/Explorer/Tabs/NotebookV2Tab.ts`, `NotebookTabBase.ts`, + `NTeractUtil.ts`, `FileSystemUtil.ts`. +- Delete (tabs/panes/viewer): `src/Explorer/Tabs/NotebookV2Tab.ts`, `NotebookTabBase.ts`, `src/Explorer/Panes/CopyNotebookPane/`, `src/Explorer/Controls/NotebookViewer/`, - `src/CellOutputViewer/` (+ `cellOutputViewer` webpack entry & HTML plugin). + `src/CellOutputViewer/` (+ `cellOutputViewer` webpack entry & HTML/inline plugins). +- Delete (Schema Analyzer, pulled forward): `src/Explorer/Notebook/SchemaAnalyzer/` and + `src/Explorer/Tabs/SchemaAnalyzerTab.ts`; remove its command-bar button and any tree/menu + entry points. +- Edit to decouple survivors: `NotebookManager.tsx` (see note above), `Explorer.tsx` + (remove `openNotebook*`, import/copy/rename/createDir/read/download/upload/delete/refresh + notebook methods + `notebookToImport`; KEEP `openNotebookTerminal` / + `connectToNotebookTerminal` and the Phoenix container methods for Phase 5), + `ResourceTreeAdapter.tsx` (drop `NotebookV2Tab` import + open/copy actions; neutralize node + click handlers — full "My Notebooks" tree removal remains Phase 5), `useTabs.ts` (drop + `NotebookTabV2`), `Contracts/ViewModels.ts` (fix notebook-tab interfaces that break compile; + keep `CollectionTabKind` enum values). - Remove notebook entry points: "New Notebook"/open-notebook buttons in - `CommandBarComponentButtonFactory` (+ test), `OpenActions.tsx`, + `CommandBarComponentButtonFactory` (+ test), `OpenActions.tsx` (`OpenSampleNotebook`), `ContextMenuButtonFactory.tsx`, splash-screen notebook cards & recent-notebook items - (`MostRecentActivity` OpenNotebook type), and the `openNotebook*` / - `createNotebookContentItemFile` methods on `Explorer`. -- Remove notebook deps from `package.json`: `@nteract/*`, `@jupyterlab/*`, - `@phosphor/widgets`, `rx-jupyter` (and any now-unused transitive notebook-only libs). -- **Outcome:** Notebooks can no longer be authored, opened, or rendered. + (`MostRecentActivity` `OpenNotebook` type). +- `tsconfig.strict.json`: remove entries for deleted files (keep `NotebookContentItem.ts`). +- Remove from `package.json` ONLY the notebook-only deps with **zero** remaining importers + after the deletions (e.g. `@jupyterlab/*`, `@phosphor/widgets`, and any `@nteract/*` not + still used). DEFER `@nteract/core`, `@nteract/commutable`, and `rx-jupyter` (still imported + by the kept `NotebookManager` / GitHub code) to Phase 4/5. +- Update/delete affected tests; regenerate Jest snapshots (`treeNodeUtil`, + `SettingsComponent`, `StringInputPane`). +- **Outcome:** Notebooks and Schema Analyzer can no longer be authored, opened, or rendered. -### Phase 3 — Remove Schema Analyzer -- Delete `src/Explorer/Notebook/SchemaAnalyzer/` and `src/Explorer/Tabs/SchemaAnalyzerTab.ts`. -- Remove Schema Analyzer command-bar button and any tree/menu entry points. +### Phase 3 — (folded into Phase 2) +Schema Analyzer removal was pulled forward into Phase 2 because it is rendered by the nteract +engine deleted there. No separate work remains for this phase. ### Phase 4 — Remove GitHub integration - Delete `src/GitHub/`, `src/Explorer/Controls/GitHub/`, @@ -160,7 +211,13 @@ and remove all UI entry points that open notebooks. ### Phase 5 — Remove Phoenix and the notebook container/allocation core - Delete `src/Phoenix/`, `src/Explorer/Notebook/NotebookContainerClient.ts`, `src/Explorer/Notebook/NotebookManager.tsx`, `src/Explorer/Notebook/useNotebook.ts`, - `src/Utils/NotebookConfigurationUtils.ts`, `src/hooks/useNotebookSnapshotStore.ts`. + `src/Explorer/Notebook/NotebookContentItem.ts`, `src/Explorer/Notebook/NotebookUtil.ts` + (+ `NotebookUtil.test.ts`), `src/Utils/NotebookConfigurationUtils.ts`, + `src/hooks/useNotebookSnapshotStore.ts`. (`NotebookContentItem.ts` and `NotebookUtil.ts` + deletion was deferred from Phase 2 because the "My Notebooks" tree/store and the GitHub + client still referenced them; remove them here once those consumers are gone. If GitHub + was removed in Phase 4, `NotebookUtil`'s `getName`/`isNotebookFile` helpers were inlined + there.) - Remove from `Explorer.tsx`: `phoenixClient`, `notebookManager`, `_isInitializingNotebooks`, `initNotebooks`, `initiateAndRefreshNotebookList`, `refreshNotebookList`, `allocateContainer`, container heartbeat/connection logic, and notebook-server URL diff --git a/jest.config.js b/jest.config.js index d265f685d6..8f6c5b0c99 100644 --- a/jest.config.js +++ b/jest.config.js @@ -70,7 +70,6 @@ module.exports = { moduleNameMapper: { "^.*[.](png|gif|less|css)$": "/mockModule", "(.*)$[.](svg)": "/mockModule/$1", - "@nteract/stateful-components/(.*)$": "/mockModule", "@fluentui/react/lib/(.*)$": "@fluentui/react/lib-commonjs/$1", // https://github.com/microsoft/fluentui/wiki/Version-8-release-notes "monaco-editor/(.*)$": "/__mocks__/monaco-editor", "^dnd-core$": "dnd-core/dist/cjs", diff --git a/package-lock.json b/package-lock.json index c172ed01cd..282df390cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,35 +18,14 @@ "@babel/plugin-proposal-decorators": "7.12.12", "@fluentui/react": "8.119.0", "@fluentui/react-components": "9.54.2", - "@jupyterlab/services": "6.0.2", - "@jupyterlab/terminal": "3.6.8", "@microsoft/applicationinsights-web": "2.6.1", "@nteract/commutable": "7.5.1", - "@nteract/connected-components": "6.8.2", "@nteract/core": "15.1.9", - "@nteract/data-explorer": "8.2.12", - "@nteract/directory-listing": "2.0.6", - "@nteract/dropdown-menu": "1.0.1", - "@nteract/editor": "10.1.12", "@nteract/fixtures": "2.3.0", - "@nteract/iron-icons": "1.0.0", - "@nteract/jupyter-widgets": "2.0.0", - "@nteract/logos": "1.0.0", "@nteract/markdown": "4.6.0", - "@nteract/monaco-editor": "3.2.2", - "@nteract/octicons": "2.0.0", - "@nteract/outputs": "3.0.9", - "@nteract/presentational-components": "3.4.12", - "@nteract/stateful-components": "1.7.0", - "@nteract/styles": "2.0.2", - "@nteract/transform-geojson": "5.1.8", - "@nteract/transform-model-debug": "5.0.1", - "@nteract/transform-plotly": "6.1.6", - "@nteract/transform-vdom": "4.0.11", - "@nteract/transform-vega": "7.0.6", + "@nteract/myths": "0.1.9", "@octokit/request": "8.4.1", "@octokit/rest": "17.9.2", - "@phosphor/widgets": "1.9.3", "@testing-library/jest-dom": "6.4.6", "@types/lodash": "4.14.171", "@types/mkdirp": "1.0.1", @@ -64,6 +43,7 @@ "crossroads": "0.12.2", "css-element-queries": "1.1.1", "d3": "7.9.0", + "d3-collection": "1.0.7", "datatables.net-colreorder-dt": "1.7.0", "datatables.net-dt": "1.13.8", "date-fns": "1.29.0", @@ -122,6 +102,9 @@ "utility-types": "3.10.0", "uuid": "9.0.0", "web-vitals": "4.2.4", + "ws": "8.20.1", + "xterm": "4.19.0", + "xterm-addon-fit": "0.5.0", "zustand": "3.5.0" }, "devDependencies": { @@ -4450,13 +4433,6 @@ "react": ">=0.14.0" } }, - "node_modules/@icons/material": { - "version": "0.2.4", - "license": "MIT", - "peerDependencies": { - "react": "*" - } - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -6280,471 +6256,12 @@ "version": "3.4.0", "license": "Apache-2.0" }, - "node_modules/@jupyterlab/apputils": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/apputils/-/apputils-3.6.8.tgz", - "integrity": "sha512-8BxyDbpBFOD3vjlUEZc39xNPtnKMQIFzUHH2S1oLECT0jcHNK08wuymPsYBcbvGO2s3YI57z0iz+JIXUmMhdSg==", - "dependencies": { - "@jupyterlab/coreutils": "^5.6.8", - "@jupyterlab/observables": "^4.6.8", - "@jupyterlab/services": "^6.6.8", - "@jupyterlab/settingregistry": "^3.6.8", - "@jupyterlab/statedb": "^3.6.8", - "@jupyterlab/translation": "^3.6.8", - "@jupyterlab/ui-components": "^3.6.8", - "@lumino/algorithm": "^1.9.0", - "@lumino/commands": "^1.19.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/domutils": "^1.8.0", - "@lumino/messaging": "^1.10.0", - "@lumino/polling": "^1.9.0", - "@lumino/properties": "^1.8.0", - "@lumino/signaling": "^1.10.0", - "@lumino/virtualdom": "^1.14.0", - "@lumino/widgets": "^1.37.2", - "@types/react": "^17.0.0", - "react": "^17.0.1", - "react-dom": "^17.0.1", - "sanitize-html": "~2.7.3", - "url": "^0.11.0" - } - }, - "node_modules/@jupyterlab/apputils/node_modules/@jupyterlab/services": { - "version": "6.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-6.6.8.tgz", - "integrity": "sha512-zuW6UvWeYcDK0QIIHyr2Cs7EXlXgfPhlMiuaIUD4CGMRQmWW9ATGeeyozOvNCaswIN/2ErDFITgF1SwH+dsmOw==", - "dependencies": { - "@jupyterlab/coreutils": "^5.6.8", - "@jupyterlab/nbformat": "^3.6.8", - "@jupyterlab/observables": "^4.6.8", - "@jupyterlab/settingregistry": "^3.6.8", - "@jupyterlab/statedb": "^3.6.8", - "@lumino/algorithm": "^1.9.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/polling": "^1.9.0", - "@lumino/signaling": "^1.10.0", - "node-fetch": "^2.6.0", - "ws": "^7.4.6" - } - }, - "node_modules/@jupyterlab/apputils/node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jupyterlab/apputils/node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/@jupyterlab/apputils/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@jupyterlab/coreutils": { - "version": "5.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-5.6.8.tgz", - "integrity": "sha512-lWrDeIJRkdmlBZ2Yw+wDLxQVjO8MWojOxsu+KcWQF0iuETGSoa4i+HNJKb9yv+JJSIbXHyBBrM0EAY7Z6XRb6w==", - "dependencies": { - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/signaling": "^1.10.0", - "minimist": "~1.2.0", - "moment": "^2.24.0", - "path-browserify": "^1.0.0", - "url-parse": "~1.5.1" - } - }, - "node_modules/@jupyterlab/nbformat": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-3.6.8.tgz", - "integrity": "sha512-HprEmm1jUJAMFlhFWZOWv8XITXUBK3hRn7kzRft5gdFZ2T7ClpDLMLunobOlJAQOP01H8GBm5Je27KIoukWgfg==", - "dependencies": { - "@lumino/coreutils": "^1.11.0" - } - }, - "node_modules/@jupyterlab/observables": { - "version": "4.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-4.6.8.tgz", - "integrity": "sha512-Gcqw1iQd315mrs5sD02n6NcYVeH+TXD9G2K+MbEWmQ4WWqwhfMGC17BnuTZ8bjHGcKQ7wNjZJ8zpvLolCaJBvA==", - "dependencies": { - "@lumino/algorithm": "^1.9.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/messaging": "^1.10.0", - "@lumino/signaling": "^1.10.0" - } - }, - "node_modules/@jupyterlab/services": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-6.0.2.tgz", - "integrity": "sha512-J0wghVeqMlG71VXkOhlh3ZcyG787wzz9hSFHMGu/1ATlIDtn9z8dbdCAn92rd12jD1nDX1L2KcyjH5pLT+CKeA==", - "dependencies": { - "@jupyterlab/coreutils": "^5.0.1", - "@jupyterlab/nbformat": "^3.0.1", - "@jupyterlab/observables": "^4.0.1", - "@jupyterlab/settingregistry": "^3.0.1", - "@jupyterlab/statedb": "^3.0.1", - "@lumino/algorithm": "^1.3.3", - "@lumino/coreutils": "^1.5.3", - "@lumino/disposable": "^1.4.3", - "@lumino/polling": "^1.3.3", - "@lumino/signaling": "^1.4.3", - "node-fetch": "^2.6.0", - "ws": "^7.2.0" - } - }, - "node_modules/@jupyterlab/services/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@jupyterlab/settingregistry": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/settingregistry/-/settingregistry-3.6.8.tgz", - "integrity": "sha512-dwcsLWP8qy9N0cuGZJg3pW0oVRHXhD7blHMSgyszwlCpCD26H0C4DC3Udz0c0FMoiQ5lK/cv0u+TFSqyoMNtzA==", - "dependencies": { - "@jupyterlab/statedb": "^3.6.8", - "@lumino/commands": "^1.19.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/signaling": "^1.10.0", - "ajv": "^6.12.3", - "json5": "^2.1.1" - } - }, - "node_modules/@jupyterlab/statedb": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/statedb/-/statedb-3.6.8.tgz", - "integrity": "sha512-5zeyUhKpgbKFZdx8NA9f9kzP8kin4w33CyJdm/MyFySezn5zBwFbF3zeYAtwRGy5QQYFXgc79jgTCWkjsamYSg==", - "dependencies": { - "@lumino/commands": "^1.19.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/properties": "^1.8.0", - "@lumino/signaling": "^1.10.0" - } - }, - "node_modules/@jupyterlab/terminal": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/terminal/-/terminal-3.6.8.tgz", - "integrity": "sha512-eIHBS7qwm5WFW0zGCaYEaGQJu4hWlkF5PskQzhurYtWQF4SybvIZ8jo3+nPV3cc2zLanx+Wqc4bpnOTtlpRXPg==", - "dependencies": { - "@jupyterlab/apputils": "^3.6.8", - "@jupyterlab/services": "^6.6.8", - "@jupyterlab/translation": "^3.6.8", - "@lumino/coreutils": "^1.11.0", - "@lumino/domutils": "^1.8.0", - "@lumino/messaging": "^1.10.0", - "@lumino/widgets": "^1.37.2", - "xterm": "~4.19.0", - "xterm-addon-fit": "~0.5.0", - "xterm-addon-web-links": "~0.6.0" - } - }, - "node_modules/@jupyterlab/terminal/node_modules/@jupyterlab/services": { - "version": "6.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-6.6.8.tgz", - "integrity": "sha512-zuW6UvWeYcDK0QIIHyr2Cs7EXlXgfPhlMiuaIUD4CGMRQmWW9ATGeeyozOvNCaswIN/2ErDFITgF1SwH+dsmOw==", - "dependencies": { - "@jupyterlab/coreutils": "^5.6.8", - "@jupyterlab/nbformat": "^3.6.8", - "@jupyterlab/observables": "^4.6.8", - "@jupyterlab/settingregistry": "^3.6.8", - "@jupyterlab/statedb": "^3.6.8", - "@lumino/algorithm": "^1.9.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/polling": "^1.9.0", - "@lumino/signaling": "^1.10.0", - "node-fetch": "^2.6.0", - "ws": "^7.4.6" - } - }, - "node_modules/@jupyterlab/terminal/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@jupyterlab/translation": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/translation/-/translation-3.6.8.tgz", - "integrity": "sha512-tbnpu2jxCINJIK1GmzdfJCbDi6NQcSQA1sd2Or0dfmt1yJ6hIXfgyA+NHyAdSX8kc8ra8tNga+ZZfF3IaoC7wg==", - "dependencies": { - "@jupyterlab/coreutils": "^5.6.8", - "@jupyterlab/services": "^6.6.8", - "@jupyterlab/statedb": "^3.6.8", - "@lumino/coreutils": "^1.11.0" - } - }, - "node_modules/@jupyterlab/translation/node_modules/@jupyterlab/services": { - "version": "6.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-6.6.8.tgz", - "integrity": "sha512-zuW6UvWeYcDK0QIIHyr2Cs7EXlXgfPhlMiuaIUD4CGMRQmWW9ATGeeyozOvNCaswIN/2ErDFITgF1SwH+dsmOw==", - "dependencies": { - "@jupyterlab/coreutils": "^5.6.8", - "@jupyterlab/nbformat": "^3.6.8", - "@jupyterlab/observables": "^4.6.8", - "@jupyterlab/settingregistry": "^3.6.8", - "@jupyterlab/statedb": "^3.6.8", - "@lumino/algorithm": "^1.9.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/polling": "^1.9.0", - "@lumino/signaling": "^1.10.0", - "node-fetch": "^2.6.0", - "ws": "^7.4.6" - } - }, - "node_modules/@jupyterlab/translation/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@jupyterlab/ui-components": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@jupyterlab/ui-components/-/ui-components-3.6.8.tgz", - "integrity": "sha512-qUComc04A08EJRtYGPnoNaQ3xJZEu6Jxupb+3G80V8eYVCrFm4wouUqzPoWGP/59spy/TKT5fTlezFhj7rDrGg==", - "dependencies": { - "@blueprintjs/core": "^3.36.0", - "@blueprintjs/select": "^3.15.0", - "@jupyterlab/coreutils": "^5.6.8", - "@jupyterlab/translation": "^3.6.8", - "@lumino/algorithm": "^1.9.0", - "@lumino/commands": "^1.19.0", - "@lumino/coreutils": "^1.11.0", - "@lumino/disposable": "^1.10.0", - "@lumino/signaling": "^1.10.0", - "@lumino/virtualdom": "^1.14.0", - "@lumino/widgets": "^1.37.2", - "@rjsf/core": "^3.1.0", - "react": "^17.0.1", - "react-dom": "^17.0.1", - "typestyle": "^2.0.4" - }, - "peerDependencies": { - "react": "^17.0.1" - } - }, - "node_modules/@jupyterlab/ui-components/node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@jupyterlab/ui-components/node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, - "node_modules/@lumino/algorithm": { - "version": "1.9.2", - "license": "BSD-3-Clause" - }, - "node_modules/@lumino/collections": { - "version": "1.9.3", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2" - } - }, - "node_modules/@lumino/commands": { - "version": "1.21.1", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4", - "@lumino/domutils": "^1.8.2", - "@lumino/keyboard": "^1.8.2", - "@lumino/signaling": "^1.11.1", - "@lumino/virtualdom": "^1.14.3" - } - }, - "node_modules/@lumino/coreutils": { - "version": "1.12.1", - "license": "BSD-3-Clause", - "peerDependencies": { - "crypto": "1.0.1" - } - }, - "node_modules/@lumino/disposable": { - "version": "1.10.4", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/signaling": "^1.11.1" - } - }, - "node_modules/@lumino/domutils": { - "version": "1.8.2", - "license": "BSD-3-Clause" - }, - "node_modules/@lumino/dragdrop": { - "version": "1.14.5", - "resolved": "https://registry.npmjs.org/@lumino/dragdrop/-/dragdrop-1.14.5.tgz", - "integrity": "sha512-LC5xB82+xGF8hFyl716TMpV32OIMIMl+s3RU1PaqDkD6B7PkgiVk6NkJ4X9/GcEvl2igkvlGQt/3L7qxDAJNxw==", - "dependencies": { - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4" - } - }, - "node_modules/@lumino/keyboard": { - "version": "1.8.2", - "license": "BSD-3-Clause" - }, - "node_modules/@lumino/messaging": { - "version": "1.10.3", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/collections": "^1.9.3" - } - }, - "node_modules/@lumino/polling": { - "version": "1.11.4", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4", - "@lumino/signaling": "^1.11.1" - } - }, - "node_modules/@lumino/properties": { - "version": "1.8.2", - "license": "BSD-3-Clause" - }, - "node_modules/@lumino/signaling": { - "version": "1.11.1", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/properties": "^1.8.2" - } - }, - "node_modules/@lumino/virtualdom": { - "version": "1.14.3", - "license": "BSD-3-Clause", - "dependencies": { - "@lumino/algorithm": "^1.9.2" - } - }, - "node_modules/@lumino/widgets": { - "version": "1.37.2", - "resolved": "https://registry.npmjs.org/@lumino/widgets/-/widgets-1.37.2.tgz", - "integrity": "sha512-NHKu1NBDo6ETBDoNrqSkornfUCwc8EFFzw6+LWBfYVxn2PIwciq2SdiJGEyNqL+0h/A9eVKb5ui5z4cwpRekmQ==", - "dependencies": { - "@lumino/algorithm": "^1.9.2", - "@lumino/commands": "^1.21.1", - "@lumino/coreutils": "^1.12.1", - "@lumino/disposable": "^1.10.4", - "@lumino/domutils": "^1.8.2", - "@lumino/dragdrop": "^1.14.5", - "@lumino/keyboard": "^1.8.2", - "@lumino/messaging": "^1.10.3", - "@lumino/properties": "^1.8.2", - "@lumino/signaling": "^1.11.1", - "@lumino/virtualdom": "^1.14.3" - } - }, "node_modules/@microsoft/applicationinsights-analytics-js": { "version": "2.6.1", "license": "MIT", @@ -6894,10 +6411,6 @@ "url-join": "^4.0.0" } }, - "node_modules/@nteract/any-vega": { - "version": "1.0.1", - "license": "MIT" - }, "node_modules/@nteract/commutable": { "version": "7.5.1", "license": "BSD-3-Clause", @@ -6914,43 +6427,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/@nteract/connected-components": { - "version": "6.8.2", - "license": "BSD-3-Clause", - "dependencies": { - "@blueprintjs/core": "^3.7.0", - "@nteract/commutable": "^7.3.2", - "@nteract/core": "^15.1.0", - "@nteract/types": "^7.1.0", - "react-redux": "^6.0.0", - "redux": "^4.0.0" - }, - "peerDependencies": { - "immutable": "^4.0.0-rc.12", - "react": "^16.3.2", - "styled-components": ">= 5.0.1" - } - }, - "node_modules/@nteract/connected-components/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, - "node_modules/@nteract/connected-components/node_modules/react-redux": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^16.8.2" - }, - "peerDependencies": { - "react": "^16.4.0-0", - "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" - } - }, "node_modules/@nteract/core": { "version": "15.1.9", "resolved": "https://registry.npmjs.org/@nteract/core/-/core-15.1.9.tgz", @@ -6968,390 +6444,6 @@ "immutable": "^4.0.0-rc.12" } }, - "node_modules/@nteract/data-explorer": { - "version": "8.2.12", - "resolved": "https://registry.npmjs.org/@nteract/data-explorer/-/data-explorer-8.2.12.tgz", - "integrity": "sha512-p88YyzJ6eOgrslJ8X9ILURlSDeyRz8j3VvLbbSGrsDKMmz7mpfLDqAZBuUHRzQZTZF9QalfTnKW+ajA0DX79BA==", - "dependencies": { - "@nteract/octicons": "^2.0.0", - "d3-collection": "^1.0.7", - "d3-scale": "^3.0.0", - "d3-shape": "^1.2.2", - "numeral": "^2.0.6", - "react-color": "^2.14.1", - "react-table": "6.11.5", - "react-table-hoc-fixed-columns": "2.1.3", - "semiotic": "^2.0.0-rc.8" - }, - "peerDependencies": { - "numeral": "^2.0.6", - "react": "^16.13.1", - "styled-components": ">= 4" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-array": { - "version": "1.2.4", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-chord": { - "version": "1.0.6", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "1", - "d3-path": "1" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-contour": { - "version": "1.3.2", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "^1.1.1" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-dispatch": { - "version": "1.0.6", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-force": { - "version": "1.2.1", - "license": "BSD-3-Clause", - "dependencies": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-format": { - "version": "1.4.5", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-hierarchy": { - "version": "1.1.9", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-interpolate": { - "version": "1.4.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-color": "1" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-path": { - "version": "1.0.9", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-polygon": { - "version": "1.0.6", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-quadtree": { - "version": "1.0.7", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-scale": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", - "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", - "dependencies": { - "d3-array": "^2.3.0", - "d3-format": "1 - 2", - "d3-interpolate": "1.2.0 - 2", - "d3-time": "^2.1.1", - "d3-time-format": "2 - 3" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-scale/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-scale/node_modules/d3-time": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", - "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", - "dependencies": { - "d3-array": "2" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-time": { - "version": "1.1.0", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/d3-time-format": { - "version": "2.3.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-time": "1" - } - }, - "node_modules/@nteract/data-explorer/node_modules/d3-timer": { - "version": "1.0.10", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/data-explorer/node_modules/dequal": { - "version": "2.0.3", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/@nteract/data-explorer/node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/@nteract/data-explorer/node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/@nteract/data-explorer/node_modules/semiotic": { - "version": "2.0.1", - "license": "Apache-2.0", - "dependencies": { - "d3-array": "^1.2.0", - "d3-brush": "^2.1.0", - "d3-chord": "^1.0.4", - "d3-collection": "^1.0.1", - "d3-contour": "^1.1.1", - "d3-force": "^1.0.2", - "d3-glyphedge": "^1.2.0", - "d3-hexbin": "^0.2.2", - "d3-hierarchy": "^1.1.3", - "d3-interpolate": "^1.1.5", - "d3-path-arrows": "^0.4.0", - "d3-polygon": "^1.0.5", - "d3-sankey-circular": "0.25.0", - "d3-scale": "^1.0.3", - "d3-selection": "2", - "d3-shape": "^1.3.5", - "d3-voronoi": "^1.0.2", - "dequal": "^2.0.3", - "labella": "1.1.4", - "memoize-one": "^5.1.1", - "object-assign": "4.1.1", - "polygon-offset": "0.3.1", - "react-annotation": "3.0.0-beta.4", - "regression": "^2.0.1", - "svg-path-bounding-box": "1.0.4" - }, - "peerDependencies": { - "react": "^18.1.0", - "react-dom": "^18.1.0" - } - }, - "node_modules/@nteract/data-explorer/node_modules/semiotic/node_modules/d3-scale": { - "version": "1.0.7", - "license": "BSD-3-Clause", - "dependencies": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-color": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" - } - }, - "node_modules/@nteract/directory-listing": { - "version": "2.0.6", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/octicons": "^2.0.0", - "react-hot-loader": "^4.1.2", - "react-timeago": "^4.1.9", - "styled-components": "^4.1.3" - }, - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/directory-listing/node_modules/css-to-react-native": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^3.3.0" - } - }, - "node_modules/@nteract/directory-listing/node_modules/postcss-value-parser": { - "version": "3.3.1", - "license": "MIT" - }, - "node_modules/@nteract/directory-listing/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, - "node_modules/@nteract/directory-listing/node_modules/styled-components": { - "version": "4.4.1", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@emotion/is-prop-valid": "^0.8.1", - "@emotion/unitless": "^0.7.0", - "babel-plugin-styled-components": ">= 1", - "css-to-react-native": "^2.2.2", - "memoize-one": "^5.0.0", - "merge-anything": "^2.2.4", - "prop-types": "^15.5.4", - "react-is": "^16.6.0", - "stylis": "^3.5.0", - "stylis-rule-sheet": "^0.0.10", - "supports-color": "^5.5.0" - }, - "peerDependencies": { - "react": ">= 16.3.0", - "react-dom": ">= 16.3.0" - } - }, - "node_modules/@nteract/directory-listing/node_modules/stylis": { - "version": "3.5.4", - "license": "MIT" - }, - "node_modules/@nteract/directory-listing/node_modules/stylis-rule-sheet": { - "version": "0.0.10", - "license": "MIT", - "peerDependencies": { - "stylis": "^3.5.0" - } - }, - "node_modules/@nteract/dropdown-menu": { - "version": "1.0.1", - "license": "BSD-3-Clause", - "dependencies": { - "react-hot-loader": "^4.1.2", - "styled-components": "^4.1.3" - }, - "peerDependencies": { - "react": "^16.3.2", - "react-dom": "^16.3.2" - } - }, - "node_modules/@nteract/dropdown-menu/node_modules/css-to-react-native": { - "version": "2.3.2", - "license": "MIT", - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^3.3.0" - } - }, - "node_modules/@nteract/dropdown-menu/node_modules/postcss-value-parser": { - "version": "3.3.1", - "license": "MIT" - }, - "node_modules/@nteract/dropdown-menu/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, - "node_modules/@nteract/dropdown-menu/node_modules/styled-components": { - "version": "4.4.1", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@emotion/is-prop-valid": "^0.8.1", - "@emotion/unitless": "^0.7.0", - "babel-plugin-styled-components": ">= 1", - "css-to-react-native": "^2.2.2", - "memoize-one": "^5.0.0", - "merge-anything": "^2.2.4", - "prop-types": "^15.5.4", - "react-is": "^16.6.0", - "stylis": "^3.5.0", - "stylis-rule-sheet": "^0.0.10", - "supports-color": "^5.5.0" - }, - "peerDependencies": { - "react": ">= 16.3.0", - "react-dom": ">= 16.3.0" - } - }, - "node_modules/@nteract/dropdown-menu/node_modules/stylis": { - "version": "3.5.4", - "license": "MIT" - }, - "node_modules/@nteract/dropdown-menu/node_modules/stylis-rule-sheet": { - "version": "0.0.10", - "license": "MIT", - "peerDependencies": { - "stylis": "^3.5.0" - } - }, - "node_modules/@nteract/editor": { - "version": "10.1.12", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/messaging": "^7.0.19", - "@nteract/outputs": "^3.0.11", - "codemirror": "5.61.1", - "rxjs": "^6.3.3" - }, - "peerDependencies": { - "immutable": "^4.0.0-rc.12", - "react": "^16.3.2", - "react-dom": "^16.3.2", - "styled-components": ">= 5.0.1" - } - }, - "node_modules/@nteract/editor/node_modules/@nteract/outputs": { - "version": "3.0.11", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/markdown": "^4.5.2", - "@nteract/mathjax": "^4.0.11", - "ansi-to-react": "^6.0.5", - "react-json-tree": "^0.12.1" - }, - "peerDependencies": { - "react": "^16.3.2", - "styled-components": ">= 4" - } - }, - "node_modules/@nteract/editor/node_modules/react-base16-styling": { - "version": "0.7.0", - "license": "MIT", - "dependencies": { - "base16": "^1.0.0", - "lodash.curry": "^4.1.1", - "lodash.flow": "^3.5.0", - "pure-color": "^1.3.0" - } - }, - "node_modules/@nteract/editor/node_modules/react-json-tree": { - "version": "0.12.1", - "license": "MIT", - "dependencies": { - "prop-types": "^15.7.2", - "react-base16-styling": "^0.7.0" - }, - "peerDependencies": { - "react": "^16.3.0", - "react-dom": "^16.3.0" - } - }, "node_modules/@nteract/epics": { "version": "5.1.0", "license": "BSD-3-Clause", @@ -7443,28 +6535,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/@nteract/iron-icons": { - "version": "1.0.0", - "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/jupyter-widgets": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/logos": { - "version": "1.0.0", - "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^16.3.2", - "react-dom": "^16.3.2" - } - }, "node_modules/@nteract/markdown": { "version": "4.6.0", "license": "BSD-3-Clause", @@ -7497,133 +6567,10 @@ "@types/uuid": "^8.0.0", "lodash.clonedeep": "^4.5.0", "rxjs": "^6.6.0", - "uuid": "^8.0.0" - } - }, - "node_modules/@nteract/messaging/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@nteract/monaco-editor": { - "version": "3.2.2", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/core": "^14.0.0", - "@nteract/messaging": "^7.0.12", - "lodash.debounce": "^4.0.6", - "monaco-editor": "0.18.1", - "rxjs": "^6.3.3" - }, - "peerDependencies": { - "immutable": "^4.0.0-rc.12", - "react": "^16.3.2", - "react-dom": "^16.3.2", - "styled-components": ">= 4" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/actions": { - "version": "6.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/commutable": "^7.2.12", - "@nteract/messaging": "^7.0.7", - "@nteract/types": "^6.0.7", - "immutable": "^4.0.0-rc.12", - "rx-jupyter": "^5.5.9" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/core": { - "version": "14.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/actions": "^6.0.0", - "@nteract/commutable": "^7.2.12", - "@nteract/epics": "^4.0.9", - "@nteract/reducers": "^4.0.0", - "@nteract/selectors": "^2.8.10", - "@nteract/types": "^6.0.7", - "redux-logger": "^3.0.6" - }, - "peerDependencies": { - "immutable": "^4.0.0-rc.12" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/epics": { - "version": "4.0.9", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/actions": "^6.0.0", - "@nteract/commutable": "^7.2.12", - "@nteract/messaging": "^7.0.7", - "@nteract/mythic-notifications": "^0.1.9", - "@nteract/selectors": "^2.8.10", - "@nteract/types": "^6.0.7", - "file-saver": "^2.0.0", - "redux": "^4.0.1", - "redux-observable": "^2.0.0-alpha.0", - "rx-jupyter": "^5.5.9", - "rxjs": "^6.3.3" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/mythic-notifications": { - "version": "0.1.9", - "license": "BSD-3-Clause", - "dependencies": { - "@blueprintjs/core": "^3.7.0", - "@nteract/myths": "^0.1.9" - }, - "peerDependencies": { - "immutable": "^4.0.0-rc.12", - "react": "^16.3.2", - "react-dom": "^16.3.2", - "styled-components": ">= 4" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/reducers": { - "version": "4.0.0", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/actions": "^6.0.0", - "@nteract/commutable": "^7.2.12", - "@nteract/types": "^6.0.7", - "escape-carriage": "^1.3.0", - "immutable": "^4.0.0-rc.12", - "lodash.has": "^4.5.2", - "redux": "^4.0.1", - "redux-immutable": "^4.0.0", - "uuid": "^8.0.0" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/selectors": { - "version": "2.8.10", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/commutable": "^7.2.12", - "@nteract/types": "^6.0.7", - "immutable": "^4.0.0-rc.12", - "reselect": "^4.0.0", - "rx-jupyter": "^5.5.9" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/@nteract/types": { - "version": "6.0.7", - "license": "MIT", - "dependencies": { - "@nteract/commutable": "^7.2.12", - "immutable": "^4.0.0-rc.12", - "rxjs": "^6.3.3", - "uuid": "^8.0.0" - } - }, - "node_modules/@nteract/monaco-editor/node_modules/monaco-editor": { - "version": "0.18.1", - "license": "MIT" + "uuid": "^8.0.0" + } }, - "node_modules/@nteract/monaco-editor/node_modules/uuid": { + "node_modules/@nteract/messaging/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", @@ -7751,6 +6698,8 @@ }, "node_modules/@nteract/myths": { "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@nteract/myths/-/myths-0.1.9.tgz", + "integrity": "sha512-aPLUamd0kTnzy+8usMsGs+kgMfxfx2QNvDuxd5+ko+9DKuyMJx8quNuqphlxaLXNGiJrnfNi5krXaAyNEil3Sw==", "license": "BSD-3-Clause", "dependencies": { "react-redux": "^6.0.0", @@ -7766,10 +6715,14 @@ }, "node_modules/@nteract/myths/node_modules/react-is": { "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, "node_modules/@nteract/myths/node_modules/react-redux": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz", + "integrity": "sha512-T52I52Kxhbqy/6TEfBv85rQSDz6+Y28V/pf52vDWs1YRXG19mcFOGfHnY2HsNFHyhP+ST34Aih98fvt6tqwVcQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", @@ -7784,27 +6737,6 @@ "redux": "^2.0.0 || ^3.0.0 || ^4.0.0-0" } }, - "node_modules/@nteract/octicons": { - "version": "2.0.0", - "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/outputs": { - "version": "3.0.9", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/markdown": "^4.3.10-alpha.0", - "@nteract/mathjax": "^4.0.7", - "ansi-to-react": "^6.0.5", - "react-json-tree": "^0.11.0" - }, - "peerDependencies": { - "react": "^16.3.2", - "styled-components": ">= 4" - } - }, "node_modules/@nteract/presentational-components": { "version": "3.4.12", "resolved": "https://registry.npmjs.org/@nteract/presentational-components/-/presentational-components-3.4.12.tgz", @@ -7869,95 +6801,6 @@ "url-join": "^4.0.0" } }, - "node_modules/@nteract/stateful-components": { - "version": "1.7.0", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/commutable": "^7.3.2", - "@nteract/core": "^15.1.0", - "@nteract/fixtures": "^2.3.10", - "@nteract/markdown": "4.5.1", - "@nteract/mythic-configuration": "^1.0.2", - "@nteract/outputs": "^3.0.9", - "@nteract/presentational-components": "^3.4.2", - "immutable": "^4.0.0-rc.12", - "redux": "^4.0.4" - } - }, - "node_modules/@nteract/stateful-components/node_modules/@nteract/fixtures": { - "version": "2.3.19", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/commutable": "^7.4.5", - "@nteract/reducers": "^5.1.9", - "@nteract/types": "^7.1.9" - } - }, - "node_modules/@nteract/stateful-components/node_modules/@nteract/markdown": { - "version": "4.5.1", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/mathjax": "^4.0.7", - "@nteract/presentational-components": "^3.3.11", - "react-markdown": "^4.0.0" - }, - "peerDependencies": { - "react": "^16.11.0", - "react-dom": "^16.3.2" - } - }, - "node_modules/@nteract/styles": { - "version": "2.0.2", - "license": "BSD-3-Clause" - }, - "node_modules/@nteract/transform-geojson": { - "version": "5.1.8", - "license": "BSD-3-Clause", - "dependencies": { - "leaflet": "^1.3.4" - }, - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/transform-model-debug": { - "version": "5.0.1", - "license": "BSD-3-Clause", - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/transform-plotly": { - "version": "6.1.6", - "license": "BSD-3-Clause", - "dependencies": { - "lodash.clonedeep": "^4.5.0", - "plotly.js-dist": "^1.51.3" - }, - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/transform-vdom": { - "version": "4.0.11", - "license": "BSD-3-Clause", - "dependencies": { - "lodash.clonedeep": "^4.5.0" - }, - "peerDependencies": { - "react": "^16.3.2" - } - }, - "node_modules/@nteract/transform-vega": { - "version": "7.0.6", - "license": "BSD-3-Clause", - "dependencies": { - "@nteract/any-vega": "^1.0.1" - }, - "peerDependencies": { - "react": "^16.3.2" - } - }, "node_modules/@nteract/types": { "version": "7.1.9", "license": "MIT", @@ -8491,100 +7334,6 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, - "node_modules/@phosphor/algorithm": { - "version": "1.2.0", - "license": "BSD-3-Clause" - }, - "node_modules/@phosphor/collections": { - "version": "1.2.0", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0" - } - }, - "node_modules/@phosphor/commands": { - "version": "1.7.2", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0", - "@phosphor/coreutils": "^1.3.1", - "@phosphor/disposable": "^1.3.1", - "@phosphor/domutils": "^1.1.4", - "@phosphor/keyboard": "^1.1.3", - "@phosphor/signaling": "^1.3.1" - } - }, - "node_modules/@phosphor/coreutils": { - "version": "1.3.1", - "license": "BSD-3-Clause" - }, - "node_modules/@phosphor/disposable": { - "version": "1.3.1", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0", - "@phosphor/signaling": "^1.3.1" - } - }, - "node_modules/@phosphor/domutils": { - "version": "1.1.4", - "license": "BSD-3-Clause" - }, - "node_modules/@phosphor/dragdrop": { - "version": "1.4.1", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/coreutils": "^1.3.1", - "@phosphor/disposable": "^1.3.1" - } - }, - "node_modules/@phosphor/keyboard": { - "version": "1.1.3", - "license": "BSD-3-Clause" - }, - "node_modules/@phosphor/messaging": { - "version": "1.3.0", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0", - "@phosphor/collections": "^1.2.0" - } - }, - "node_modules/@phosphor/properties": { - "version": "1.1.3", - "license": "BSD-3-Clause" - }, - "node_modules/@phosphor/signaling": { - "version": "1.3.1", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0" - } - }, - "node_modules/@phosphor/virtualdom": { - "version": "1.2.0", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0" - } - }, - "node_modules/@phosphor/widgets": { - "version": "1.9.3", - "license": "BSD-3-Clause", - "dependencies": { - "@phosphor/algorithm": "^1.2.0", - "@phosphor/commands": "^1.7.2", - "@phosphor/coreutils": "^1.3.1", - "@phosphor/disposable": "^1.3.1", - "@phosphor/domutils": "^1.1.4", - "@phosphor/dragdrop": "^1.4.1", - "@phosphor/keyboard": "^1.1.3", - "@phosphor/messaging": "^1.3.0", - "@phosphor/properties": "^1.1.3", - "@phosphor/signaling": "^1.3.1", - "@phosphor/virtualdom": "^1.2.0" - } - }, "node_modules/@playwright/test": { "version": "1.59.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", @@ -8618,33 +7367,6 @@ "version": "2.0.0", "license": "MIT" }, - "node_modules/@rjsf/core": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-3.2.1.tgz", - "integrity": "sha512-dk8ihvxFbcuIwU7G+HiJbFgwyIvaumPt5g5zfnuC26mwTUPlaDGFXKK2yITp8tJ3+hcwS5zEXtAN9wUkfuM4jA==", - "dependencies": { - "@types/json-schema": "^7.0.7", - "ajv": "^6.7.0", - "core-js-pure": "^3.6.5", - "json-schema-merge-allof": "^0.6.0", - "jsonpointer": "^5.0.0", - "lodash": "^4.17.15", - "nanoid": "^3.1.23", - "prop-types": "^15.7.2", - "react-is": "^16.9.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/@rjsf/core/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/@shikijs/core": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.12.0.tgz", @@ -9840,13 +8562,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-table": { - "version": "6.8.14", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-window": { "version": "1.8.8", "dev": true, @@ -10664,10 +9379,6 @@ "version": "5.0.1", "license": "MIT" }, - "node_modules/anser": { - "version": "1.4.10", - "license": "MIT" - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -10712,18 +9423,6 @@ "node": ">=4" } }, - "node_modules/ansi-to-react": { - "version": "6.1.6", - "license": "BSD-3-Clause", - "dependencies": { - "anser": "^1.4.1", - "escape-carriage": "^1.3.0" - }, - "peerDependencies": { - "react": "^16.3.2 || ^17.0.0", - "react-dom": "^16.3.2 || ^17.0.0" - } - }, "node_modules/antlr4": { "version": "4.7.1", "license": "BSD" @@ -11462,18 +10161,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/babel-runtime": { - "version": "6.26.0", - "license": "MIT", - "dependencies": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "node_modules/babel-runtime/node_modules/regenerator-runtime": { - "version": "0.11.1", - "license": "MIT" - }, "node_modules/bail": { "version": "1.0.5", "license": "MIT", @@ -11486,10 +10173,6 @@ "version": "1.0.2", "license": "MIT" }, - "node_modules/base16": { - "version": "1.0.0", - "license": "MIT" - }, "node_modules/base64-arraybuffer": { "version": "0.2.0", "engines": { @@ -11542,6 +10225,7 @@ }, "node_modules/big.js": { "version": "5.2.2", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -11555,10 +10239,6 @@ "node": ">=8" } }, - "node_modules/bintrees": { - "version": "1.0.2", - "license": "MIT" - }, "node_modules/bl": { "version": "4.1.0", "license": "MIT", @@ -12187,10 +10867,6 @@ "node": ">= 0.12.0" } }, - "node_modules/codemirror": { - "version": "5.61.1", - "license": "MIT" - }, "node_modules/collapse-white-space": { "version": "1.0.6", "license": "MIT", @@ -12301,27 +10977,6 @@ "node": ">= 0.6" } }, - "node_modules/compute-gcd": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz", - "integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==", - "dependencies": { - "validate.io-array": "^1.0.3", - "validate.io-function": "^1.0.2", - "validate.io-integer-array": "^1.0.0" - } - }, - "node_modules/compute-lcm": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz", - "integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==", - "dependencies": { - "compute-gcd": "^1.2.1", - "validate.io-array": "^1.0.3", - "validate.io-function": "^1.0.2", - "validate.io-integer-array": "^1.0.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "license": "MIT" @@ -12509,11 +11164,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/core-js": { - "version": "2.6.12", - "hasInstallScript": true, - "license": "MIT" - }, "node_modules/core-js-compat": { "version": "3.38.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", @@ -12529,6 +11179,7 @@ }, "node_modules/core-js-pure": { "version": "3.33.3", + "dev": true, "hasInstallScript": true, "license": "MIT", "funding": { @@ -12777,14 +11428,6 @@ "signals": "<2.0" } }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", - "license": "ISC", - "peer": true - }, "node_modules/css-color-keywords": { "version": "1.0.0", "license": "ISC", @@ -12972,17 +11615,6 @@ "node": ">=12" } }, - "node_modules/d3-brush": { - "version": "2.1.0", - "license": "BSD-3-Clause", - "dependencies": { - "d3-dispatch": "1 - 2", - "d3-drag": "2", - "d3-interpolate": "1 - 2", - "d3-selection": "2", - "d3-transition": "2" - } - }, "node_modules/d3-chord": { "version": "3.0.1", "license": "ISC", @@ -12995,6 +11627,8 @@ }, "node_modules/d3-collection": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==", "license": "BSD-3-Clause" }, "node_modules/d3-color": { @@ -13117,14 +11751,6 @@ "node": ">=12" } }, - "node_modules/d3-glyphedge": { - "version": "1.2.0", - "license": "Unlicense" - }, - "node_modules/d3-hexbin": { - "version": "0.2.2", - "license": "BSD-3-Clause" - }, "node_modules/d3-hierarchy": { "version": "3.1.2", "license": "ISC", @@ -13146,22 +11772,6 @@ "node": ">=12" } }, - "node_modules/d3-path-arrows": { - "version": "0.4.0", - "license": "MIT", - "dependencies": { - "d3-array": "^1.2.1", - "d3-selection": "^1.1.0" - } - }, - "node_modules/d3-path-arrows/node_modules/d3-array": { - "version": "1.2.4", - "license": "BSD-3-Clause" - }, - "node_modules/d3-path-arrows/node_modules/d3-selection": { - "version": "1.4.2", - "license": "BSD-3-Clause" - }, "node_modules/d3-polygon": { "version": "3.0.1", "license": "ISC", @@ -13183,19 +11793,6 @@ "node": ">=12" } }, - "node_modules/d3-sankey-circular": { - "version": "0.25.0", - "license": "MIT", - "dependencies": { - "d3-array": "^1.2.1", - "d3-collection": "^1.0.4", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey-circular/node_modules/d3-array": { - "version": "1.2.4", - "license": "BSD-3-Clause" - }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", @@ -13226,17 +11823,6 @@ "version": "2.0.0", "license": "BSD-3-Clause" }, - "node_modules/d3-shape": { - "version": "1.3.7", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-shape/node_modules/d3-path": { - "version": "1.0.9", - "license": "BSD-3-Clause" - }, "node_modules/d3-time": { "version": "2.1.1", "license": "BSD-3-Clause", @@ -13269,10 +11855,6 @@ "d3-selection": "2" } }, - "node_modules/d3-voronoi": { - "version": "1.1.4", - "license": "BSD-3-Clause" - }, "node_modules/d3-zoom": { "version": "3.0.0", "license": "ISC", @@ -13592,6 +12174,7 @@ }, "node_modules/deepmerge": { "version": "4.3.1", + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14063,6 +12646,7 @@ }, "node_modules/emojis-list": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -15851,11 +14435,6 @@ "node": ">= 0.6" } }, - "node_modules/free-style": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", - "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==" - }, "node_modules/fresh": { "version": "0.5.2", "dev": true, @@ -15916,6 +14495,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -16079,14 +14659,6 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, - "node_modules/global": { - "version": "4.4.0", - "license": "MIT", - "dependencies": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, "node_modules/global-modules": { "version": "2.0.0", "dev": true, @@ -17601,13 +16173,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -17741,6 +16306,7 @@ }, "node_modules/is-what": { "version": "3.14.1", + "dev": true, "license": "MIT" }, "node_modules/is-whitespace-character": { @@ -22114,27 +20680,9 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "license": "MIT" - }, - "node_modules/json-schema-compare": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", - "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", - "dependencies": { - "lodash": "^4.17.4" - } - }, - "node_modules/json-schema-merge-allof": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.6.0.tgz", - "integrity": "sha512-LEw4VMQVRceOPLuGRWcxW5orTTiR9ZAtqTAe4rQUjNADTeR81bezBVFa0MqIwp0YmHIM1KkhSjZM7o+IQhaPbQ==", - "dependencies": { - "compute-lcm": "^1.1.0", - "json-schema-compare": "^0.2.2", - "lodash": "^4.17.4" - } + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -22189,14 +20737,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -22297,10 +20837,6 @@ "version": "3.5.1", "license": "MIT" }, - "node_modules/labella": { - "version": "1.1.4", - "license": "Apache-2.0" - }, "node_modules/launch-editor": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", @@ -22312,10 +20848,6 @@ "shell-quote": "^1.8.4" } }, - "node_modules/leaflet": { - "version": "1.9.4", - "license": "BSD-2-Clause" - }, "node_modules/less": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/less/-/less-4.5.1.tgz", @@ -22525,10 +21057,6 @@ "version": "4.5.0", "license": "MIT" }, - "node_modules/lodash.curry": { - "version": "4.1.1", - "license": "MIT" - }, "node_modules/lodash.debounce": { "version": "4.0.8", "license": "MIT" @@ -22543,10 +21071,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.flow": { - "version": "3.5.0", - "license": "MIT" - }, "node_modules/lodash.has": { "version": "4.5.2", "license": "MIT" @@ -22703,18 +21227,6 @@ "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/martinez-polygon-clipping": { - "version": "0.1.5", - "license": "MIT", - "dependencies": { - "bintrees": "^1.0.1", - "tinyqueue": "^1.1.0" - } - }, - "node_modules/material-colors": { - "version": "1.2.6", - "license": "ISC" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -22761,13 +21273,6 @@ "version": "5.2.1", "license": "MIT" }, - "node_modules/merge-anything": { - "version": "2.4.4", - "license": "MIT", - "dependencies": { - "is-what": "^3.3.1" - } - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -22945,13 +21450,6 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, - "node_modules/moment": { - "version": "2.29.4", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/monaco-editor": { "version": "0.44.0", "license": "MIT" @@ -23164,6 +21662,7 @@ }, "node_modules/node-fetch": { "version": "2.6.7", + "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -23223,13 +21722,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/numeral": { - "version": "2.0.6", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/nwsapi": { "version": "2.2.7", "dev": true, @@ -23559,10 +22051,6 @@ "node": ">= 0.10" } }, - "node_modules/parse-srcset": { - "version": "1.0.2", - "license": "MIT" - }, "node_modules/parse5": { "version": "7.1.2", "dev": true, @@ -23792,10 +22280,6 @@ "util": "^0.10.3" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "license": "MIT" - }, "node_modules/path-exists": { "version": "3.0.0", "license": "MIT", @@ -23976,17 +22460,6 @@ "version": "1.52.3", "license": "MIT" }, - "node_modules/plotly.js-dist": { - "version": "1.58.5", - "license": "MIT" - }, - "node_modules/polygon-offset": { - "version": "0.3.1", - "license": "MIT", - "dependencies": { - "martinez-polygon-clipping": "^0.1.5" - } - }, "node_modules/popper.js": { "version": "1.16.1", "license": "MIT", @@ -24016,6 +22489,7 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -24115,6 +22589,7 @@ "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, "funding": [ { "type": "github", @@ -24321,6 +22796,7 @@ }, "node_modules/process": { "version": "0.11.10", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -24445,10 +22921,6 @@ "node": ">=6" } }, - "node_modules/pure-color": { - "version": "1.3.0", - "license": "MIT" - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -24501,6 +22973,7 @@ "version": "6.15.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -24521,6 +22994,7 @@ }, "node_modules/querystringify": { "version": "2.2.0", + "dev": true, "license": "MIT" }, "node_modules/queue-microtask": { @@ -24668,44 +23142,6 @@ "react-dom": ">=15.6.2" } }, - "node_modules/react-annotation": { - "version": "3.0.0-beta.4", - "license": "Apache-2.0", - "dependencies": { - "prop-types": "^15", - "viz-annotation": "0.0.5" - }, - "peerDependencies": { - "react": "^16", - "react-dom": "^16" - } - }, - "node_modules/react-base16-styling": { - "version": "0.5.3", - "license": "MIT", - "dependencies": { - "base16": "^1.0.0", - "lodash.curry": "^4.0.1", - "lodash.flow": "^3.3.0", - "pure-color": "^1.2.0" - } - }, - "node_modules/react-color": { - "version": "2.19.3", - "license": "MIT", - "dependencies": { - "@icons/material": "^0.2.4", - "lodash": "^4.17.15", - "lodash-es": "^4.17.15", - "material-colors": "^1.2.1", - "prop-types": "^15.5.10", - "reactcss": "^1.2.0", - "tinycolor2": "^1.4.1" - }, - "peerDependencies": { - "react": "*" - } - }, "node_modules/react-dev-utils": { "version": "12.0.1", "dev": true, @@ -24974,52 +23410,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-hot-loader": { - "version": "4.13.1", - "license": "MIT", - "dependencies": { - "fast-levenshtein": "^2.0.6", - "global": "^4.3.0", - "hoist-non-react-statics": "^3.3.0", - "loader-utils": "^2.0.3", - "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4", - "shallowequal": "^1.1.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "@types/react": "^15.0.0 || ^16.0.0 || ^17.0.0", - "react": "^15.0.0 || ^16.0.0 || ^17.0.0", - "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-hot-loader/node_modules/loader-utils": { - "version": "2.0.4", - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/react-hot-loader/node_modules/source-map": { - "version": "0.7.4", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, "node_modules/react-hotkeys": { "version": "2.0.0", "license": "ISC", @@ -25055,19 +23445,6 @@ "version": "18.2.0", "license": "MIT" }, - "node_modules/react-json-tree": { - "version": "0.11.2", - "license": "MIT", - "dependencies": { - "babel-runtime": "^6.6.1", - "prop-types": "^15.5.8", - "react-base16-styling": "^0.5.1" - }, - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0", - "react-dom": "^15.0.0 || ^16.0.0" - } - }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "license": "MIT" @@ -25177,36 +23554,6 @@ "react": ">= 0.14.0" } }, - "node_modules/react-table": { - "version": "6.11.5", - "license": "MIT", - "dependencies": { - "@types/react-table": "^6.8.5", - "classnames": "^2.2.5", - "react-is": "^16.8.1" - }, - "peerDependencies": { - "prop-types": "^15.7.0", - "react": "^16.x.x", - "react-dom": "^16.x.x" - } - }, - "node_modules/react-table-hoc-fixed-columns": { - "version": "2.1.3", - "license": "MIT", - "dependencies": { - "classnames": "^2.2.6", - "uniqid": "^5.0.3" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.2.0", - "react-dom": "^15.3.0 || ^16.2.0" - } - }, - "node_modules/react-table/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, "node_modules/react-test-renderer": { "version": "16.14.0", "dev": true, @@ -25235,13 +23582,6 @@ "object-assign": "^4.1.1" } }, - "node_modules/react-timeago": { - "version": "4.4.0", - "license": "MIT", - "peerDependencies": { - "react": "^15.0.0 || ^16.0.0" - } - }, "node_modules/react-toggle": { "version": "4.1.3", "license": "MIT", @@ -25298,13 +23638,6 @@ "react": ">=0.14.1" } }, - "node_modules/reactcss": { - "version": "1.2.3", - "license": "MIT", - "dependencies": { - "lodash": "^4.0.1" - } - }, "node_modules/readable-stream": { "version": "2.3.8", "dev": true, @@ -25568,10 +23901,6 @@ "jsesc": "bin/jsesc" } }, - "node_modules/regression": { - "version": "2.0.1", - "license": "MIT" - }, "node_modules/relateurl": { "version": "0.2.7", "dev": true, @@ -25725,6 +24054,7 @@ }, "node_modules/requires-port": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/reselect": { @@ -25958,48 +24288,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/sanitize-html": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", - "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", - "dependencies": { - "deepmerge": "^4.2.2", - "escape-string-regexp": "^4.0.0", - "htmlparser2": "^8.0.0", - "is-plain-object": "^5.0.0", - "parse-srcset": "^1.0.2", - "postcss": "^8.3.11" - } - }, - "node_modules/sanitize-html/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sanitize-html/node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, "node_modules/sax": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", @@ -26032,6 +24320,7 @@ "node_modules/scheduler": { "version": "0.20.2", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -26538,6 +24827,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -26897,23 +25187,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svg-path-bounding-box": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "svgpath": "^2.0.0" - }, - "bin": { - "svg-path-bounding-box": "bin/caster.js" - } - }, - "node_modules/svgpath": { - "version": "2.6.0", - "license": "MIT", - "funding": { - "url": "https://github.com/fontello/svg2ttf?sponsor=1" - } - }, "node_modules/swr": { "version": "0.4.0", "license": "MIT", @@ -27110,18 +25383,10 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, - "node_modules/tinycolor2": { - "version": "1.6.0", - "license": "MIT" - }, "node_modules/tinykeys": { "version": "2.1.0", "license": "MIT" }, - "node_modules/tinyqueue": { - "version": "1.2.3", - "license": "ISC" - }, "node_modules/tmp": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", @@ -27550,20 +25815,6 @@ "node": ">=4.2.0" } }, - "node_modules/typestyle": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.4.0.tgz", - "integrity": "sha512-/d1BL6Qi+YlMLEydnUEB8KL/CAjAN8cyt3/UyGnOyBrWf7bLGcR/6yhmsaUstO2IcYwZfagjE7AIzuI2vUW9mg==", - "dependencies": { - "csstype": "3.0.10", - "free-style": "3.1.0" - } - }, - "node_modules/typestyle/node_modules/csstype": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", - "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" - }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -27685,10 +25936,6 @@ "x-is-string": "^0.1.0" } }, - "node_modules/uniqid": { - "version": "5.4.0", - "license": "MIT" - }, "node_modules/unist-util-is": { "version": "3.0.0", "license": "MIT" @@ -27791,18 +26038,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", - "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", - "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.12.3" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/url-join": { "version": "4.0.1", "license": "MIT" @@ -27865,17 +26100,13 @@ }, "node_modules/url-parse": { "version": "1.5.10", + "dev": true, "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==" - }, "node_modules/use-disposable": { "version": "1.0.2", "license": "MIT", @@ -27969,38 +26200,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "devOptional": true }, - "node_modules/validate.io-array": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", - "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==" - }, - "node_modules/validate.io-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz", - "integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==" - }, - "node_modules/validate.io-integer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz", - "integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==", - "dependencies": { - "validate.io-number": "^1.0.3" - } - }, - "node_modules/validate.io-integer-array": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz", - "integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==", - "dependencies": { - "validate.io-array": "^1.0.3", - "validate.io-integer": "^1.0.4" - } - }, - "node_modules/validate.io-number": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz", - "integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==" - }, "node_modules/values-to-keys": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/values-to-keys/-/values-to-keys-1.1.0.tgz", @@ -28043,24 +26242,6 @@ "unist-util-stringify-position": "^1.1.1" } }, - "node_modules/viz-annotation": { - "version": "0.0.5", - "license": "ISC", - "dependencies": { - "d3-shape": "~1.0.4" - } - }, - "node_modules/viz-annotation/node_modules/d3-path": { - "version": "1.0.9", - "license": "BSD-3-Clause" - }, - "node_modules/viz-annotation/node_modules/d3-shape": { - "version": "1.0.6", - "license": "BSD-3-Clause", - "dependencies": { - "d3-path": "1" - } - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -29555,22 +27736,15 @@ "version": "4.19.0", "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz", "integrity": "sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ==", - "deprecated": "This package is now deprecated. Move to @xterm/xterm instead." + "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", + "license": "MIT" }, "node_modules/xterm-addon-fit": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz", "integrity": "sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==", "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", - "peerDependencies": { - "xterm": "^4.0.0" - } - }, - "node_modules/xterm-addon-web-links": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.6.0.tgz", - "integrity": "sha512-H6XzjWWZu8FBo+fnYpxdPk9w5M6drbsvwPEJZGRS38MihiQaVFpKlCMKdfRgDbKGE530tw1yH54rhpZfHgt2/A==", - "deprecated": "This package is now deprecated. Move to @xterm/addon-web-links instead.", + "license": "MIT", "peerDependencies": { "xterm": "^4.0.0" } diff --git a/package.json b/package.json index 9843793e6c..f90908e6c1 100644 --- a/package.json +++ b/package.json @@ -13,35 +13,14 @@ "@babel/plugin-proposal-decorators": "7.12.12", "@fluentui/react": "8.119.0", "@fluentui/react-components": "9.54.2", - "@jupyterlab/services": "6.0.2", - "@jupyterlab/terminal": "3.6.8", "@microsoft/applicationinsights-web": "2.6.1", "@nteract/commutable": "7.5.1", - "@nteract/connected-components": "6.8.2", "@nteract/core": "15.1.9", - "@nteract/data-explorer": "8.2.12", - "@nteract/directory-listing": "2.0.6", - "@nteract/dropdown-menu": "1.0.1", - "@nteract/editor": "10.1.12", "@nteract/fixtures": "2.3.0", - "@nteract/iron-icons": "1.0.0", - "@nteract/jupyter-widgets": "2.0.0", - "@nteract/logos": "1.0.0", "@nteract/markdown": "4.6.0", - "@nteract/monaco-editor": "3.2.2", - "@nteract/octicons": "2.0.0", - "@nteract/outputs": "3.0.9", - "@nteract/presentational-components": "3.4.12", - "@nteract/stateful-components": "1.7.0", - "@nteract/styles": "2.0.2", - "@nteract/transform-geojson": "5.1.8", - "@nteract/transform-model-debug": "5.0.1", - "@nteract/transform-plotly": "6.1.6", - "@nteract/transform-vdom": "4.0.11", - "@nteract/transform-vega": "7.0.6", + "@nteract/myths": "0.1.9", "@octokit/request": "8.4.1", "@octokit/rest": "17.9.2", - "@phosphor/widgets": "1.9.3", "@testing-library/jest-dom": "6.4.6", "@types/lodash": "4.14.171", "@types/mkdirp": "1.0.1", @@ -59,6 +38,7 @@ "crossroads": "0.12.2", "css-element-queries": "1.1.1", "d3": "7.9.0", + "d3-collection": "1.0.7", "datatables.net-colreorder-dt": "1.7.0", "datatables.net-dt": "1.13.8", "date-fns": "1.29.0", @@ -118,6 +98,8 @@ "uuid": "9.0.0", "web-vitals": "4.2.4", "ws": "8.20.1", + "xterm": "4.19.0", + "xterm-addon-fit": "0.5.0", "zustand": "3.5.0" }, "overrides": { diff --git a/patches/@phosphor+virtualdom+1.2.0.patch b/patches/@phosphor+virtualdom+1.2.0.patch deleted file mode 100644 index cb409335ca..0000000000 --- a/patches/@phosphor+virtualdom+1.2.0.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@phosphor/virtualdom/lib/index.d.ts b/node_modules/@phosphor/virtualdom/lib/index.d.ts -index 95682b9..73e2daa 100644 ---- a/node_modules/@phosphor/virtualdom/lib/index.d.ts -+++ b/node_modules/@phosphor/virtualdom/lib/index.d.ts -@@ -58,7 +58,7 @@ export declare type ElementEventMap = { - ondrop: DragEvent; - ondurationchange: Event; - onemptied: Event; -- onended: MediaStreamErrorEvent; -+ onended: ErrorEvent; - onerror: ErrorEvent; - onfocus: FocusEvent; - oninput: Event; diff --git a/src/CellOutputViewer/CellOutputViewer.less b/src/CellOutputViewer/CellOutputViewer.less deleted file mode 100644 index 0be451c1ae..0000000000 --- a/src/CellOutputViewer/CellOutputViewer.less +++ /dev/null @@ -1,15 +0,0 @@ -.schema-analyzer-cell-outputs { - padding: 10px 2px; -} - -// Mimic FluentUI8's DocumentCard style -.schema-analyzer-cell-output { - margin-bottom: 20px; - padding: 14px 20px; - border: 1px solid rgb(237, 235, 233); -} - -.schema-analyzer-cell-output:hover { - border-color: rgb(200, 198, 196); - box-shadow: inset 0 0 0 1px rgb(200, 198, 196) -} \ No newline at end of file diff --git a/src/CellOutputViewer/CellOutputViewer.tsx b/src/CellOutputViewer/CellOutputViewer.tsx deleted file mode 100644 index 4deccb7a22..0000000000 --- a/src/CellOutputViewer/CellOutputViewer.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { createImmutableOutput, JSONObject, OnDiskOutput } from "@nteract/commutable"; -// import outputs individually to avoid increasing the bundle size -import { KernelOutputError } from "@nteract/outputs/lib/components/kernel-output-error"; -import { Output } from "@nteract/outputs/lib/components/output"; -import { StreamText } from "@nteract/outputs/lib/components/stream-text"; -import { ContentRef } from "@nteract/types"; -import "bootstrap/dist/css/bootstrap.css"; -import postRobot from "post-robot"; -import * as React from "react"; -import * as ReactDOM from "react-dom"; -import "../../externals/iframeResizer.contentWindow.min.js"; // Required for iFrameResizer to work -import { SnapshotRequest } from "../Explorer/Notebook/NotebookComponent/types"; -import "../Explorer/Notebook/NotebookRenderer/base.css"; -import "../Explorer/Notebook/NotebookRenderer/default.css"; -import { NotebookUtil } from "../Explorer/Notebook/NotebookUtil"; -import "./CellOutputViewer.less"; -import { TransformMedia } from "./TransformMedia"; - -export interface SnapshotResponse { - imageSrc: string; - requestId: string; -} -export interface CellOutputViewerProps { - id: string; - contentRef: ContentRef; - outputsContainerClassName: string; - outputClassName: string; - outputs: OnDiskOutput[]; - onMetadataChange: (metadata: JSONObject, mediaType: string, index?: number) => void; -} - -const onInit = async () => { - postRobot.on( - "props", - { - window: window.parent, - domain: window.location.origin, - }, - (event) => { - // Typescript definition for event is wrong. So read props by casting to - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const props = (event as any).data as CellOutputViewerProps; - const outputs = ( -
- {props.outputs?.map((output, index) => ( -
- - props.onMetadataChange(metadata, mediaType, index)} - /> - props.onMetadataChange(metadata, mediaType, index)} - /> - - - -
- ))} -
- ); - - ReactDOM.render(outputs, document.getElementById("cellOutput")); - }, - ); - - postRobot.on( - "snapshotRequest", - { - window: window.parent, - domain: window.location.origin, - }, - async (event): Promise => { - const topNode = document.getElementById("cellOutput"); - if (!topNode) { - const errorMsg = "No top node to snapshot"; - return Promise.reject(new Error(errorMsg)); - } - - // Typescript definition for event is wrong. So read props by casting to - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const snapshotRequest = (event as any).data as SnapshotRequest; - const result = await NotebookUtil.takeScreenshotDomToImage( - topNode, - snapshotRequest.aspectRatio, - undefined, - snapshotRequest.downloadFilename, - ); - - return { - imageSrc: result.imageSrc, - requestId: snapshotRequest.requestId, - }; - }, - ); -}; - -// Entry point -window.addEventListener("load", onInit); diff --git a/src/CellOutputViewer/TransformMedia.tsx b/src/CellOutputViewer/TransformMedia.tsx deleted file mode 100644 index 1f1c7f67b2..0000000000 --- a/src/CellOutputViewer/TransformMedia.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { ImmutableDisplayData, ImmutableExecuteResult, JSONObject } from "@nteract/commutable"; -// import outputs individually to avoid increasing the bundle size -import { HTML } from "@nteract/outputs/lib/components/media/html"; -import { Image } from "@nteract/outputs/lib/components/media/image"; -import { JavaScript } from "@nteract/outputs/lib/components/media/javascript"; -import { Json } from "@nteract/outputs/lib/components/media/json"; -import { LaTeX } from "@nteract/outputs/lib/components/media/latex"; -import { Plain } from "@nteract/outputs/lib/components/media/plain"; -import { SVG } from "@nteract/outputs/lib/components/media/svg"; -import { ContentRef } from "@nteract/types"; -import React, { Suspense } from "react"; - -const EmptyTransform = (): JSX.Element => <>; - -const displayOrder = [ - "application/vnd.jupyter.widget-view+json", - "application/vnd.vega.v5+json", - "application/vnd.vega.v4+json", - "application/vnd.vega.v3+json", - "application/vnd.vega.v2+json", - "application/vnd.vegalite.v4+json", - "application/vnd.vegalite.v3+json", - "application/vnd.vegalite.v2+json", - "application/vnd.vegalite.v1+json", - "application/geo+json", - "application/vnd.plotly.v1+json", - "text/vnd.plotly.v1+html", - "application/x-nteract-model-debug+json", - "application/vnd.dataresource+json", - "application/vdom.v1+json", - "application/json", - "application/javascript", - "text/html", - "text/markdown", - "text/latex", - "image/svg+xml", - "image/gif", - "image/png", - "image/jpeg", - "text/plain", -]; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const transformsById = new Map>([ - ["text/vnd.plotly.v1+html", React.lazy(() => import("@nteract/transform-plotly"))], - ["application/vnd.plotly.v1+json", React.lazy(() => import("@nteract/transform-plotly"))], - ["application/geo+json", EmptyTransform], // TODO: The geojson transform will likely need some work because of the basemap URL(s) - ["application/x-nteract-model-debug+json", React.lazy(() => import("@nteract/transform-model-debug"))], - ["application/vnd.dataresource+json", React.lazy(() => import("@nteract/data-explorer"))], - ["application/vnd.jupyter.widget-view+json", React.lazy(() => import("./transforms/WidgetDisplay"))], - ["application/vnd.vegalite.v1+json", React.lazy(() => import("./transforms/VegaLite1"))], - ["application/vnd.vegalite.v2+json", React.lazy(() => import("./transforms/VegaLite2"))], - ["application/vnd.vegalite.v3+json", React.lazy(() => import("./transforms/VegaLite3"))], - ["application/vnd.vegalite.v4+json", React.lazy(() => import("./transforms/VegaLite4"))], - ["application/vnd.vega.v2+json", React.lazy(() => import("./transforms/Vega2"))], - ["application/vnd.vega.v3+json", React.lazy(() => import("./transforms/Vega3"))], - ["application/vnd.vega.v4+json", React.lazy(() => import("./transforms/Vega4"))], - ["application/vnd.vega.v5+json", React.lazy(() => import("./transforms/Vega5"))], - ["application/vdom.v1+json", React.lazy(() => import("@nteract/transform-vdom"))], - ["application/json", Json], - ["application/javascript", JavaScript], - ["text/html", HTML], - ["text/markdown", React.lazy(() => import("@nteract/outputs/lib/components/media/markdown"))], // Markdown increases the bundle size so lazy load it - ["text/latex", LaTeX], - ["image/svg+xml", SVG], - ["image/gif", Image], - ["image/png", Image], - ["image/jpeg", Image], - ["text/plain", Plain], -]); - -interface TransformMediaProps { - output_type: string; - id: string; - contentRef: ContentRef; - output?: ImmutableDisplayData | ImmutableExecuteResult; - onMetadataChange: (metadata: JSONObject, mediaType: string) => void; -} - -export const TransformMedia = (props: TransformMediaProps): JSX.Element => { - const { Media, mediaType, data, metadata } = getMediaInfo(props); - - // If we had no valid result, return an empty output - if (!mediaType || !data) { - return <>; - } - - return ( - Loading...}> - - - ); -}; - -const getMediaInfo = (props: TransformMediaProps) => { - const { output, output_type } = props; - // This component should only be used with display data and execute result - if (!output || !(output_type === "display_data" || output_type === "execute_result")) { - console.warn("connected transform media managed to get a non media bundle output"); - return { - Media: EmptyTransform, - }; - } - - // Find the first mediaType in the output data that we support with a handler - const mediaType = displayOrder.find( - (key) => - Object.prototype.hasOwnProperty.call(output.data, key) && - (Object.prototype.hasOwnProperty.call(transformsById, key) || transformsById.get(key)), - ); - - if (mediaType) { - const metadata = output.metadata.get(mediaType); - const data = output.data[mediaType]; - - const Media = transformsById.get(mediaType); - return { - Media, - mediaType, - data, - metadata, - }; - } - - return { - Media: EmptyTransform, - mediaType, - output, - }; -}; - -export default TransformMedia; diff --git a/src/CellOutputViewer/cellOutputViewer.html b/src/CellOutputViewer/cellOutputViewer.html deleted file mode 100644 index b801b81136..0000000000 --- a/src/CellOutputViewer/cellOutputViewer.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Cell Output Viewer - - - -
- - diff --git a/src/CellOutputViewer/transforms/Vega2.ts b/src/CellOutputViewer/transforms/Vega2.ts deleted file mode 100644 index 08a358f54c..0000000000 --- a/src/CellOutputViewer/transforms/Vega2.ts +++ /dev/null @@ -1 +0,0 @@ -export { Vega2 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/Vega3.ts b/src/CellOutputViewer/transforms/Vega3.ts deleted file mode 100644 index 87289b52fa..0000000000 --- a/src/CellOutputViewer/transforms/Vega3.ts +++ /dev/null @@ -1 +0,0 @@ -export { Vega3 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/Vega4.ts b/src/CellOutputViewer/transforms/Vega4.ts deleted file mode 100644 index a95100d63a..0000000000 --- a/src/CellOutputViewer/transforms/Vega4.ts +++ /dev/null @@ -1 +0,0 @@ -export { Vega4 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/Vega5.ts b/src/CellOutputViewer/transforms/Vega5.ts deleted file mode 100644 index 6fa20b452c..0000000000 --- a/src/CellOutputViewer/transforms/Vega5.ts +++ /dev/null @@ -1 +0,0 @@ -export { Vega5 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite1.ts b/src/CellOutputViewer/transforms/VegaLite1.ts deleted file mode 100644 index fb615d92c6..0000000000 --- a/src/CellOutputViewer/transforms/VegaLite1.ts +++ /dev/null @@ -1 +0,0 @@ -export { VegaLite1 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite2.ts b/src/CellOutputViewer/transforms/VegaLite2.ts deleted file mode 100644 index f664e0bdaf..0000000000 --- a/src/CellOutputViewer/transforms/VegaLite2.ts +++ /dev/null @@ -1 +0,0 @@ -export { VegaLite2 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite3.ts b/src/CellOutputViewer/transforms/VegaLite3.ts deleted file mode 100644 index 8a57abbc49..0000000000 --- a/src/CellOutputViewer/transforms/VegaLite3.ts +++ /dev/null @@ -1 +0,0 @@ -export { VegaLite3 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/VegaLite4.ts b/src/CellOutputViewer/transforms/VegaLite4.ts deleted file mode 100644 index fca92c80fa..0000000000 --- a/src/CellOutputViewer/transforms/VegaLite4.ts +++ /dev/null @@ -1 +0,0 @@ -export { VegaLite4 as default } from "@nteract/transform-vega"; diff --git a/src/CellOutputViewer/transforms/WidgetDisplay.ts b/src/CellOutputViewer/transforms/WidgetDisplay.ts deleted file mode 100644 index 4387a4f5dc..0000000000 --- a/src/CellOutputViewer/transforms/WidgetDisplay.ts +++ /dev/null @@ -1 +0,0 @@ -export { WidgetDisplay as default } from "@nteract/jupyter-widgets"; diff --git a/src/Contracts/ViewModels.ts b/src/Contracts/ViewModels.ts index cfeaf1b953..2df7182937 100644 --- a/src/Contracts/ViewModels.ts +++ b/src/Contracts/ViewModels.ts @@ -160,7 +160,6 @@ export interface Collection extends CollectionBase { onTableEntitiesClick(): void; onGraphDocumentsClick(): void; onMongoDBDocumentsClick(): void; - onSchemaAnalyzerClick(): void; openTab(): void; onSettingsClick: () => Promise; diff --git a/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.less b/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.less deleted file mode 100644 index 24188abf69..0000000000 --- a/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.less +++ /dev/null @@ -1,11 +0,0 @@ -.notebookViewerMetadataContainer { - margin: 0px 10px; - - .title, .decoration, .persona { - display: inline-block; - } - - .extras { - margin-top: 5px; - } -} \ No newline at end of file diff --git a/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.test.tsx b/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.test.tsx deleted file mode 100644 index 5235b1d288..0000000000 --- a/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.test.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { shallow } from "enzyme"; -import React from "react"; -import { NotebookMetadataComponent, NotebookMetadataComponentProps } from "./NotebookMetadataComponent"; - -describe("NotebookMetadataComponent", () => { - it("renders un-liked notebook", () => { - const props: NotebookMetadataComponentProps = { - data: { - id: "id", - name: "name", - description: "description", - author: "author", - thumbnailUrl: "thumbnailUrl", - created: "created", - gitSha: "gitSha", - tags: ["tag"], - isSample: false, - downloads: 0, - favorites: 0, - views: 0, - newCellId: undefined, - policyViolations: undefined, - pendingScanJobIds: undefined, - }, - isFavorite: false, - downloadButtonText: "Download", - onTagClick: undefined, - }; - - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - it("renders liked notebook", () => { - const props: NotebookMetadataComponentProps = { - data: { - id: "id", - name: "name", - description: "description", - author: "author", - thumbnailUrl: "thumbnailUrl", - created: "created", - gitSha: "gitSha", - tags: ["tag"], - isSample: false, - downloads: 0, - favorites: 0, - views: 0, - newCellId: undefined, - policyViolations: undefined, - pendingScanJobIds: undefined, - }, - isFavorite: true, - downloadButtonText: "Download", - onTagClick: undefined, - }; - - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - }); - - // TODO Add test for metadata display -}); diff --git a/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx b/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx deleted file mode 100644 index 248d8b5506..0000000000 --- a/src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Wrapper around Notebook metadata - */ -import { FontWeights, Icon, Link, Persona, PersonaSize, Stack, Text } from "@fluentui/react"; -import * as React from "react"; -import { IGalleryItem } from "../../../Juno/JunoClient"; -import * as FileSystemUtil from "../../Notebook/FileSystemUtil"; -import "./NotebookViewerComponent.less"; -import CosmosDBLogo from "../../../../images/CosmosDB-logo.svg"; - -export interface NotebookMetadataComponentProps { - data: IGalleryItem; - isFavorite: boolean; - downloadButtonText?: string; - onTagClick: (tag: string) => void; -} - -export class NotebookMetadataComponent extends React.Component { - public render(): JSX.Element { - const options: Intl.DateTimeFormatOptions = { - year: "numeric", - month: "short", - day: "numeric", - }; - - const dateString = new Date(this.props.data.created).toLocaleString("default", options); - - return ( - - - - - {FileSystemUtil.stripExtension(this.props.data.name, "ipynb")} - - - - - - {this.props.data.favorites} likes - - - - - - - {dateString} - - {this.props.data.views} - - - - {this.props.data.downloads} - - - - - {this.props.data.tags?.map((tag, index, array) => ( - - this.props.onTagClick(tag)}>{tag} - {index === array.length - 1 ? <> : ", "} - - ))} - - - - Description - - - {this.props.data.description} - - ); - } -} diff --git a/src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.less b/src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.less deleted file mode 100644 index d17581f2e8..0000000000 --- a/src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.less +++ /dev/null @@ -1,8 +0,0 @@ -@import "../../../../less/Common/Constants"; - -.notebookViewerContainer { - padding: 30px; - height: 100%; - width: 100%; - overflow-y: auto; -} diff --git a/src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx b/src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx deleted file mode 100644 index a6a9cd16d2..0000000000 --- a/src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Wrapper around Notebook Viewer Read only content - */ -import { Icon, Link, ProgressIndicator } from "@fluentui/react"; -import { Notebook } from "@nteract/commutable"; -import { createContentRef } from "@nteract/core"; -import * as React from "react"; -import { contents } from "rx-jupyter"; -import { getErrorMessage, getErrorStack, handleError } from "../../../Common/ErrorHandlingUtils"; -import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient"; -import { SessionStorageUtility } from "../../../Shared/StorageUtility"; -import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; -import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor"; -import Explorer from "../../Explorer"; -import { NotebookClientV2 } from "../../Notebook/NotebookClientV2"; -import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper"; -import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookReadOnlyRenderer"; -import { useNotebook } from "../../Notebook/useNotebook"; -import { NotebookMetadataComponent } from "./NotebookMetadataComponent"; -import "./NotebookViewerComponent.less"; - -export interface NotebookViewerComponentProps { - container?: Explorer; - junoClient?: JunoClient; - notebookUrl: string; - galleryItem?: IGalleryItem; - isFavorite?: boolean; - backNavigationText: string; - hideInputs?: boolean; - hidePrompts?: boolean; - onBackClick: () => void; - onTagClick: (tag: string) => void; -} - -interface NotebookViewerComponentState { - content: Notebook; - galleryItem?: IGalleryItem; - isFavorite?: boolean; - showProgressBar: boolean; -} - -export class NotebookViewerComponent extends React.Component< - NotebookViewerComponentProps, - NotebookViewerComponentState -> { - private clientManager: NotebookClientV2; - private notebookComponentBootstrapper: NotebookComponentBootstrapper; - - constructor(props: NotebookViewerComponentProps) { - super(props); - - this.clientManager = new NotebookClientV2({ - connectionInfo: { authToken: undefined, notebookServerEndpoint: undefined, forwardingId: undefined }, - databaseAccountName: undefined, - defaultExperience: "NotebookViewer", - isReadOnly: true, - cellEditorType: "codemirror", - autoSaveInterval: 365 * 24 * 3600 * 1000, // There is no way to turn off auto-save, set to 1 year - contentProvider: contents.JupyterContentProvider, // NotebookViewer only knows how to talk to Jupyter contents API - }); - - this.notebookComponentBootstrapper = new NotebookComponentBootstrapper({ - notebookClient: this.clientManager, - contentRef: createContentRef(), - }); - - this.state = { - content: undefined, - galleryItem: props.galleryItem, - isFavorite: props.isFavorite, - showProgressBar: true, - }; - - this.loadNotebookContent(); - } - - private async loadNotebookContent(): Promise { - const startKey = traceStart(Action.NotebooksGalleryViewNotebook, { - notebookUrl: this.props.notebookUrl, - notebookId: this.props.galleryItem?.id, - isSample: this.props.galleryItem?.isSample, - }); - - try { - const response = await fetch(this.props.notebookUrl); - if (!response.ok) { - this.setState({ showProgressBar: false }); - throw new Error(`Received HTTP ${response.status} while fetching ${this.props.notebookUrl}`); - } - - traceSuccess( - Action.NotebooksGalleryViewNotebook, - { - notebookUrl: this.props.notebookUrl, - notebookId: this.props.galleryItem?.id, - isSample: this.props.galleryItem?.isSample, - }, - startKey, - ); - - const notebook: Notebook = await response.json(); - this.notebookComponentBootstrapper.setContent("json", notebook); - this.setState({ content: notebook, showProgressBar: false }); - - if (this.props.galleryItem && !SessionStorageUtility.getEntry(this.props.galleryItem.id)) { - const response = await this.props.junoClient.increaseNotebookViews(this.props.galleryItem.id); - if (!response.data) { - throw new Error(`Received HTTP ${response.status} while increasing notebook views`); - } - this.setState({ galleryItem: response.data }); - SessionStorageUtility.setEntry(this.props.galleryItem?.id, "true"); - } - } catch (error) { - traceFailure( - Action.NotebooksGalleryViewNotebook, - { - notebookUrl: this.props.notebookUrl, - notebookId: this.props.galleryItem?.id, - isSample: this.props.galleryItem?.isSample, - error: getErrorMessage(error), - errorStack: getErrorStack(error), - }, - startKey, - ); - - this.setState({ showProgressBar: false }); - handleError(error, "NotebookViewerComponent/loadNotebookContent", "Failed to load notebook content"); - } - } - - public render(): JSX.Element { - return ( -
- {this.props.backNavigationText !== undefined ? ( - - {this.props.backNavigationText} - - ) : ( - <> - )} - - {this.state.galleryItem ? ( -
- -
- ) : ( - <> - )} - - {this.state.showProgressBar && } - - {this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, { - hideInputs: this.props.hideInputs, - hidePrompts: this.props.hidePrompts, - })} -
- ); - } - - public static getDerivedStateFromProps( - props: NotebookViewerComponentProps, - state: NotebookViewerComponentState, - ): Partial { - let galleryItem = props.galleryItem; - let isFavorite = props.isFavorite; - - if (state.galleryItem !== undefined) { - galleryItem = state.galleryItem; - } - - if (state.isFavorite !== undefined) { - isFavorite = state.isFavorite; - } - - return { - galleryItem, - isFavorite, - }; - } -} diff --git a/src/Explorer/Controls/NotebookViewer/__snapshots__/NotebookMetadataComponent.test.tsx.snap b/src/Explorer/Controls/NotebookViewer/__snapshots__/NotebookMetadataComponent.test.tsx.snap deleted file mode 100644 index a8b87926ca..0000000000 --- a/src/Explorer/Controls/NotebookViewer/__snapshots__/NotebookMetadataComponent.test.tsx.snap +++ /dev/null @@ -1,197 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`NotebookMetadataComponent renders liked notebook 1`] = ` - - - - - name - - - - - - - 0 - likes - - - - - - - Invalid Date - - - - - 0 - - - - 0 - - - - - - tag - - - - - Description - - - description - - -`; - -exports[`NotebookMetadataComponent renders un-liked notebook 1`] = ` - - - - - name - - - - - - - 0 - likes - - - - - - - Invalid Date - - - - - 0 - - - - 0 - - - - - - tag - - - - - Description - - - description - - -`; diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index 53ab9c6583..dd1d8153bd 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -130,7 +130,6 @@ exports[`SettingsComponent renders 1`] = ` "refreshNotebookList": [Function], "resourceTree": ResourceTreeAdapter { "container": [Circular], - "copyNotebook": [Function], "parameters": [Function], }, }, @@ -252,7 +251,6 @@ exports[`SettingsComponent renders 1`] = ` "refreshNotebookList": [Function], "resourceTree": ResourceTreeAdapter { "container": [Circular], - "copyNotebook": [Function], "parameters": [Function], }, }, @@ -479,7 +477,6 @@ exports[`SettingsComponent renders 1`] = ` "refreshNotebookList": [Function], "resourceTree": ResourceTreeAdapter { "container": [Circular], - "copyNotebook": [Function], "parameters": [Function], }, }, @@ -551,7 +548,6 @@ exports[`SettingsComponent renders 1`] = ` "refreshNotebookList": [Function], "resourceTree": ResourceTreeAdapter { "container": [Circular], - "copyNotebook": [Function], "parameters": [Function], }, } @@ -720,7 +716,6 @@ exports[`SettingsComponent renders 1`] = ` "refreshNotebookList": [Function], "resourceTree": ResourceTreeAdapter { "container": [Circular], - "copyNotebook": [Function], "parameters": [Function], }, }, @@ -792,7 +787,6 @@ exports[`SettingsComponent renders 1`] = ` "refreshNotebookList": [Function], "resourceTree": ResourceTreeAdapter { "container": [Circular], - "copyNotebook": [Function], "parameters": [Function], }, } diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 5a5d8c69d9..e554bbfaf5 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -24,7 +24,7 @@ import { AuthType } from "../AuthType"; import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer"; import * as Constants from "../Common/Constants"; import { Areas, ConnectionStatusType, HttpStatusCodes, Notebook } from "../Common/Constants"; -import { getErrorMessage, getErrorStack, handleError } from "../Common/ErrorHandlingUtils"; +import { getErrorMessage, getErrorStack } from "../Common/ErrorHandlingUtils"; import * as Logger from "../Common/Logger"; import { QueriesClient } from "../Common/QueriesClient"; import { readCollection } from "../Common/dataAccess/readCollection"; @@ -43,28 +43,20 @@ import { Action, ActionModifiers } from "../Shared/Telemetry/TelemetryConstants" import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor"; import { updateUserContext, userContext } from "../UserContext"; import { getCollectionName, getUploadName } from "../Utils/APITypeUtils"; -import { stringToBlob } from "../Utils/BlobUtils"; import { isCapabilityEnabled } from "../Utils/CapabilityUtils"; -import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils"; -import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils"; import { useSidePanel } from "../hooks/useSidePanel"; import { ReactTabKind, useTabs } from "../hooks/useTabs"; import "./ComponentRegisterer"; import { DialogProps, useDialog } from "./Controls/Dialog"; import { useCommandBar } from "./Menus/CommandBar/CommandBarComponentAdapter"; -import * as FileSystemUtil from "./Notebook/FileSystemUtil"; -import { NotebookContentItem, NotebookContentItemType } from "./Notebook/NotebookContentItem"; import type NotebookManager from "./Notebook/NotebookManager"; -import { NotebookUtil } from "./Notebook/NotebookUtil"; import { useNotebook } from "./Notebook/useNotebook"; import { AddCollectionPanel } from "./Panes/AddCollectionPanel/AddCollectionPanel"; import { CassandraAddCollectionPane } from "./Panes/CassandraAddCollectionPane/CassandraAddCollectionPane"; import { ExecuteSprocParamsPane } from "./Panes/ExecuteSprocParamsPane/ExecuteSprocParamsPane"; -import { StringInputPane } from "./Panes/StringInputPane/StringInputPane"; import { UploadItemsPane } from "./Panes/UploadItemsPane/UploadItemsPane"; import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient"; -import NotebookV2Tab, { NotebookTabOptions } from "./Tabs/NotebookV2Tab"; import TabsBase from "./Tabs/TabsBase"; import TerminalTab from "./Tabs/TerminalTab"; import Database from "./Tree/Database"; @@ -93,10 +85,6 @@ export default class Explorer { public notebookManager?: NotebookManager; private _isInitializingNotebooks: boolean; - private notebookToImport: { - name: string; - content: string; - }; private static readonly MaxNbDatabasesToAutoExpand = 5; public phoenixClient: PhoenixClient; @@ -652,313 +640,11 @@ export default class Explorer { } } - public uploadFile( - name: string, - content: string, - parent: NotebookContentItem, - isGithubTree?: boolean, - ): Promise { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to upload notebook, but notebook is not enabled"; - handleError(error, "Explorer/uploadFile"); - throw new Error(error); - } - - const promise = this.notebookManager?.notebookContentClient.uploadFileAsync(name, content, parent, isGithubTree); - promise - .then(() => this.resourceTree.triggerRender()) - .catch((reason) => useDialog.getState().showOkModalDialog("Unable to upload file", getErrorMessage(reason))); - return promise; - } - - public async importAndOpen(path: string): Promise { - const name = NotebookUtil.getName(path); - const item = NotebookUtil.createNotebookContentItem(name, path, "file"); - const parent = this.resourceTree.myNotebooksContentRoot; - - if (parent && parent.children && useNotebook.getState().isNotebookEnabled && this.notebookManager?.notebookClient) { - const existingItem = _.find(parent.children, (node) => node.name === name); - if (existingItem) { - return this.openNotebook(existingItem); - } - - const content = await this.readFile(item); - const uploadedItem = await this.uploadFile(name, content, parent); - return this.openNotebook(uploadedItem); - } - - return Promise.resolve(false); - } - - public async importAndOpenContent(name: string, content: string): Promise { - const parent = this.resourceTree.myNotebooksContentRoot; - - if (parent && parent.children && useNotebook.getState().isNotebookEnabled && this.notebookManager?.notebookClient) { - if (this.notebookToImport && this.notebookToImport.name === name && this.notebookToImport.content === content) { - this.notebookToImport = undefined; // we don't want to try opening this notebook again - } - - const existingItem = _.find(parent.children, (node) => node.name === name); - if (existingItem) { - return this.openNotebook(existingItem); - } - - const uploadedItem = await this.uploadFile(name, content, parent); - return this.openNotebook(uploadedItem); - } - - this.notebookToImport = { name, content }; // we'll try opening this notebook later on - return Promise.resolve(false); - } - - public copyNotebook(name: string, content: string): void { - this.notebookManager?.openCopyNotebookPane(name, content); - } - - /** - * Note: To keep it simple, this creates a disconnected NotebookContentItem that is not connected to the resource tree. - * Connecting it to a tree possibly requires the intermediate missing folders if the item is nested in a subfolder. - * Manually creating the missing folders between the root and its parent dir would break the UX: expanding a folder - * will not fetch its content if the children array exists (and has only one child which was manually created). - * Fetching the intermediate folders possibly involves a few chained async calls which isn't ideal. - * - * @param name - * @param path - */ - public createNotebookContentItemFile(name: string, path: string): NotebookContentItem { - return NotebookUtil.createNotebookContentItem(name, path, "file"); - } - - public async openNotebook(notebookContentItem: NotebookContentItem): Promise { - if (!notebookContentItem || !notebookContentItem.path) { - throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`); - } - if (notebookContentItem.type === NotebookContentItemType.Notebook && useNotebook.getState().isPhoenixNotebooks) { - await this.allocateContainer(); - } - - const notebookTabs = useTabs - .getState() - .getTabs( - ViewModels.CollectionTabKind.NotebookV2, - (tab) => - (tab as NotebookV2Tab).notebookPath && - FileSystemUtil.isPathEqual((tab as NotebookV2Tab).notebookPath(), notebookContentItem.path), - ) as NotebookV2Tab[]; - let notebookTab = notebookTabs && notebookTabs[0]; - - if (notebookTab) { - useTabs.getState().activateTab(notebookTab); - } else { - const options: NotebookTabOptions = { - account: userContext.databaseAccount, - tabKind: ViewModels.CollectionTabKind.NotebookV2, - node: undefined, - title: notebookContentItem.name, - tabPath: notebookContentItem.path, - collection: undefined, - masterKey: userContext.masterKey || "", - isTabsContentExpanded: ko.observable(true), - onLoadStartKey: undefined, - container: this, - notebookContentItem, - }; - - try { - const NotebookTabV2 = await import(/* webpackChunkName: "NotebookV2Tab" */ "./Tabs/NotebookV2Tab"); - notebookTab = new NotebookTabV2.default(options); - useTabs.getState().activateNewTab(notebookTab); - } catch (reason) { - console.error("Import NotebookV2Tab failed!", reason); - return false; - } - } - - return true; - } - - public renameNotebook(notebookFile: NotebookContentItem, isGithubTree?: boolean): void { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to rename notebook, but notebook is not enabled"; - handleError(error, "Explorer/renameNotebook"); - throw new Error(error); - } - - // Don't delete if tab is open to avoid accidental deletion - const openedNotebookTabs = useTabs - .getState() - .getTabs(ViewModels.CollectionTabKind.NotebookV2, (tab: NotebookV2Tab) => { - return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), notebookFile.path); - }); - if (openedNotebookTabs.length > 0) { - useDialog - .getState() - .showOkModalDialog("Unable to rename file", "This file is being edited. Please close the tab and try again."); - } else { - useSidePanel.getState().openSidePanel( - "Rename Notebook", - { - useSidePanel.getState().closeSidePanel(); - this.resourceTree.triggerRender(); - }} - inputLabel="Enter new notebook name" - submitButtonLabel="Rename" - errorMessage="Could not rename notebook" - inProgressMessage="Renaming notebook to" - successMessage="Renamed notebook to" - paneTitle="Rename Notebook" - defaultInput={FileSystemUtil.stripExtension(notebookFile.name, "ipynb")} - onSubmit={(notebookFile: NotebookContentItem, input: string): Promise => - this.notebookManager?.notebookContentClient.renameNotebook(notebookFile, input, isGithubTree) - } - notebookFile={notebookFile} - />, - ); - } - } - - public onCreateDirectory(parent: NotebookContentItem, isGithubTree?: boolean): void { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to create notebook directory, but notebook is not enabled"; - handleError(error, "Explorer/onCreateDirectory"); - throw new Error(error); - } - - useSidePanel.getState().openSidePanel( - "Create new directory", - { - useSidePanel.getState().closeSidePanel(); - this.resourceTree.triggerRender(); - }} - errorMessage="Could not create directory " - inProgressMessage="Creating directory " - successMessage="Created directory " - inputLabel="Enter new directory name" - paneTitle="Create new directory" - submitButtonLabel="Create" - defaultInput="" - onSubmit={(notebookFile: NotebookContentItem, input: string): Promise => - this.notebookManager?.notebookContentClient.createDirectory(notebookFile, input, isGithubTree) - } - notebookFile={parent} - />, - ); - } - - public readFile(notebookFile: NotebookContentItem): Promise { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to read file, but notebook is not enabled"; - handleError(error, "Explorer/downloadFile"); - throw new Error(error); - } - - return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path); - } - - public downloadFile(notebookFile: NotebookContentItem): Promise { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to download file, but notebook is not enabled"; - handleError(error, "Explorer/downloadFile"); - throw new Error(error); - } - - const clearMessage = NotificationConsoleUtils.logConsoleProgress(`Downloading ${notebookFile.path}`); - - return this.notebookManager?.notebookContentClient.readFileContent(notebookFile.path).then( - (content: string) => { - const blob = stringToBlob(content, "text/plain"); - if (navigator.msSaveBlob) { - // for IE and Edge - navigator.msSaveBlob(blob, notebookFile.name); - } else { - const downloadLink: HTMLAnchorElement = document.createElement("a"); - const url = URL.createObjectURL(blob); - downloadLink.href = url; - downloadLink.target = "_self"; - downloadLink.download = notebookFile.name; - - // for some reason, FF displays the download prompt only when - // the link is added to the dom so we add and remove it - document.body.appendChild(downloadLink); - downloadLink.click(); - downloadLink.remove(); - } - - clearMessage(); - }, - (error) => { - logConsoleError(`Could not download notebook ${getErrorMessage(error)}`); - clearMessage(); - }, - ); - } - - private refreshNotebookList = async (): Promise => { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - return; - } - - await this.resourceTree.initialize(); - await useNotebook.getState().initializeNotebooksTree(this.notebookManager); - - this.notebookManager?.refreshPinnedRepos(); - if (this.notebookToImport) { - this.importAndOpenContent(this.notebookToImport.name, this.notebookToImport.content); - } + private refreshNotebookList = (): Promise => { + // Notebook authoring and listing have been removed. + return Promise.resolve(); }; - public deleteNotebookFile(item: NotebookContentItem, isGithubTree?: boolean): Promise { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to delete notebook file, but notebook is not enabled"; - handleError(error, "Explorer/deleteNotebookFile"); - throw new Error(error); - } - - // Don't delete if tab is open to avoid accidental deletion - const openedNotebookTabs = useTabs - .getState() - .getTabs(ViewModels.CollectionTabKind.NotebookV2, (tab: NotebookV2Tab) => { - return tab.notebookPath && FileSystemUtil.isPathEqual(tab.notebookPath(), item.path); - }); - if (openedNotebookTabs.length > 0) { - useDialog - .getState() - .showOkModalDialog("Unable to delete file", "This file is being edited. Please close the tab and try again."); - return Promise.reject(); - } - - if (item.type === NotebookContentItemType.Directory && item.children && item.children.length > 0) { - useDialog.getState().openDialog({ - isModal: true, - title: "Unable to delete file", - subText: "Directory is not empty.", - primaryButtonText: "Close", - secondaryButtonText: undefined, - onPrimaryButtonClick: () => useDialog.getState().closeDialog(), - onSecondaryButtonClick: undefined, - }); - return Promise.reject(); - } - - return this.notebookManager?.notebookContentClient.deleteContentItem(item, isGithubTree).then( - () => logConsoleInfo(`Successfully deleted: ${item.path}`), - (reason) => logConsoleError(`Failed to delete "${item.path}": ${JSON.stringify(reason)}`), - ); - } - - // TODO: Delete this function when ResourceTreeAdapter is removed. - public async refreshContentItem(item: NotebookContentItem): Promise { - if (!useNotebook.getState().isNotebookEnabled || !this.notebookManager?.notebookContentClient) { - const error = "Attempt to refresh notebook list, but notebook is not enabled"; - handleError(error, "Explorer/refreshContentItem"); - return Promise.reject(new Error(error)); - } - - await this.notebookManager?.notebookContentClient.updateItemChildrenInPlace(item); - } - public openNotebookTerminal(kind: ViewModels.TerminalKind): void { this.connectToNotebookTerminal(kind); } @@ -1046,32 +732,6 @@ export default class Explorer { } } - public async handleOpenFileAction(path: string): Promise { - if (useNotebook.getState().isPhoenixNotebooks === undefined) { - await useNotebook.getState().getPhoenixStatus(); - } - if (useNotebook.getState().isPhoenixNotebooks) { - await this.allocateContainer(); - } - - // We still use github urls like https://github.com/Azure-Samples/cosmos-notebooks/blob/master/CSharp_quickstarts/GettingStarted_CSharp.ipynb - // when launching a notebook quickstart from Portal. In future we should just use gallery id and use Juno to fetch instead of directly - // calling GitHub. For now convert this url to a raw url and download content. - const gitHubInfo = fromContentUri(path); - if (gitHubInfo) { - const rawUrl = toRawContentUri(gitHubInfo.owner, gitHubInfo.repo, gitHubInfo.branch, gitHubInfo.path); - const response = await fetch(rawUrl); - if (response.status === Constants.HttpStatusCodes.OK) { - this.notebookToImport = { - name: NotebookUtil.getName(path), - content: await response.text(), - }; - - this.importAndOpenContent(this.notebookToImport.name, this.notebookToImport.content); - } - } - } - public openUploadItemsPane(onUpload?: (data: UploadDetailsRecord[]) => void): void { useSidePanel.getState().openSidePanel("Upload " + getUploadName(), ); } diff --git a/src/Explorer/MostRecentActivity/MostRecentActivity.ts b/src/Explorer/MostRecentActivity/MostRecentActivity.ts index 18847338ce..a5f6ab6d64 100644 --- a/src/Explorer/MostRecentActivity/MostRecentActivity.ts +++ b/src/Explorer/MostRecentActivity/MostRecentActivity.ts @@ -4,13 +4,6 @@ import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility"; export enum Type { OpenCollection = "OpenCollection", - OpenNotebook = "OpenNotebook", -} - -export interface OpenNotebookItem { - type: Type.OpenNotebook; - name: string; - path: string; } export interface OpenCollectionItem { @@ -19,7 +12,7 @@ export interface OpenCollectionItem { collectionId: string; } -type Item = OpenNotebookItem | OpenCollectionItem; +type Item = OpenCollectionItem; const itemsMaxNumber: number = 5; @@ -42,14 +35,14 @@ const migrateOldData = () => { componentName: AppStateComponentNames.MostRecentActivity, globalAccountName: accountName, }, - itemsMap[accountId].map((item) => { - if ((item.type as unknown as number) === 0) { - item.type = Type.OpenCollection; - } else if ((item.type as unknown as number) === 1) { - item.type = Type.OpenNotebook; - } - return item; - }), + itemsMap[accountId] + .filter((item) => (item.type as unknown as number) !== 1 && (item.type as string) !== "OpenNotebook") + .map((item) => { + if ((item.type as unknown as number) === 0) { + item.type = Type.OpenCollection; + } + return item; + }), ); } }); @@ -97,7 +90,7 @@ export const getItems = (accountName: string): Item[] => { componentName: AppStateComponentNames.MostRecentActivity, globalAccountName: accountName, }) as Item[]) || [] - ); + ).filter((item) => item.type in Type); }; export const collectionWasOpened = ( diff --git a/src/Explorer/Notebook/FileSystemUtil.ts b/src/Explorer/Notebook/FileSystemUtil.ts deleted file mode 100644 index 41bb06b8a2..0000000000 --- a/src/Explorer/Notebook/FileSystemUtil.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * file list returns path starting with ./blah - * rename returns simply blah. - * Both are the same. This method only handles these two cases and no other complicated paths that may contain .. - * ./ inside the path. - * TODO: this should go away when not using jupyter for file operations and use normalized paths. - * @param path1 - * @param path2 - */ -export function isPathEqual(path1: string, path2: string): boolean { - const normalize = (path: string): string => { - const dotSlash = "./"; - if (path.indexOf(dotSlash) === 0) { - path = path.substring(dotSlash.length); - } - return path; - }; - - return normalize(path1) === normalize(path2); -} - -/** - * Remove extension - * @param path - * @param extension Without the ".". e.g. "ipynb" (and not ".ipynb") - */ -export function stripExtension(path: string, extension: string): string { - const splitted = path.split("."); - if (splitted[splitted.length - 1] === extension) { - splitted.pop(); - } - return splitted.join("."); -} diff --git a/src/Explorer/Notebook/NTeractUtil.ts b/src/Explorer/Notebook/NTeractUtil.ts deleted file mode 100644 index 2fdd9ae336..0000000000 --- a/src/Explorer/Notebook/NTeractUtil.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NotebookContentRecordProps, selectors } from "@nteract/core"; - -/** - * A bunch of utilities to interact with nteract - */ -export function getCurrentCellType(content: NotebookContentRecordProps): "markdown" | "code" | "raw" | undefined { - if (!content) { - return undefined; - } - - const cellFocusedId = selectors.notebook.cellFocused(content.model); - if (cellFocusedId) { - const cell = selectors.notebook.cellById(content.model, { id: cellFocusedId }); - if (cell) { - return cell.cell_type; - } - } - - return undefined; -} diff --git a/src/Explorer/Notebook/NotebookClientV2.ts b/src/Explorer/Notebook/NotebookClientV2.ts deleted file mode 100644 index da902b568d..0000000000 --- a/src/Explorer/Notebook/NotebookClientV2.ts +++ /dev/null @@ -1,287 +0,0 @@ -// Manages all the redux logic for the notebook nteract code -// TODO: Merge with NotebookClient? -// Vendor modules -import { - actions, - AppState, - ContentRecord, - createHostRef, - createKernelspecsRef, - HostRecord, - HostRef, - IContentProvider, - KernelspecsRef, - makeAppRecord, - makeCommsRecord, - makeContentsRecord, - makeEditorsRecord, - makeEntitiesRecord, - makeHostsRecord, - makeJupyterHostRecord, - makeStateRecord, - makeTransformsRecord, -} from "@nteract/core"; -import { configOption, defineConfigOption } from "@nteract/mythic-configuration"; -import { Media } from "@nteract/outputs"; -import TransformVDOM from "@nteract/transform-vdom"; -import * as Immutable from "immutable"; -import { Notification } from "react-notification-system"; -import { AnyAction, Dispatch, Middleware, MiddlewareAPI, Store } from "redux"; -import * as Constants from "../../Common/Constants"; -import { NotebookWorkspaceConnectionInfo } from "../../Contracts/DataModels"; -import { Action } from "../../Shared/Telemetry/TelemetryConstants"; -import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; -import { userContext } from "../../UserContext"; -import configureStore from "./NotebookComponent/store"; -import { CdbAppState, makeCdbRecord } from "./NotebookComponent/types"; - -export type KernelSpecsDisplay = { name: string; displayName: string }; - -export interface NotebookClientV2Parameters { - connectionInfo: NotebookWorkspaceConnectionInfo; - databaseAccountName: string; - defaultExperience: string; - isReadOnly?: boolean; // if true: do not fetch kernelspecs automatically (this is for notebook viewer) - cellEditorType?: string; // override "codemirror" default, - autoSaveInterval?: number; // in ms - contentProvider: IContentProvider; -} - -export type ActionListener = (newValue: any) => void; - -export class NotebookClientV2 { - private store: Store; - private contentHostRef: HostRef; - private kernelSpecsForDisplay: KernelSpecsDisplay[] = []; - private kernelSpecsRef: KernelspecsRef; - - private databaseAccountName: string; - private defaultExperience: string; - - constructor(params: NotebookClientV2Parameters) { - this.databaseAccountName = params.databaseAccountName; - this.defaultExperience = params.defaultExperience; - - this.configureStore(params); - - this.kernelSpecsRef = createKernelspecsRef(); - - // Fetch kernel specs when opening new tab - if (!params.isReadOnly) { - this.getStore().dispatch( - actions.fetchKernelspecs({ - hostRef: this.contentHostRef, - kernelspecsRef: this.kernelSpecsRef, - }), - ); - } - } - - public getAvailableKernelSpecs(): KernelSpecsDisplay[] { - return this.kernelSpecsForDisplay; - } - - public getStore(): Store { - return this.store; - } - - /** - * Lazy init redux store as singleton. - * Don't move store in Explorer yet as it is typed to AppState which is nteract-specific - */ - private configureStore(params: NotebookClientV2Parameters): void { - const jupyterHostRecord = makeJupyterHostRecord({ - id: null, - type: "jupyter", - defaultKernelName: "python", - token: params.connectionInfo.authToken, - origin: params.connectionInfo.notebookServerEndpoint, - basePath: "/", // Jupyter server base URL - bookstoreEnabled: false, //!!config.bookstore.version, - showHeaderEditor: true, - crossDomain: true, - }); - - this.contentHostRef = createHostRef(); - const NullTransform = (): any => null; - const kernelspecsRef = createKernelspecsRef(); - - const initialState: CdbAppState = { - app: makeAppRecord({ - version: "dataExplorer 1.0", - host: jupyterHostRecord, - // TODO: tamitta: notificationSystem.addNotification was removed, do we need a substitute? - }), - core: makeStateRecord({ - currentKernelspecsRef: kernelspecsRef, - entities: makeEntitiesRecord({ - editors: makeEditorsRecord({}), - hosts: makeHostsRecord({ - byRef: Immutable.Map().set(this.contentHostRef, jupyterHostRecord), - }), - comms: makeCommsRecord(), - contents: makeContentsRecord({ - byRef: Immutable.Map(), - }), - transforms: userContext.features.sandboxNotebookOutputs - ? undefined - : makeTransformsRecord({ - displayOrder: Immutable.List([ - "application/vnd.jupyter.widget-view+json", - "application/vnd.vega.v5+json", - "application/vnd.vega.v4+json", - "application/vnd.vega.v3+json", - "application/vnd.vega.v2+json", - "application/vnd.vegalite.v3+json", - "application/vnd.vegalite.v2+json", - "application/vnd.vegalite.v1+json", - "application/geo+json", - "application/vnd.plotly.v1+json", - "text/vnd.plotly.v1+html", - "application/x-nteract-model-debug+json", - "application/vnd.dataresource+json", - "application/vdom.v1+json", - "application/json", - "application/javascript", - "text/html", - "text/markdown", - "text/latex", - "image/svg+xml", - "image/gif", - "image/png", - "image/jpeg", - "text/plain", - ]), - byId: Immutable.Map({ - "text/vnd.plotly.v1+html": NullTransform, - "application/vnd.plotly.v1+json": NullTransform, - "application/geo+json": NullTransform, - "application/x-nteract-model-debug+json": NullTransform, - "application/vnd.dataresource+json": NullTransform, - "application/vnd.jupyter.widget-view+json": NullTransform, - "application/vnd.vegalite.v1+json": NullTransform, - "application/vnd.vegalite.v2+json": NullTransform, - "application/vnd.vegalite.v3+json": NullTransform, - "application/vnd.vega.v2+json": NullTransform, - "application/vnd.vega.v3+json": NullTransform, - "application/vnd.vega.v4+json": NullTransform, - "application/vnd.vega.v5+json": NullTransform, - "application/vdom.v1+json": TransformVDOM, - "application/json": Media.Json, - "application/javascript": Media.JavaScript, - "text/html": Media.HTML, - "text/markdown": Media.Markdown, - "text/latex": Media.LaTeX, - "image/svg+xml": Media.SVG, - "image/gif": Media.Image, - "image/png": Media.Image, - "image/jpeg": Media.Image, - "text/plain": Media.Plain, - }), - }), - }), - }), - cdb: makeCdbRecord({ - databaseAccountName: params.databaseAccountName, - defaultExperience: params.defaultExperience, - }), - }; - - /** - * Intercept kernelspecs updates actions rather than subscribing to the store state changes (which - * is triggered for *any* state change). - * TODO: Use react-redux connect() to subscribe to state changes? - */ - const cacheKernelSpecsMiddleware: Middleware = - , S extends AppState>({ dispatch, getState }: MiddlewareAPI) => - (next: Dispatch) => - (action: A): A => { - switch (action.type) { - case actions.FETCH_KERNELSPECS_FULFILLED: { - const payload = (action as unknown as actions.FetchKernelspecsFulfilled).payload; - const defaultKernelName = payload.defaultKernelName; - this.kernelSpecsForDisplay = Object.values(payload.kernelspecs) - .filter((spec) => !spec.metadata?.hasOwnProperty("hidden")) - .map((spec) => ({ - name: spec.name, - displayName: spec.displayName, - })) - .sort((a: KernelSpecsDisplay, b: KernelSpecsDisplay) => { - // Put default at the top, otherwise lexicographically compare - if (a.displayName === defaultKernelName) { - return -1; - } else if (b.name === defaultKernelName) { - return 1; - } else { - return a.displayName.localeCompare(b.displayName); - } - }); - break; - } - } - - return next(action); - }; - - const traceErrorFct = (title: string, message: string) => { - TelemetryProcessor.traceFailure(Action.NotebookErrorNotification, { - dataExplorerArea: Constants.Areas.Notebook, - title, - message, - level: "Error", - }); - console.error(`${title}: ${message}`); - }; - - this.store = configureStore( - initialState, - params.contentProvider, - traceErrorFct, - [cacheKernelSpecsMiddleware], - !params.isReadOnly, - ); - - // Additional configuration - this.store.dispatch(configOption("editorType").action(params.cellEditorType ?? "codemirror")); - this.store.dispatch( - configOption("autoSaveInterval").action(params.autoSaveInterval ?? Constants.Notebook.autoSaveIntervalMs), - ); - this.store.dispatch(configOption("codeMirror.lineNumbers").action(true)); - - const readOnlyConfigOption = configOption("codeMirror.readOnly"); - const readOnlyValue = params.isReadOnly ? "nocursor" : undefined; - if (!readOnlyConfigOption) { - defineConfigOption({ - label: "Read-only", - key: "codeMirror.readOnly", - values: [ - { label: "Read-Only", value: "nocursor" }, - { label: "Not read-only", value: undefined }, - ], - defaultValue: readOnlyValue, - }); - } else { - this.store.dispatch(readOnlyConfigOption.action(readOnlyValue)); - } - } - - /** - * Handle notification coming from nteract - * The messages coming from nteract are not good enough to expose to user. - * We use the notificationsToUserEpic to control the messages from action. - * We log possible errors coming from nteract in telemetry and display in console - */ - private handleNotification = (msg: Notification): void => { - if (msg.level === "error") { - TelemetryProcessor.traceFailure(Action.NotebookErrorNotification, { - dataExplorerArea: Constants.Areas.Notebook, - title: msg.title, - message: msg.message, - level: msg.level, - }); - console.error(`${msg.title}: ${msg.message}`); - } else { - console.log(`${msg.title}: ${msg.message}`); - } - }; -} diff --git a/src/Explorer/Notebook/NotebookComponent/ContentProviders/InMemoryContentProvider.ts b/src/Explorer/Notebook/NotebookComponent/ContentProviders/InMemoryContentProvider.ts deleted file mode 100644 index 7841581b57..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/ContentProviders/InMemoryContentProvider.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { FileType, IContent, IContentProvider, ServerConfig } from "@nteract/core"; -import { Observable, of } from "rxjs"; -import { AjaxResponse } from "rxjs/ajax"; -import { HttpStatusCodes } from "../../../../Common/Constants"; -import { getErrorMessage } from "../../../../Common/ErrorHandlingUtils"; -import * as Logger from "../../../../Common/Logger"; - -export interface InMemoryContentProviderParams { - [path: string]: { readonly: boolean; content: IContent }; -} - -// Nteract relies on `errno` property to figure out the kind of failure -// That's why we need a custom wrapper around Error to include `errno` property -class InMemoryContentProviderError extends Error { - constructor( - error: string, - public errno: number = InMemoryContentProvider.SelfErrorCode, - ) { - super(error); - } -} - -export class InMemoryContentProvider implements IContentProvider { - public static readonly SelfErrorCode = 666; - - constructor(private params: InMemoryContentProviderParams) {} - - public remove(): Observable { - return this.errorResponse("Not implemented", "remove"); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public get(_config: ServerConfig, uri: string): Observable { - const item = this.params[uri]; - if (item) { - return of(this.createSuccessAjaxResponse(HttpStatusCodes.OK, item.content)); - } - - return this.errorResponse(`${uri} not found`, "get"); - } - - public update(): Observable { - return this.errorResponse("Not implemented", "update"); - } - - public create(): Observable { - return this.errorResponse("Not implemented", "create"); - } - - public save( - _config: ServerConfig, // eslint-disable-line @typescript-eslint/no-unused-vars - uri: string, - model: Partial>, - ): Observable { - const item = this.params[uri]; - if (item) { - if (!item.readonly) { - Object.assign(item.content, model); - } - return of(this.createSuccessAjaxResponse(HttpStatusCodes.OK, item.content)); - } - - return this.errorResponse(`${uri} not found`, "save"); - } - - public listCheckpoints(): Observable { - return this.errorResponse("Not implemented", "listCheckpoints"); - } - - public createCheckpoint(): Observable { - return this.errorResponse("Not implemented", "createCheckpoint"); - } - - public deleteCheckpoint(): Observable { - return this.errorResponse("Not implemented", "deleteCheckpoint"); - } - - public restoreFromCheckpoint(): Observable { - return this.errorResponse("Not implemented", "restoreFromCheckpoint"); - } - - private errorResponse(message: string, functionName: string): Observable { - const error = new InMemoryContentProviderError(message); - Logger.logError(error.message, `InMemoryContentProvider/${functionName}`, error.errno); - return of(this.createErrorAjaxResponse(error)); - } - - private createSuccessAjaxResponse(status: number, content: IContent): AjaxResponse { - return { - originalEvent: new Event("no-op"), - xhr: new XMLHttpRequest(), - request: {}, - status, - response: content ? content : undefined, - responseText: content ? JSON.stringify(content) : undefined, - responseType: "json", - }; - } - - private createErrorAjaxResponse(error: InMemoryContentProviderError): AjaxResponse { - return { - originalEvent: new Event("no-op"), - xhr: new XMLHttpRequest(), - request: {}, - status: error.errno, - response: error, - responseText: getErrorMessage(error), - responseType: "json", - }; - } -} diff --git a/src/Explorer/Notebook/NotebookComponent/ContentProviders/InMemoryContentProviderUtils.ts b/src/Explorer/Notebook/NotebookComponent/ContentProviders/InMemoryContentProviderUtils.ts deleted file mode 100644 index 1a4d6c1508..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/ContentProviders/InMemoryContentProviderUtils.ts +++ /dev/null @@ -1,15 +0,0 @@ -// memory:// -// Custom scheme for in memory content -export const ContentUriPattern = /memory:\/\/([^/]*)/; - -export function fromContentUri(contentUri: string): undefined | string { - const matches = contentUri.match(ContentUriPattern); - if (matches && matches.length > 1) { - return matches[1]; - } - return undefined; -} - -export function toContentUri(path: string): string { - return `memory://${path}`; -} diff --git a/src/Explorer/Notebook/NotebookComponent/InMemoryContentProviderUtils.test.ts b/src/Explorer/Notebook/NotebookComponent/InMemoryContentProviderUtils.test.ts deleted file mode 100644 index a5fd5df805..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/InMemoryContentProviderUtils.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as InMemoryContentProviderUtils from "./ContentProviders/InMemoryContentProviderUtils"; - -describe("fromContentUri", () => { - it("fromContentUri should return valid result", () => { - const contentUri = "memory://resource/path"; - const result = "resource"; - - expect(InMemoryContentProviderUtils.fromContentUri(contentUri)).toEqual(result); - }); - - it("fromContentUri should return undefined on invalid input", () => { - const contentUri = "invalid"; - - expect(InMemoryContentProviderUtils.fromContentUri(contentUri)).toEqual(undefined); - }); - - it("toContentUri should return valid result", () => { - const path = "resource/path"; - const result = "memory://resource/path"; - - expect(InMemoryContentProviderUtils.toContentUri(path)).toEqual(result); - }); -}); diff --git a/src/Explorer/Notebook/NotebookComponent/NotebookComponent.less b/src/Explorer/Notebook/NotebookComponent/NotebookComponent.less deleted file mode 100644 index 1bd303e1d5..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/NotebookComponent.less +++ /dev/null @@ -1,13 +0,0 @@ -.notebookComponentContainer { - text-transform: none; - line-height: 1.28581; - letter-spacing: 0; - font-size: 14px; - font-weight: 400; - color: #182026; - height: 100%; - - .hotKeys { - height: 100%; - } -} diff --git a/src/Explorer/Notebook/NotebookComponent/NotebookComponent.tsx b/src/Explorer/Notebook/NotebookComponent/NotebookComponent.tsx deleted file mode 100644 index 8bae8afb01..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/NotebookComponent.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { ContentRef } from "@nteract/core"; -import * as React from "react"; -import NotificationSystem, { System as ReactNotificationSystem } from "react-notification-system"; -import { default as Contents } from "./contents"; - -export class NotebookComponent extends React.Component<{ contentRef: ContentRef }> { - notificationSystem!: ReactNotificationSystem; - - shouldComponentUpdate(nextProps: { contentRef: ContentRef }): boolean { - return nextProps.contentRef !== this.props.contentRef; - } - - public render(): JSX.Element { - return ( -
- - { - this.notificationSystem = notificationSystem; - }} - /> -
- ); - } -} diff --git a/src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx b/src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx deleted file mode 100644 index ba7e181563..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx +++ /dev/null @@ -1,50 +0,0 @@ -// Vendor modules -import { actions, createContentRef, createKernelRef, selectors } from "@nteract/core"; -import * as React from "react"; -import { ReactAdapter } from "../../../Bindings/ReactBindingHandler"; -import { NotebookClientV2 } from "../NotebookClientV2"; -import { NotebookContentItem } from "../NotebookContentItem"; -import { NotebookComponentBootstrapper } from "./NotebookComponentBootstrapper"; -import VirtualCommandBarComponent from "./VirtualCommandBarComponent"; - -export interface NotebookComponentAdapterOptions { - contentItem: NotebookContentItem; - notebooksBasePath: string; - notebookClient: NotebookClientV2; - onUpdateKernelInfo: () => void; -} - -export class NotebookComponentAdapter extends NotebookComponentBootstrapper implements ReactAdapter { - private onUpdateKernelInfo: () => void; - public parameters: any; - - constructor(options: NotebookComponentAdapterOptions) { - super({ - contentRef: selectors.contentRefByFilepath(options.notebookClient.getStore().getState(), { - filepath: options.contentItem.path, - }), - notebookClient: options.notebookClient, - }); - - this.onUpdateKernelInfo = options.onUpdateKernelInfo; - - if (!this.contentRef) { - this.contentRef = createContentRef(); - const kernelRef = createKernelRef(); - - // Request fetching notebook content - this.getStore().dispatch( - actions.fetchContent({ - filepath: options.contentItem.path, - params: {}, - kernelRef, - contentRef: this.contentRef, - }), - ); - } - } - - protected renderExtraComponent = (): JSX.Element => { - return ; - }; -} diff --git a/src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx b/src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx deleted file mode 100644 index 7ced8cb203..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx +++ /dev/null @@ -1,379 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { Link } from "@fluentui/react"; -import { CellId, CellType, ImmutableNotebook } from "@nteract/commutable"; -// Vendor modules -import { actions, AppState, ContentRef, KernelRef, NotebookContentRecord, selectors } from "@nteract/core"; -import "@nteract/styles/editor-overrides.css"; -import "@nteract/styles/global-variables.css"; -import "codemirror/addon/hint/show-hint.css"; -import "codemirror/lib/codemirror.css"; -import { Notebook } from "Common/Constants"; -import { useDialog } from "Explorer/Controls/Dialog"; -import * as React from "react"; -import { Provider } from "react-redux"; -import "react-table/react-table.css"; -import { AnyAction, Store } from "redux"; -import { NotebookClientV2 } from "../NotebookClientV2"; -import { NotebookContentProviderType, NotebookUtil } from "../NotebookUtil"; -import * as NteractUtil from "../NTeractUtil"; -import * as CdbActions from "./actions"; -import { NotebookComponent } from "./NotebookComponent"; -import "./NotebookComponent.less"; - -export interface NotebookComponentBootstrapperOptions { - notebookClient: NotebookClientV2; - contentRef: ContentRef; -} - -interface IWrapModel { - name: string; - path: string; - last_modified: Date; - created: string; - content: unknown; - format: string; - mimetype: unknown; - size: number; - writeable: boolean; - type: string; -} - -export class NotebookComponentBootstrapper { - public contentRef: ContentRef; - protected renderExtraComponent: () => JSX.Element; - - private notebookClient: NotebookClientV2; - - constructor(options: NotebookComponentBootstrapperOptions) { - this.notebookClient = options.notebookClient; - this.contentRef = options.contentRef; - } - - protected static wrapModelIntoContent(name: string, path: string, content: unknown): IWrapModel { - return { - name, - path, - last_modified: new Date(), - created: "", - content, - format: "json", - mimetype: undefined, - size: 0, - writeable: false, - type: "notebook", - }; - } - - private renderDefaultNotebookComponent(props: any): JSX.Element { - return ( - <> - {this.renderExtraComponent && this.renderExtraComponent()} - {React.createElement<{ contentRef: ContentRef }>(NotebookComponent, { contentRef: this.contentRef, ...props })} - - ); - } - - public getContent(): { name: string; content: string | ImmutableNotebook } { - const record = this.getStore().getState().core.entities.contents.byRef.get(this.contentRef); - let content: string | ImmutableNotebook; - switch (record.model.type) { - case "notebook": - content = record.model.notebook; - break; - case "file": - content = record.model.text; - break; - default: - throw new Error(`Unsupported model type ${record.model.type}`); - } - - return { - name: NotebookUtil.getName(record.filepath), - content, - }; - } - - public getNotebookPath(): string { - return this.getStore().getState().core.entities.contents.byRef.get(this.contentRef)?.filepath; - } - - public setContent(name: string, content: unknown): void { - this.getStore().dispatch( - actions.fetchContentFulfilled({ - filepath: undefined, - model: NotebookComponentBootstrapper.wrapModelIntoContent(name, undefined, content), - kernelRef: undefined, - contentRef: this.contentRef, - }), - ); - } - - /** - * We can overload the notebook renderer here - * @param renderer - * @props additional props - */ - public renderComponent( - renderer?: any, // TODO FIX THIS React.ComponentClass<{ contentRef: ContentRef; isReadOnly?: boolean }>, - props?: any, - ): JSX.Element { - return ( - - {renderer - ? React.createElement<{ contentRef: ContentRef }>(renderer, { contentRef: this.contentRef, ...props }) - : this.renderDefaultNotebookComponent(props)} - - ); - } - - /* Notebook operations. See nteract/packages/connected-components/src/notebook-menu/index.tsx */ - public notebookSave(): void { - if ( - NotebookUtil.getContentProviderType(this.getNotebookPath()) === - NotebookContentProviderType.JupyterContentProviderType - ) { - useDialog.getState().showOkCancelModalDialog( - Notebook.saveNotebookModalTitle, - undefined, - "Save", - async () => { - this.getStore().dispatch( - actions.save({ - contentRef: this.contentRef, - }), - ); - }, - "Cancel", - undefined, - this.getSaveNotebookSubText(), - ); - } else { - this.getStore().dispatch( - actions.save({ - contentRef: this.contentRef, - }), - ); - } - } - - public notebookChangeKernel(kernelSpecName: string): void { - this.getStore().dispatch( - actions.changeKernelByName({ - contentRef: this.contentRef, - kernelSpecName, - oldKernelRef: this.getCurrentKernelRef(), - }), - ); - } - - public notebookRunAndAdvance(): void { - this.getStore().dispatch( - CdbActions.executeFocusedCellAndFocusNext({ - contentRef: this.contentRef, - }), - ); - } - - public notebookRunAll(): void { - this.getStore().dispatch( - actions.executeAllCells({ - contentRef: this.contentRef, - }), - ); - } - - public notebookInterruptKernel(): void { - this.getStore().dispatch( - actions.interruptKernel({ - kernelRef: this.getCurrentKernelRef(), - }), - ); - } - - public notebookKillKernel(): void { - this.getStore().dispatch( - actions.killKernel({ - restarting: false, - kernelRef: this.getCurrentKernelRef(), - }), - ); - } - - public notebookRestartKernel(): void { - this.getStore().dispatch( - actions.restartKernel({ - kernelRef: this.getCurrentKernelRef(), - contentRef: this.contentRef, - outputHandling: "None", - }), - ); - } - - public notebookClearAllOutputs(): void { - this.getStore().dispatch( - actions.clearAllOutputs({ - contentRef: this.contentRef, - }), - ); - } - - public notebookInsertBelow(): void { - this.getStore().dispatch( - actions.createCellBelow({ - cellType: "code", - contentRef: this.contentRef, - }), - ); - } - - public notebookChangeCellType(type: CellType): void { - const focusedCellId = this.getFocusedCellId(); - if (!focusedCellId) { - console.error("No focused cell"); - return; - } - - this.getStore().dispatch( - actions.changeCellType({ - id: focusedCellId, - contentRef: this.contentRef, - to: type, - }), - ); - } - - public notebokCopy(): void { - const focusedCellId = this.getFocusedCellId(); - if (!focusedCellId) { - console.error("No focused cell"); - return; - } - - this.getStore().dispatch( - actions.copyCell({ - id: focusedCellId, - contentRef: this.contentRef, - }), - ); - } - - public notebookCut(): void { - const focusedCellId = this.getFocusedCellId(); - if (!focusedCellId) { - console.error("No focused cell"); - return; - } - - this.getStore().dispatch( - actions.cutCell({ - id: focusedCellId, - contentRef: this.contentRef, - }), - ); - } - - public notebookPaste(): void { - this.getStore().dispatch( - actions.pasteCell({ - contentRef: this.contentRef, - }), - ); - } - - public notebookShutdown(): void { - const store = this.getStore(); - const kernelRef = this.getCurrentKernelRef(); - - if (kernelRef) { - store.dispatch( - actions.killKernel({ - restarting: false, - kernelRef, - }), - ); - } - - store.dispatch( - CdbActions.closeNotebook({ - contentRef: this.contentRef, - }), - ); - } - - public isContentDirty(): boolean { - const content = selectors.content(this.getStore().getState(), { contentRef: this.contentRef }); - if (!content) { - return false; - } - - // TODO Fix this typing here - return selectors.notebook.isDirty(content.model as never); - } - - public isNotebookUntrusted(): boolean { - return NotebookUtil.isNotebookUntrusted(this.getStore().getState(), this.contentRef); - } - - /** - * For display purposes, only return non-killed kernels - */ - public getCurrentKernelName(): string { - const currentKernel = selectors.kernel(this.getStore().getState(), { kernelRef: this.getCurrentKernelRef() }); - return (currentKernel && currentKernel.status !== "killed" && currentKernel.kernelSpecName) || undefined; - } - - // Returns the kernel name to select in the kernels dropdown - public getSelectedKernelName(): string { - const currentKernelName = this.getCurrentKernelName(); - if (!currentKernelName) { - // if there's no live kernel, try to get the kernel name from the notebook metadata - const content = selectors.content(this.getStore().getState(), { contentRef: this.contentRef }); - const notebook = content && (content as NotebookContentRecord).model.notebook; - if (!notebook) { - return undefined; - } - - const { kernelSpecName } = NotebookUtil.extractNewKernel("", notebook); - return kernelSpecName || undefined; - } - - return currentKernelName; - } - - public getActiveCellTypeStr(): string { - const content = selectors.content(this.getStore().getState(), { contentRef: this.contentRef }); - return NteractUtil.getCurrentCellType(content as NotebookContentRecord); - } - - private getCurrentKernelRef(): KernelRef { - return selectors.kernelRefByContentRef(this.getStore().getState(), { contentRef: this.contentRef }); - } - - private getFocusedCellId(): CellId { - const content = selectors.content(this.getStore().getState(), { contentRef: this.contentRef }); - if (!content) { - return undefined; - } - - return selectors.notebook.cellFocused((content as NotebookContentRecord).model); - } - - protected getStore(): Store { - return this.notebookClient.getStore(); - } - - private getSaveNotebookSubText(): JSX.Element { - return ( - <> -

{Notebook.saveNotebookModalContent}

-
-

- {Notebook.newNotebookModalContent2} - - {Notebook.learnMore} - -

- - ); - } -} diff --git a/src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts b/src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts deleted file mode 100644 index 36a5e1708d..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/NotebookContentProvider.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { FileType, IContent, IContentProvider, IGetParams, ServerConfig } from "@nteract/core"; -import { Observable } from "rxjs"; -import { AjaxResponse } from "rxjs/ajax"; -import { GitHubContentProvider } from "../../../GitHub/GitHubContentProvider"; -import * as GitHubUtils from "../../../Utils/GitHubUtils"; -import { InMemoryContentProvider } from "./ContentProviders/InMemoryContentProvider"; -import * as InMemoryContentProviderUtils from "./ContentProviders/InMemoryContentProviderUtils"; - -export class NotebookContentProvider implements IContentProvider { - constructor( - private inMemoryContentProvider: InMemoryContentProvider, - private gitHubContentProvider: GitHubContentProvider, - private jupyterContentProvider: IContentProvider, - ) {} - - public remove(serverConfig: ServerConfig, path: string): Observable { - return this.getContentProvider(path).remove(serverConfig, path); - } - - public get(serverConfig: ServerConfig, path: string, params: Partial): Observable { - return this.getContentProvider(path).get(serverConfig, path, params); - } - - public update( - serverConfig: ServerConfig, - path: string, - model: Partial>, - ): Observable { - return this.getContentProvider(path).update(serverConfig, path, model); - } - - public create( - serverConfig: ServerConfig, - path: string, - model: Partial> & { type: FT }, - ): Observable { - return this.getContentProvider(path).create(serverConfig, path, model); - } - - public save( - serverConfig: ServerConfig, - path: string, - model: Partial>, - ): Observable { - return this.getContentProvider(path).save(serverConfig, path, model); - } - - public listCheckpoints(serverConfig: ServerConfig, path: string): Observable { - return this.getContentProvider(path).listCheckpoints(serverConfig, path); - } - - public createCheckpoint(serverConfig: ServerConfig, path: string): Observable { - return this.getContentProvider(path).createCheckpoint(serverConfig, path); - } - - public deleteCheckpoint(serverConfig: ServerConfig, path: string, checkpointID: string): Observable { - return this.getContentProvider(path).deleteCheckpoint(serverConfig, path, checkpointID); - } - - public restoreFromCheckpoint( - serverConfig: ServerConfig, - path: string, - checkpointID: string, - ): Observable { - return this.getContentProvider(path).restoreFromCheckpoint(serverConfig, path, checkpointID); - } - - private getContentProvider(path: string): IContentProvider { - if (InMemoryContentProviderUtils.fromContentUri(path)) { - return this.inMemoryContentProvider; - } - - if (GitHubUtils.fromContentUri(path)) { - return this.gitHubContentProvider; - } - - return this.jupyterContentProvider; - } -} diff --git a/src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx b/src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx deleted file mode 100644 index 373d1ed4c3..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { AppState, ContentRef, selectors } from "@nteract/core"; -import * as React from "react"; -import { connect } from "react-redux"; -import { NotebookUtil } from "../NotebookUtil"; -import * as NteractUtil from "../NTeractUtil"; - -interface VirtualCommandBarComponentProps { - kernelSpecName: string; - kernelStatus: string; - currentCellType: string; - isNotebookUntrusted: boolean; - onRender: () => void; -} - -class VirtualCommandBarComponent extends React.Component { - constructor(props: VirtualCommandBarComponentProps) { - super(props); - this.state = {}; - } - - shouldComponentUpdate(nextProps: VirtualCommandBarComponentProps): boolean { - return ( - this.props.kernelStatus !== nextProps.kernelStatus || - this.props.kernelSpecName !== nextProps.kernelSpecName || - this.props.currentCellType !== nextProps.currentCellType || - this.props.isNotebookUntrusted !== nextProps.isNotebookUntrusted - ); - } - - public render(): JSX.Element { - this.props.onRender && this.props.onRender(); - return <>; - } -} - -interface InitialProps { - contentRef: ContentRef; - onRender: () => void; -} - -// Redux -const makeMapStateToProps = ( - initialState: AppState, - initialProps: InitialProps, -): ((state: AppState) => VirtualCommandBarComponentProps) => { - const { contentRef } = initialProps; - const mapStateToProps = (state: AppState) => { - const content = selectors.content(state, { contentRef }); - let kernelStatus, kernelSpecName, currentCellType; - - if (!content || content.type !== "notebook") { - return { - kernelStatus, - kernelSpecName, - currentCellType, - isNotebookUntrusted: NotebookUtil.isNotebookUntrusted(state, contentRef), - } as VirtualCommandBarComponentProps; - } - - const kernelRef = content.model.kernelRef; - let kernel; - if (kernelRef) { - kernel = selectors.kernel(state, { kernelRef }); - } - - if (kernel) { - kernelStatus = kernel.status; - kernelSpecName = kernel.kernelSpecName; - } - - currentCellType = NteractUtil.getCurrentCellType(content); - return { - kernelStatus, - kernelSpecName, - currentCellType, - isNotebookUntrusted: NotebookUtil.isNotebookUntrusted(state, contentRef), - onRender: initialProps.onRender, - }; - }; - - return mapStateToProps; -}; - -export default connect(makeMapStateToProps)(VirtualCommandBarComponent); diff --git a/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts b/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts deleted file mode 100644 index 312637fce2..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/__mocks__/rx-jupyter.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Observable, of } from "rxjs"; -import { AjaxRequest, AjaxResponse } from "rxjs/ajax"; - -let fakeAjaxResponse: AjaxResponse = { - originalEvent: (undefined), - xhr: new XMLHttpRequest(), - request: (null), - status: 200, - response: {}, - responseText: "", - responseType: "json", -}; -export const sessions = { - create: (): Observable => of(fakeAjaxResponse), - __setResponse: (response: AjaxResponse) => { - fakeAjaxResponse = response; - }, - createSpy: undefined as any, -}; diff --git a/src/Explorer/Notebook/NotebookComponent/actions.ts b/src/Explorer/Notebook/NotebookComponent/actions.ts deleted file mode 100644 index 31d8ee170b..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/actions.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { CellId } from "@nteract/commutable"; -import { ContentRef } from "@nteract/core"; -import { Action } from "../../../Shared/Telemetry/TelemetryConstants"; -import { SnapshotFragment, SnapshotRequest } from "./types"; - -export const CLOSE_NOTEBOOK = "CLOSE_NOTEBOOK"; -export interface CloseNotebookAction { - type: "CLOSE_NOTEBOOK"; - payload: { - contentRef: ContentRef; - }; -} - -export const closeNotebook = (payload: { contentRef: ContentRef }): CloseNotebookAction => { - return { - type: CLOSE_NOTEBOOK, - payload, - }; -}; - -export const EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT = "EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT"; -export interface ExecuteFocusedCellAndFocusNextAction { - type: "EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT"; - payload: { - contentRef: ContentRef; - }; -} - -export const executeFocusedCellAndFocusNext = (payload: { - contentRef: ContentRef; -}): ExecuteFocusedCellAndFocusNextAction => { - return { - type: EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT, - payload, - }; -}; - -export const UPDATE_KERNEL_RESTART_DELAY = "UPDATE_KERNEL_RESTART_DELAY"; -export interface UpdateKernelRestartDelayAction { - type: "UPDATE_KERNEL_RESTART_DELAY"; - payload: { - delayMs: number; - }; -} - -export const UpdateKernelRestartDelay = (payload: { delayMs: number }): UpdateKernelRestartDelayAction => { - return { - type: UPDATE_KERNEL_RESTART_DELAY, - payload, - }; -}; - -export const SET_HOVERED_CELL = "SET_HOVERED_CELL"; -export interface SetHoveredCellAction { - type: "SET_HOVERED_CELL"; - payload: { - cellId: CellId; - }; -} - -export const setHoveredCell = (payload: { cellId: CellId }): SetHoveredCellAction => { - return { - type: SET_HOVERED_CELL, - payload, - }; -}; - -export const TRACE_NOTEBOOK_TELEMETRY = "TRACE_NOTEBOOK_TELEMETRY"; -export interface TraceNotebookTelemetryAction { - type: "TRACE_NOTEBOOK_TELEMETRY"; - payload: { - action: Action; - actionModifier?: string; - data?: any; - }; -} - -export const traceNotebookTelemetry = (payload: { - action: Action; - actionModifier?: string; - data?: any; -}): TraceNotebookTelemetryAction => { - return { - type: TRACE_NOTEBOOK_TELEMETRY, - payload, - }; -}; - -export const STORE_CELL_OUTPUT_SNAPSHOT = "STORE_CELL_OUTPUT_SNAPSHOT"; -export interface StoreCellOutputSnapshotAction { - type: "STORE_CELL_OUTPUT_SNAPSHOT"; - payload: { - cellId: string; - snapshot: SnapshotFragment; - }; -} - -export const storeCellOutputSnapshot = (payload: { - cellId: string; - snapshot: SnapshotFragment; -}): StoreCellOutputSnapshotAction => { - return { - type: STORE_CELL_OUTPUT_SNAPSHOT, - payload, - }; -}; - -export const STORE_NOTEBOOK_SNAPSHOT = "STORE_NOTEBOOK_SNAPSHOT"; -export interface StoreNotebookSnapshotAction { - type: "STORE_NOTEBOOK_SNAPSHOT"; - payload: { - imageSrc: string; - requestId: string; - }; -} - -export const storeNotebookSnapshot = (payload: { - imageSrc: string; - requestId: string; -}): StoreNotebookSnapshotAction => { - return { - type: STORE_NOTEBOOK_SNAPSHOT, - payload, - }; -}; - -export const TAKE_NOTEBOOK_SNAPSHOT = "TAKE_NOTEBOOK_SNAPSHOT"; -export interface TakeNotebookSnapshotAction { - type: "TAKE_NOTEBOOK_SNAPSHOT"; - payload: SnapshotRequest; -} - -export const takeNotebookSnapshot = (payload: SnapshotRequest): TakeNotebookSnapshotAction => { - return { - type: TAKE_NOTEBOOK_SNAPSHOT, - payload, - }; -}; - -export const NOTEBOOK_SNAPSHOT_ERROR = "NOTEBOOK_SNAPSHOT_ERROR"; -export interface NotebookSnapshotErrorAction { - type: "NOTEBOOK_SNAPSHOT_ERROR"; - payload: { - error: string; - }; -} - -export const notebookSnapshotError = (payload: { error: string }): NotebookSnapshotErrorAction => { - return { - type: NOTEBOOK_SNAPSHOT_ERROR, - payload, - }; -}; diff --git a/src/Explorer/Notebook/NotebookComponent/contents/file/index.tsx b/src/Explorer/Notebook/NotebookComponent/contents/file/index.tsx deleted file mode 100644 index 4d305cd6cb..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/contents/file/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { AppState, ContentRef, selectors } from "@nteract/core"; -import * as React from "react"; -import { connect } from "react-redux"; -import styled from "styled-components"; -import NotebookRenderer from "../../../NotebookRenderer/NotebookRenderer"; -import * as TextFile from "./text-file"; - -const PaddedContainer = styled.div` - padding-left: var(--nt-spacing-l, 10px); - padding-top: var(--nt-spacing-m, 10px); - padding-right: var(--nt-spacing-m, 10px); -`; - -const JupyterExtensionContainer = styled.div` - display: flex; - flex-flow: column; - align-items: stretch; - height: 100%; -`; - -const JupyterExtensionChoiceContainer = styled.div` - flex: 1 1 auto; - overflow: auto; -`; - -interface FileProps { - type: "notebook" | "file" | "dummy"; - contentRef: ContentRef; - mimetype?: string | null; -} - -export class File extends React.PureComponent { - getChoice = () => { - let choice; - - // notebooks don't report a mimetype so we'll use the content.type - if (this.props.type === "notebook") { - choice = ; - } else if (this.props.type === "dummy") { - choice = undefined; - } else if (this.props.mimetype === undefined || !TextFile.handles(this.props.mimetype)) { - // This should not happen as we intercept mimetype upstream, but just in case - choice = ( - -
-            This file type cannot be rendered. Please download the file, in order to view it outside of Data Explorer.
-          
-
- ); - } else { - choice = ; - } - - return choice; - }; - - render(): JSX.Element { - const choice = this.getChoice(); - - // Right now we only handle one kind of editor - // If/when we support more modes, we would case them off here - return ( - - - {choice} - - - ); - } -} - -interface InitialProps { - contentRef: ContentRef; -} - -// Since the contentRef stays unique for the duration of this file, -// we use the makeMapStateToProps pattern to optimize re-render -const makeMapStateToProps = (initialState: AppState, initialProps: InitialProps) => { - const { contentRef } = initialProps; - - const mapStateToProps = (state: AppState) => { - const content = selectors.content(state, initialProps); - - return { - contentRef, - mimetype: content.mimetype, - type: content.type, - }; - }; - - return mapStateToProps; -}; - -export const ConnectedFile = connect(makeMapStateToProps)(File); - -export default ConnectedFile; diff --git a/src/Explorer/Notebook/NotebookComponent/contents/file/text-file.tsx b/src/Explorer/Notebook/NotebookComponent/contents/file/text-file.tsx deleted file mode 100644 index dec06c6472..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/contents/file/text-file.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { actions, AppState, ContentRef, selectors } from "@nteract/core"; -import { IMonacoProps as MonacoEditorProps } from "@nteract/monaco-editor"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import styled from "styled-components"; -import * as StringUtils from "../../../../../Utils/StringUtils"; - -const EditorContainer = styled.div` - position: absolute; - left: 0; - height: 100%; - width: 100%; - - .monaco { - height: 100%; - } -`; - -interface MappedStateProps { - mimetype: string; - text: string; - contentRef: ContentRef; - theme?: "light" | "dark"; -} - -interface MappedDispatchProps { - handleChange: (value: string) => void; -} - -type TextFileProps = MappedStateProps & MappedDispatchProps; - -interface TextFileState { - Editor: React.ComponentType; -} - -class EditorPlaceholder extends React.PureComponent { - render(): JSX.Element { - // TODO: Show a little blocky placeholder - return undefined; - } -} - -export class TextFile extends React.PureComponent { - constructor(props: TextFileProps) { - super(props); - this.state = { - Editor: EditorPlaceholder, - }; - } - - handleChange = (source: string) => { - this.props.handleChange(source); - }; - - componentDidMount(): void { - import(/* webpackChunkName: "monaco-editor" */ "@nteract/monaco-editor").then((module) => { - this.setState({ Editor: module.default }); - }); - } - - render(): JSX.Element { - const Editor = this.state.Editor; - - return ( - - - - ); - } -} - -interface InitialProps { - contentRef: ContentRef; -} - -function makeMapStateToTextFileProps( - initialState: AppState, - initialProps: InitialProps, -): (state: AppState) => MappedStateProps { - const { contentRef } = initialProps; - - const mapStateToTextFileProps = (state: AppState) => { - const content = selectors.content(state, { contentRef }); - if (!content || content.type !== "file") { - throw new Error("The text file component must have content"); - } - - const text = content.model ? content.model.text : ""; - - return { - contentRef, - mimetype: content.mimetype !== undefined ? content.mimetype : "text/plain", - text, - }; - }; - return mapStateToTextFileProps; -} - -const makeMapDispatchToTextFileProps = ( - initialDispatch: Dispatch, - initialProps: InitialProps, -): ((dispatch: Dispatch) => MappedDispatchProps) => { - const { contentRef } = initialProps; - - const mapDispatchToTextFileProps = (dispatch: Dispatch) => { - return { - handleChange: (source: string) => { - dispatch( - actions.updateFileText({ - contentRef, - text: source, - }), - ); - }, - }; - }; - return mapDispatchToTextFileProps; -}; - -const ConnectedTextFile = connect( - makeMapStateToTextFileProps, - makeMapDispatchToTextFileProps, -)(TextFile); - -export function handles(mimetype: string) { - return ( - !mimetype || - StringUtils.startsWith(mimetype, "text/") || - StringUtils.startsWith(mimetype, "application/javascript") || - StringUtils.startsWith(mimetype, "application/json") || - StringUtils.startsWith(mimetype, "application/x-ipynb+json") - ); -} - -export default ConnectedTextFile; diff --git a/src/Explorer/Notebook/NotebookComponent/contents/index.tsx b/src/Explorer/Notebook/NotebookComponent/contents/index.tsx deleted file mode 100644 index dd47fc3ec7..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/contents/index.tsx +++ /dev/null @@ -1,173 +0,0 @@ -// Vendor modules -import { CellType, ImmutableNotebook } from "@nteract/commutable"; -import { HeaderDataProps } from "@nteract/connected-components/lib/header-editor"; -import { - AppState, - ContentRef, - HostRecord, - selectors, - actions, - DirectoryContentRecordProps, - DummyContentRecordProps, - FileContentRecordProps, - NotebookContentRecordProps, -} from "@nteract/core"; -import { RecordOf } from "immutable"; -import * as React from "react"; -import { HotKeys, KeyMap } from "react-hotkeys"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; - -// Local modules -import { default as File } from "./file"; - -interface IContentsBaseProps { - contentRef: ContentRef; - error?: object | null; -} - -interface IStateToProps { - headerData?: HeaderDataProps; -} - -interface IDispatchFromProps { - handlers?: any; - onHeaderEditorChange?: (props: HeaderDataProps) => void; -} - -type ContentsProps = IContentsBaseProps & IStateToProps & IDispatchFromProps; - -class Contents extends React.PureComponent { - private keyMap: KeyMap = { - CHANGE_CELL_TYPE: ["ctrl+shift+y", "ctrl+shift+m", "meta+shift+y", "meta+shift+m"], - COPY_CELL: ["ctrl+shift+c", "meta+shift+c"], - CREATE_CELL_ABOVE: ["ctrl+shift+a", "meta+shift+a"], - CREATE_CELL_BELOW: ["ctrl+shift+b", "meta+shift+b"], - CUT_CELL: ["ctrl+shift+x", "meta+shift+x"], - DELETE_CELL: ["ctrl+shift+d", "meta+shift+d"], - EXECUTE_ALL_CELLS: ["alt+r a"], - INTERRUPT_KERNEL: ["alt+r i"], - KILL_KERNEL: ["alt+r k"], - OPEN: ["ctrl+o", "meta+o"], - PASTE_CELL: ["ctrl+shift+v"], - RESTART_KERNEL: ["alt+r r", "alt+r c", "alt+r a"], - SAVE: ["ctrl+s", "ctrl+shift+s", "meta+s", "meta+shift+s"], - }; - - render(): JSX.Element { - const { contentRef, handlers } = this.props; - - if (!contentRef) { - return <>; - } - - return ( - - - - - - ); - } -} - -const makeMapStateToProps: any = (initialState: AppState, initialProps: { contentRef: ContentRef }) => { - const host: HostRecord = initialState.app.host; - - if (host.type !== "jupyter") { - throw new Error("this component only works with jupyter apps"); - } - - const mapStateToProps = (state: AppState): Partial => { - const contentRef: ContentRef = initialProps.contentRef; - - if (!contentRef) { - throw new Error("cant display without a contentRef"); - } - - const content: - | RecordOf - | RecordOf - | RecordOf - | RecordOf - | undefined = selectors.content(state, { contentRef }); - - if (!content) { - return { - contentRef: undefined, - error: undefined, - headerData: undefined, - }; - } - - let headerData: HeaderDataProps = { - authors: [], - description: "", - tags: [], - title: "", - }; - - // If a notebook, we need to read in the headerData if available - if (content.type === "notebook") { - const notebook: ImmutableNotebook = content.model.get("notebook"); - const metadata: any = notebook.metadata.toJS(); - const { authors = [], description = "", tags = [], title = "" } = metadata; - - // Updates - headerData = Object.assign({}, headerData, { - authors, - description, - tags, - title, - }); - } - - return { - contentRef, - error: content.error, - headerData, - }; - }; - - return mapStateToProps; -}; - -const mapDispatchToProps = (dispatch: Dispatch, ownProps: ContentsProps): object => { - const { contentRef } = ownProps; - - return { - onHeaderEditorChange: (props: HeaderDataProps) => { - return dispatch( - actions.overwriteMetadataFields({ - ...props, - contentRef: ownProps.contentRef, - }), - ); - }, - // `HotKeys` handlers object - // see: https://github.com/greena13/react-hotkeys#defining-handlers - handlers: { - CHANGE_CELL_TYPE: (event: KeyboardEvent) => { - const type: CellType = event.key === "Y" ? "code" : "markdown"; - return dispatch(actions.changeCellType({ to: type, contentRef })); - }, - COPY_CELL: () => dispatch(actions.copyCell({ contentRef })), - CREATE_CELL_ABOVE: () => dispatch(actions.createCellAbove({ cellType: "code", contentRef })), - CREATE_CELL_BELOW: () => dispatch(actions.createCellBelow({ cellType: "code", contentRef })), - CUT_CELL: () => dispatch(actions.cutCell({ contentRef })), - DELETE_CELL: () => dispatch(actions.deleteCell({ contentRef })), - EXECUTE_ALL_CELLS: () => dispatch(actions.executeAllCells({ contentRef })), - INTERRUPT_KERNEL: () => dispatch(actions.interruptKernel({})), - KILL_KERNEL: () => dispatch(actions.killKernel({ restarting: false })), - PASTE_CELL: () => dispatch(actions.pasteCell({ contentRef })), - RESTART_KERNEL: (event: KeyboardEvent) => { - const outputHandling: "None" | "Clear All" | "Run All" = - event.key === "r" ? "None" : event.key === "a" ? "Run All" : "Clear All"; - return dispatch(actions.restartKernel({ outputHandling, contentRef })); - }, - SAVE: () => dispatch(actions.save({ contentRef })), - }, - }; -}; - -export default connect(makeMapStateToProps, mapDispatchToProps)(Contents); diff --git a/src/Explorer/Notebook/NotebookComponent/epics.test.ts b/src/Explorer/Notebook/NotebookComponent/epics.test.ts deleted file mode 100644 index 5916aff73c..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/epics.test.ts +++ /dev/null @@ -1,477 +0,0 @@ -import { makeNotebookRecord } from "@nteract/commutable"; -import { actions, state } from "@nteract/core"; -import * as Immutable from "immutable"; -import { StateObservable } from "redux-observable"; -import { Subject, of } from "rxjs"; -import { toArray } from "rxjs/operators"; -import * as sinon from "sinon"; - -import { NotebookUtil } from "../NotebookUtil"; -import { launchWebSocketKernelEpic } from "./epics"; -import { CdbAppState, makeCdbRecord } from "./types"; - -import { sessions } from "rx-jupyter"; - -describe("Extract kernel from notebook", () => { - it("Reads metadata kernelspec first", () => { - const fakeNotebook = makeNotebookRecord({ - metadata: Immutable.Map({ - kernelspec: { - display_name: "Python 3", - language: "python", - name: "python3", - }, - language_info: { - name: "python", - version: "3.7.3", - mimetype: "text/x-python", - codemirror_mode: { - name: "ipython", - version: 3, - }, - pygments_lexer: "ipython3", - nbconvert_exporter: "python", - file_extension: ".py", - }, - }), - }); - - const result = NotebookUtil.extractNewKernel("blah", fakeNotebook); - expect(result.kernelSpecName).toEqual("python3"); - }); - - it("Reads language info in metadata if kernelspec not present", () => { - const fakeNotebook = makeNotebookRecord({ - metadata: Immutable.Map({ - language_info: { - name: "python", - version: "3.7.3", - mimetype: "text/x-python", - codemirror_mode: { - name: "ipython", - version: 3, - }, - pygments_lexer: "ipython3", - nbconvert_exporter: "python", - file_extension: ".py", - }, - }), - }); - - const result = NotebookUtil.extractNewKernel("blah", fakeNotebook); - expect(result.kernelSpecName).toEqual("python"); - }); - - it("Returns nothing if no kernelspec nor language info is found in metadata", () => { - const fakeNotebook = makeNotebookRecord({ - metadata: Immutable.Map({ - blah: "this should be ignored", - }), - }); - - const result = NotebookUtil.extractNewKernel("blah", fakeNotebook); - expect(result.kernelSpecName).toEqual(undefined); - }); -}); - -const initialState = { - app: state.makeAppRecord({ - host: state.makeJupyterHostRecord({ - type: "jupyter", - token: "eh", - basePath: "/", - }), - }), - comms: state.makeCommsRecord(), - config: Immutable.Map({}), - core: state.makeStateRecord({ - kernelRef: "fake", - entities: state.makeEntitiesRecord({ - contents: state.makeContentsRecord({ - byRef: Immutable.Map({ - fakeContentRef: state.makeNotebookContentRecord(), - }), - }), - kernels: state.makeKernelsRecord({ - byRef: Immutable.Map({ - fake: state.makeRemoteKernelRecord({ - type: "websocket", - channels: new Subject(), - kernelSpecName: "fancy", - id: "0", - }), - }), - }), - }), - }), - cdb: makeCdbRecord({ - databaseAccountName: "dbAccountName", - defaultExperience: "defaultExperience", - }), -}; - -describe("launchWebSocketKernelEpic", () => { - const createSpy = sinon.spy(sessions, "create"); - - const contentRef = "fakeContentRef"; - const kernelRef = "fake"; - - it("launches remote kernels", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const cwd = "/"; - const kernelId = "123"; - const kernelSpecName = "kernelspecname"; - const sessionId = "sessionId"; - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName, - cwd, - selectNextKernel: true, - }), - ); - - (sessions as any).__setResponse({ - originalEvent: undefined, - xhr: new XMLHttpRequest(), - request: null, - status: 200, - response: { - id: sessionId, - path: "notebooks/Untitled7.ipynb", - name: "", - type: "notebook", - kernel: { - id: kernelId, - name: "kernel_launched", - last_activity: "2019-11-07T14:29:54.432454Z", - execution_state: "starting", - connections: 0, - }, - notebook: { - path: "notebooks/Untitled7.ipynb", - name: "", - }, - }, - responseText: null, - responseType: "json", - }); - - const responseActions = await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(responseActions).toMatchObject([ - { - type: actions.LAUNCH_KERNEL_SUCCESSFUL, - payload: { - contentRef, - kernelRef, - selectNextKernel: true, - kernel: { - info: null, - sessionId: sessionId, - type: "websocket", - kernelSpecName, - cwd, - id: kernelId, - }, - }, - }, - ]); - }); - - it("launches any kernel with no kernelspecs in the state", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const cwd = "/"; - const kernelId = "123"; - const kernelSpecName = "kernelspecname"; - const sessionId = "sessionId"; - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName, - cwd, - selectNextKernel: true, - }), - ); - - (sessions as any).__setResponse({ - originalEvent: undefined, - xhr: new XMLHttpRequest(), - request: null, - status: 200, - response: { - id: sessionId, - path: "notebooks/Untitled7.ipynb", - name: "", - type: "notebook", - kernel: { - id: kernelId, - name: "kernel_launched", - last_activity: "2019-11-07T14:29:54.432454Z", - execution_state: "starting", - connections: 0, - }, - notebook: { - path: "notebooks/Untitled7.ipynb", - name: "", - }, - }, - responseText: null, - responseType: "json", - }); - - await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(createSpy.lastCall.args[1]).toMatchObject({ - kernel: { - name: kernelSpecName, - }, - }); - }); - - it("launches no kernel if no kernel is specified and state has no kernelspecs", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const cwd = "/"; - const kernelId = "123"; - const kernelSpecName = "kernelspecname"; - const sessionId = "sessionId"; - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName: undefined, - cwd, - selectNextKernel: true, - }), - ); - - (sessions as any).__setResponse({ - originalEvent: undefined, - xhr: new XMLHttpRequest(), - request: null, - status: 200, - response: { - id: sessionId, - path: "notebooks/Untitled7.ipynb", - name: "", - type: "notebook", - kernel: { - id: kernelId, - name: "kernel_launched", - last_activity: "2019-11-07T14:29:54.432454Z", - execution_state: "starting", - connections: 0, - }, - notebook: { - path: "notebooks/Untitled7.ipynb", - name: "", - }, - }, - responseText: null, - responseType: "json", - }); - - const responseActions = await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(responseActions).toMatchObject([ - { - type: actions.LAUNCH_KERNEL_FAILED, - }, - ]); - }); - - it("emits an error if backend returns an error", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const cwd = "/"; - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName: undefined, - cwd, - selectNextKernel: true, - }), - ); - - (sessions as any).__setResponse({ - originalEvent: undefined, - xhr: new XMLHttpRequest(), - request: null, - status: 500, - response: null, - responseText: null, - responseType: "json", - }); - - const responseActions = await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(responseActions).toMatchObject([ - { - type: actions.LAUNCH_KERNEL_FAILED, - }, - ]); - }); - - describe("Choose correct kernelspecs to launch", () => { - beforeAll(() => { - // Initialize kernelspecs with 2 supported kernels - const createKernelSpecsRecord = (): Immutable.RecordOf => - state.makeKernelspecsRecord({ - byRef: Immutable.Map({ - kernelspecsref: state.makeKernelspecsByRefRecord({ - defaultKernelName: "kernel2", - byName: Immutable.Map({ - kernel1: state.makeKernelspec({ - name: "kernel1", - argv: Immutable.List([]), - env: Immutable.Map(), - interruptMode: "interruptMode1", - language: "language1", - displayName: "Kernel One", - metadata: Immutable.Map(), - resources: Immutable.Map(), - }), - kernel2: state.makeKernelspec({ - name: "kernel2", - argv: Immutable.List([]), - env: Immutable.Map(), - interruptMode: "interruptMode2", - language: "language2", - displayName: "Kernel Two", - metadata: Immutable.Map(), - resources: Immutable.Map(), - }), - }), - }), - }), - refs: Immutable.List(["kernelspecsref"]), - }); - initialState.core = initialState.core - .setIn(["entities", "kernelspecs"], createKernelSpecsRecord()) - .set("currentKernelspecsRef", "kernelspecsref"); - - // some fake response we don't care about - (sessions as any).__setResponse({ - originalEvent: undefined, - xhr: new XMLHttpRequest(), - request: null, - status: 200, - response: { - id: "sessionId", - path: "notebooks/Untitled7.ipynb", - name: "", - type: "notebook", - kernel: { - id: "kernelId", - name: "kernel_launched", - last_activity: "2019-11-07T14:29:54.432454Z", - execution_state: "starting", - connections: 0, - }, - notebook: { - path: "notebooks/Untitled7.ipynb", - name: "", - }, - }, - responseText: null, - responseType: "json", - }); - }); - - it("launches supported kernel in kernelspecs", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName: "kernel2", - cwd: "cwd", - selectNextKernel: true, - }), - ); - - await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - expect(createSpy.lastCall.args[1]).toMatchObject({ - kernel: { - name: "kernel2", - }, - }); - }); - - it("launches undefined kernel uses default kernel from kernelspecs", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName: undefined, - cwd: "cwd", - selectNextKernel: true, - }), - ); - - await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(createSpy.lastCall.args[1]).toMatchObject({ - kernel: { - name: "kernel2", - }, - }); - }); - - it("launches unsupported kernel uses default kernel from kernelspecs", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName: "This is an unknown kernelspec", - cwd: "cwd", - selectNextKernel: true, - }), - ); - - await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(createSpy.lastCall.args[1]).toMatchObject({ - kernel: { - name: "kernel2", - }, - }); - }); - - it("launches unsupported kernel uses kernelspecs with similar name", async () => { - const state$ = new StateObservable(new Subject() as any, initialState); - - const action$ = of( - actions.launchKernelByName({ - contentRef, - kernelRef, - kernelSpecName: "ernel1", - cwd: "cwd", - selectNextKernel: true, - }), - ); - - await launchWebSocketKernelEpic(action$, state$).pipe(toArray()).toPromise(); - - expect(createSpy.lastCall.args[1]).toMatchObject({ - kernel: { - name: "kernel1", - }, - }); - }); - }); -}); diff --git a/src/Explorer/Notebook/NotebookComponent/epics.ts b/src/Explorer/Notebook/NotebookComponent/epics.ts deleted file mode 100644 index 2571f7a61e..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/epics.ts +++ /dev/null @@ -1,1014 +0,0 @@ -import { - AppState, - ContentRef, - JupyterHostRecordProps, - ServerConfig as JupyterServerConfig, - KernelInfo, - KernelRef, - RemoteKernelProps, - actions, - castToSessionId, - createKernelRef, - selectors, -} from "@nteract/core"; -import { Channels, childOf, createMessage, message, ofMessageType } from "@nteract/messaging"; -import { defineConfigOption } from "@nteract/mythic-configuration"; -import { RecordOf } from "immutable"; -import { Action, AnyAction } from "redux"; -import { StateObservable, ofType } from "redux-observable"; -import { kernels, sessions } from "rx-jupyter"; -import { EMPTY, Observable, Observer, Subject, Subscriber, concat, from, interval, merge, of, timer } from "rxjs"; -import { - catchError, - concatMap, - delayWhen, - filter, - first, - map, - mergeMap, - retryWhen, - switchMap, - take, - tap, - timeout, -} from "rxjs/operators"; -import { webSocket } from "rxjs/webSocket"; -import * as Constants from "../../../Common/Constants"; -import { Areas } from "../../../Common/Constants"; -import { ActionModifiers, Action as TelemetryAction } from "../../../Shared/Telemetry/TelemetryConstants"; -import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; -import { logConsoleError, logConsoleInfo } from "../../../Utils/NotificationConsoleUtils"; -import { useTabs } from "../../../hooks/useTabs"; -import { useDialog } from "../../Controls/Dialog"; -import * as FileSystemUtil from "../FileSystemUtil"; -import * as cdbActions from "../NotebookComponent/actions"; -import { NotebookContentProviderType, NotebookUtil } from "../NotebookUtil"; -import * as CdbActions from "./actions"; -import * as TextFile from "./contents/file/text-file"; -import { CdbAppState, JupyterMessage } from "./types"; - -interface NotebookServiceConfig extends JupyterServerConfig { - userPuid?: string; -} - -const logFailureToTelemetry = (state: CdbAppState, title: string, error?: string) => { - TelemetryProcessor.traceFailure(TelemetryAction.NotebookErrorNotification, { - dataExplorerArea: Constants.Areas.Notebook, - title, - error, - }); -}; - -/** - * Automatically add a new cell if notebook is empty - * @param action$ - * @param state$ - */ -const addInitialCodeCellEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{} | actions.CreateCellBelow> => { - return action$.pipe( - ofType(actions.FETCH_CONTENT_FULFILLED) as any, - mergeMap((action: any) => { - const state = state$.value; - const contentRef = action.payload.contentRef; - const model = selectors.model(state, { contentRef }); - - // If it's not a notebook, we shouldn't be here - if (!model || model.type !== "notebook") { - return EMPTY; - } - - const cellOrder = selectors.notebook.cellOrder(model); - if (cellOrder.size === 0) { - return of( - actions.createCellAppend({ - cellType: "code", - contentRef, - }), - ); - } - - return EMPTY; - }), - ); -}; - -/** - * Updated kernels.formWebSocketURL so we pass the userId as a query param - */ -const formWebSocketURL = (serverConfig: NotebookServiceConfig, kernelId: string, sessionId?: string): string => { - const params = new URLSearchParams(); - if (sessionId) { - params.append("session_id", sessionId); - } - const q = params.toString(); - const suffix = q !== "" ? `?${q}` : ""; - const url = (serverConfig.endpoint.slice(0, -1) || "") + `api/kernels/${kernelId}/channels${suffix}`; - - return url.replace(/^http(s)?/, "ws$1"); -}; - -/** - * Override from kernel-lifecycle to improve code mirror language intellisense - * @param action$ - */ -export const acquireKernelInfoEpic = (action$: Observable) => { - return action$.pipe( - ofType(actions.LAUNCH_KERNEL_SUCCESSFUL) as any, - switchMap((action: actions.NewKernelAction) => { - const { - payload: { - kernel: { channels }, - kernelRef, - contentRef, - }, - } = action; - return acquireKernelInfo(channels, kernelRef, contentRef); - }), - ); -}; - -/** - * Send a kernel_info_request to the kernel and derive code mirror mode based on the language name. - */ -function acquireKernelInfo(channels: Channels, kernelRef: KernelRef, contentRef: ContentRef) { - const message = createMessage("kernel_info_request"); - - const obs = channels.pipe( - childOf(message), - ofMessageType("kernel_info_reply"), - first(), - mergeMap((msg) => { - const content = msg.content; - const languageInfo = (content && content.language_info) || { - name: "", - version: "", - mimetype: "", - file_extension: "", - pygments_lexer: "", - codemirror_mode: "", - nbconvert_exporter: "", - }; - - switch (languageInfo.name) { - case "csharp": - languageInfo.codemirror_mode = "text/x-csharp"; - break; - case "scala": - languageInfo.codemirror_mode = "text/x-scala"; - break; - } - - const info: KernelInfo = { - protocolVersion: content.protocol_version, - implementation: content.implementation, - implementationVersion: content.implementation_version, - banner: content.banner, - helpLinks: content.help_links, - languageName: languageInfo.name, - languageVersion: languageInfo.version, - mimetype: languageInfo.mimetype, - fileExtension: languageInfo.file_extension, - pygmentsLexer: languageInfo.pygments_lexer, - codemirrorMode: languageInfo.codemirror_mode, - nbconvertExporter: languageInfo.nbconvert_exporter, - }; - - let result; - if (!content.protocol_version.startsWith("5")) { - result = [ - actions.launchKernelFailed({ - kernelRef, - contentRef, - error: new Error( - "The kernel that you are attempting to launch does not support the latest version (v5) of the messaging protocol.", - ), - }), - ]; - } else { - result = [ - // The original action we were using - actions.setLanguageInfo({ - langInfo: msg.content.language_info, - kernelRef, - contentRef, - }), - actions.setKernelInfo({ - kernelRef, - info, - }), - ]; - } - - return of(...result); - }), - ); - - return Observable.create((observer: Observer) => { - const subscription = obs.subscribe(observer); - channels.next(message); - return subscription; - }); -} - -/** - * Updated kernels.connect so we use the updated formWebSocketURL to pass - * the userId as a query param - * @param serverConfig - * @param kernelID - * @param sessionID - */ -const connect = (serverConfig: NotebookServiceConfig, kernelID: string, sessionID?: string): Subject => { - const wsSubject = webSocket({ - url: formWebSocketURL(serverConfig, kernelID, sessionID), - protocol: serverConfig.wsProtocol, - }); - - // Create a subject that does some of the handling inline for the session - // and ensuring it's serialized - return Subject.create( - Subscriber.create( - (message?: JupyterMessage) => { - if (typeof message === "object") { - const sessionizedMessage = { - ...message, - header: { - session: sessionID, - token: serverConfig.token, - ...message.header, - }, - }; - wsSubject.next(sessionizedMessage); - } else { - console.error("Message must be an object, the app sent", message); - } - }, - (e: Error) => wsSubject.error(e), - () => wsSubject.complete(), - ), // Subscriber - // Subject.create takes a subscriber and an observable. We're only - // overriding the subscriber here so we pass the subject on as an - // observable as the second argument to Subject.create (since it's - // _also_ an observable) - wsSubject, - ); -}; - -/** - * Override launch websocket kernel epic: - * - pass the userId - * - if kernelspecs are present in the state: - * * verify that the kernel name matches one of the kernelspecs - * * else attempt to pick a kernel that matches the name from the kernelspecs list - * * else pick the default kernel specs - * @param action$ - * @param state$ - */ -export const launchWebSocketKernelEpic = ( - action$: Observable, - state$: StateObservable, -) => { - return action$.pipe( - ofType(actions.LAUNCH_KERNEL_BY_NAME) as any, - // Only accept jupyter servers for the host with this epic - filter(() => selectors.isCurrentHostJupyter(state$.value as never)), - switchMap((action: actions.LaunchKernelByNameAction) => { - const state = state$.value; - const host = selectors.currentHost(state); - if (host.type !== "jupyter") { - return EMPTY; - } - const serverConfig: NotebookServiceConfig = selectors.serverConfig(host); - - const { - payload: { kernelSpecName, cwd, kernelRef, contentRef }, - } = action; - - const content = selectors.content(state, { contentRef }); - if (!content || content.type !== "notebook") { - return EMPTY; - } - - let kernelSpecToLaunch = kernelSpecName; - - const currentKernelspecs = selectors.currentKernelspecs(state$.value); - - if (!kernelSpecToLaunch) { - if (currentKernelspecs) { - kernelSpecToLaunch = currentKernelspecs.defaultKernelName; - const msg = `No kernelspec name specified to launch, using default kernel: ${kernelSpecToLaunch}`; - logConsoleInfo(msg); - logFailureToTelemetry(state$.value, "Launching alternate kernel", msg); - } else { - return of( - actions.launchKernelFailed({ - error: new Error( - "Unable to launch kernel: no kernelspec name specified to launch and no default kernelspecs", - ), - contentRef, - }), - ); - } - } else if (currentKernelspecs && !currentKernelspecs.byName.get(kernelSpecToLaunch)) { - let msg = `Cannot launch kernelspec: "${kernelSpecToLaunch}" is not supported by the notebook server.`; - - // Find a kernel that best matches the kernel name - const match = currentKernelspecs.byName.find( - (value) => value.name.toLowerCase().indexOf(kernelSpecName.toLowerCase()) !== -1, - ); - if (match) { - kernelSpecToLaunch = match.name; - msg += ` Found kernel with similar name: ${kernelSpecToLaunch}`; - } else { - kernelSpecToLaunch = currentKernelspecs.defaultKernelName; - msg += ` Using default kernel: ${kernelSpecToLaunch}`; - } - logConsoleInfo(msg); - logFailureToTelemetry(state$.value, "Launching alternate kernel", msg); - } - - const sessionPayload = { - kernel: { - id: null, - name: kernelSpecToLaunch, - } as any, - name: "", - path: content.filepath.replace(/^\/+/g, ""), - type: "notebook", - }; - - return sessions.create(serverConfig, sessionPayload).pipe( - mergeMap((data) => { - const session = data.response; - - const sessionId = castToSessionId(session.id); - - const kernel: RemoteKernelProps = Object.assign({}, session.kernel, { - type: "websocket", - info: null, - sessionId, - cwd, - channels: connect(serverConfig, session.kernel.id, sessionId), - kernelSpecName: kernelSpecToLaunch, - }); - - kernel.channels.next(message({ msg_type: "kernel_info_request" })); - - return of( - actions.launchKernelSuccessful({ - kernel, - kernelRef, - contentRef: action.payload.contentRef, - selectNextKernel: true, - }), - ); - }), - catchError((error) => { - return of(actions.launchKernelFailed({ error })); - }), - ); - }), - ); -}; -/** - * Override the restartWebSocketKernelEpic from nteract since the /restart endpoint of our kernels has not - * been implmemented; - * TODO: Remove this epic once the /restart endpoint is implemented. - */ -export const restartWebSocketKernelEpic = ( - action$: Observable, - state$: StateObservable, -) => - action$.pipe( - ofType(actions.RESTART_KERNEL) as any, - concatMap((action: actions.RestartKernel) => { - const state = state$.value; - - const contentRef = action.payload.contentRef; - const kernelRef = selectors.kernelRefByContentRef(state, { contentRef }) || action.payload.kernelRef; - - /** - * If there is still no KernelRef, then throw an error. - */ - if (!kernelRef) { - return of( - actions.restartKernelFailed({ - error: new Error("Can't execute restart without kernel ref."), - kernelRef: "none provided", - contentRef, - }), - ); - } - - const host = selectors.currentHost(state); - if (host.type !== "jupyter") { - return of( - actions.restartKernelFailed({ - error: new Error("Can't restart a kernel with no Jupyter host."), - kernelRef, - contentRef, - }), - ); - } - - const kernel = selectors.kernel(state, { kernelRef }); - if (!kernel) { - return of( - actions.restartKernelFailed({ - error: new Error("Can't restart a kernel that does not exist."), - kernelRef, - contentRef, - }), - ); - } - - if (kernel.type !== "websocket" || !kernel.id) { - return of( - actions.restartKernelFailed({ - error: new Error("Can only restart Websocket kernels via API."), - kernelRef, - contentRef, - }), - ); - } - - const newKernelRef = createKernelRef(); - const kill = actions.killKernel({ - restarting: true, - kernelRef, - }); - - const relaunch = actions.launchKernelByName({ - kernelSpecName: kernel.kernelSpecName ?? undefined, - cwd: kernel.cwd, - kernelRef: newKernelRef, - selectNextKernel: true, - contentRef: contentRef, - }); - - const awaitKernelReady = action$.pipe( - ofType(actions.LAUNCH_KERNEL_SUCCESSFUL) as any, - filter((action: actions.NewKernelAction | actions.RestartKernel) => action.payload.kernelRef === newKernelRef), - take(1), - timeout(60000), // If kernel doesn't come up within this interval we will abort follow-on actions. - concatMap(() => { - const restartSuccess = actions.restartKernelSuccessful({ - kernelRef: newKernelRef, - contentRef, - }); - - if ((action as actions.RestartKernel).payload.outputHandling === "Run All") { - return of(restartSuccess, actions.executeAllCells({ contentRef })); - } else { - return of(restartSuccess); - } - }), - catchError((error) => { - return of( - actions.restartKernelFailed({ - error, - kernelRef: newKernelRef, - contentRef, - }), - ); - }), - ); - - return merge(of(kill, relaunch), awaitKernelReady); - }), - ); - -/** - * Override changeWebSocketKernelEpic: - * - to pass the userId when connecting to the kernel. - * - to override extractNewKernel() - * @param action$ - * @param state$ - */ -const changeWebSocketKernelEpic = ( - action$: Observable, - state$: StateObservable, -) => { - return action$.pipe( - ofType(actions.CHANGE_KERNEL_BY_NAME) as any, - // Only accept jupyter servers for the host with this epic - filter(() => selectors.isCurrentHostJupyter(state$.value as never)), - switchMap((action: actions.ChangeKernelByName) => { - const { - payload: { contentRef, oldKernelRef, kernelSpecName }, - } = action; - const state = state$.value; - const host = selectors.currentHost(state); - if (host.type !== "jupyter") { - return EMPTY; - } - - const serverConfig: NotebookServiceConfig = selectors.serverConfig(host); - if (!oldKernelRef) { - return EMPTY; - } - - const oldKernel = selectors.kernel(state, { kernelRef: oldKernelRef }); - if (!oldKernel || oldKernel.type !== "websocket") { - return EMPTY; - } - const { sessionId } = oldKernel; - if (!sessionId) { - return EMPTY; - } - - const content = selectors.content(state, { contentRef }); - if (!content || content.type !== "notebook") { - return EMPTY; - } - const { - filepath, - model: { notebook }, - } = content; - const { cwd } = NotebookUtil.extractNewKernel(filepath, notebook); - - const kernelRef = createKernelRef(); - return kernels.start(serverConfig, kernelSpecName, cwd).pipe( - mergeMap(({ response }) => { - const { id: kernelId } = response; - const sessionPayload = { - kernel: { id: kernelId, name: kernelSpecName }, - }; - // The sessions API will close down the old kernel for us if it is on this session - return sessions.update(serverConfig, sessionId, sessionPayload).pipe( - mergeMap(({ response: session }) => { - const kernel: RemoteKernelProps = Object.assign({}, session.kernel, { - type: "websocket", - sessionId, - cwd, - channels: connect(serverConfig, session.kernel.id, sessionId), - kernelSpecName, - }); - return of( - actions.launchKernelSuccessful({ - kernel, - kernelRef, - contentRef: action.payload.contentRef, - selectNextKernel: true, - }), - ); - }), - catchError((error) => of(actions.launchKernelFailed({ error, kernelRef, contentRef }))), - ); - }), - catchError((error) => of(actions.launchKernelFailed({ error, kernelRef, contentRef }))), - ); - }), - ); -}; - -/** - * Automatically focus on cell if only one cell - * @param action$ - * @param state$ - */ -const focusInitialCodeCellEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{} | actions.FocusCell> => { - return action$.pipe( - ofType(actions.CREATE_CELL_APPEND) as any, - mergeMap((action: any) => { - const state = state$.value; - const contentRef = action.payload.contentRef; - const model = selectors.model(state, { contentRef }); - - // If it's not a notebook, we shouldn't be here - if (!model || model.type !== "notebook") { - return EMPTY; - } - - const cellOrder = selectors.notebook.cellOrder(model); - if (cellOrder.size === 1) { - const id = cellOrder.get(0); - // Focus on the cell - return of( - actions.focusCell({ - id, - contentRef, - }), - ); - } - - return EMPTY; - }), - ); -}; - -/** - * Capture some actions to display to notification console - * TODO: Log these (or everything) in telemetry? - * @param action$ - * @param state$ - */ -const notificationsToUserEpic = (action$: Observable, state$: StateObservable): Observable<{}> => { - return action$.pipe( - ofType( - actions.RESTART_KERNEL_SUCCESSFUL, - actions.RESTART_KERNEL_FAILED, - actions.SAVE_FULFILLED, - actions.SAVE_FAILED, - actions.FETCH_CONTENT_FAILED, - ) as any, - mergeMap((action: any) => { - switch (action.type) { - case actions.RESTART_KERNEL_SUCCESSFUL: { - const title = "Kernel restart"; - const msg = "Kernel successfully restarted"; - logConsoleInfo(msg); - logFailureToTelemetry(state$.value, title, msg); - break; - } - case actions.RESTART_KERNEL_FAILED: - // TODO: enable once incorrect kernel restart failure signals are fixed - // NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, "Failed to restart kernel"); - break; - case actions.SAVE_FAILED: { - const title = "Save failure"; - const msg = `Failed to save notebook: ${(action as actions.SaveFailed).payload.error}`; - logConsoleError(msg); - logFailureToTelemetry(state$.value, title, msg); - break; - } - case actions.FETCH_CONTENT_FAILED: { - const typedAction: actions.FetchContentFailed = action; - const filepath = selectors.filepath(state$.value, { contentRef: typedAction.payload.contentRef }); - const title = "Fetching content failure"; - const msg = `Failed to fetch notebook content: ${filepath}, error: ${typedAction.payload.error}`; - logConsoleError(msg); - logFailureToTelemetry(state$.value, title, msg); - break; - } - } - return EMPTY; - }), - ); -}; - -/** - * Connection lost: ping server until back up and restart kernel - * @param action$ - * @param state$ - */ -const handleKernelConnectionLostEpic = ( - action$: Observable, - state$: StateObservable, -): Observable => { - return action$.pipe( - ofType(actions.UPDATE_DISPLAY_FAILED) as any, - mergeMap((action: any) => { - const state = state$.value; - - const msg = "Notebook was disconnected from kernel"; - logConsoleError(msg); - logFailureToTelemetry(state, "Error", "Kernel connection error"); - - const host = selectors.currentHost(state); - const serverConfig: NotebookServiceConfig = selectors.serverConfig(host as RecordOf); - - const contentRef = action.payload.contentRef; - const kernelRef = selectors.kernelRefByContentRef(state$.value, { contentRef }); - - const delayMs = state.cdb.kernelRestartDelayMs; - if (delayMs > Constants.Notebook.kernelRestartMaxDelayMs) { - const msg = - "Restarted kernel too many times. Please reload the page to enable Data Explorer to restart the kernel automatically."; - logConsoleError(msg); - logFailureToTelemetry(state, "Kernel restart error", msg); - - useDialog.getState().showOkModalDialog("kernel restarts", msg); - - return of(EMPTY); - } - - return concat( - of(CdbActions.UpdateKernelRestartDelay({ delayMs: delayMs * 1.5 })), - sessions.list(serverConfig).pipe( - delayWhen(() => timer(delayMs)), - map((xhr) => { - return actions.restartKernel({ - outputHandling: "None", - kernelRef, - contentRef, - }); - }), - retryWhen((errors) => { - return errors.pipe( - delayWhen(() => timer(Constants.Notebook.heartbeatDelayMs)), - tap(() => console.log("retrying...")), // TODO: Send new action? - ); - }), - ), - ); - }), - ); -}; - -/** - * Connection lost: clean up kernel ref - * @param action$ - * @param state$ - */ -export const cleanKernelOnConnectionLostEpic = ( - action$: Observable, - state$: StateObservable, -): Observable => { - return action$.pipe( - ofType(actions.UPDATE_DISPLAY_FAILED) as any, - switchMap((action: any) => { - const contentRef = action.payload.contentRef; - const kernelRef = selectors.kernelRefByContentRef(state$.value, { contentRef }); - return of( - actions.killKernelSuccessful({ - kernelRef, - }), - ); - }), - ); -}; - -/** - * Execute focused cell and focus next cell - * @param action$ - * @param state$ - */ -const executeFocusedCellAndFocusNextEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{} | actions.FocusNextCellEditor> => { - return action$.pipe( - ofType(CdbActions.EXECUTE_FOCUSED_CELL_AND_FOCUS_NEXT) as any, - mergeMap((action: any) => { - const contentRef = action.payload.contentRef; - return concat( - of(actions.executeFocusedCell({ contentRef })), - of(actions.focusNextCell({ contentRef, createCellIfUndefined: false })), - ); - }), - ); -}; - -/** - * Close tab if mimetype not supported - * @param action$ - * @param state$ - */ -const closeUnsupportedMimetypesEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{}> => { - return action$.pipe( - ofType(actions.FETCH_CONTENT_FULFILLED) as any, - mergeMap((action: any) => { - const mimetype = action.payload.model.mimetype; - if (!TextFile.handles(mimetype)) { - const filepath = action.payload.filepath; - // Close tab and show error message - useTabs - .getState() - .closeTabsByComparator( - (tab: any) => - (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath), - ); - const msg = `${filepath} cannot be rendered. Please download the file, in order to view it outside of Data Explorer.`; - useDialog.getState().showOkModalDialog("File cannot be rendered", msg); - logConsoleError(msg); - } - return EMPTY; - }), - ); -}; - -/** - * Close tab if file content fails to fetch not supported - * @param action$ - * @param state$ - */ -const closeContentFailedToFetchEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{}> => { - return action$.pipe( - ofType(actions.FETCH_CONTENT_FAILED) as any, - mergeMap((action: any) => { - const filepath = action.payload.filepath; - // Close tab and show error message - useTabs - .getState() - .closeTabsByComparator( - (tab: any) => (tab as any).notebookPath && FileSystemUtil.isPathEqual((tab as any).notebookPath(), filepath), - ); - const msg = `Failed to load file: ${filepath}.`; - useDialog.getState().showOkModalDialog("Failure to load", msg); - logConsoleError(msg); - return EMPTY; - }), - ); -}; - -const traceNotebookTelemetryEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{}> => { - return action$.pipe( - ofType(cdbActions.TRACE_NOTEBOOK_TELEMETRY) as any, - mergeMap((action: cdbActions.TraceNotebookTelemetryAction) => { - const state = state$.value; - - TelemetryProcessor.trace(action.payload.action, action.payload.actionModifier, { - ...action.payload.data, - databaseAccountName: state.cdb.databaseAccountName, - defaultExperience: state.cdb.defaultExperience, - dataExplorerArea: Areas.Notebook, - }); - return EMPTY; - }), - ); -}; - -/** - * Log notebook information to telemetry - * # raw cells, # markdown cells, # code cells, total - * @param action$ - * @param state$ - */ -const traceNotebookInfoEpic = ( - action$: Observable, - state$: StateObservable, -): Observable<{} | cdbActions.TraceNotebookTelemetryAction> => { - return action$.pipe( - ofType(actions.FETCH_CONTENT_FULFILLED) as any, - mergeMap((action: { payload: any }) => { - const state = state$.value; - const contentRef = action.payload.contentRef; - const model = selectors.model(state, { contentRef }); - - // If it's not a notebook, we shouldn't be here - if (!model || model.type !== "notebook") { - return EMPTY; - } - - const dataToLog = { - nbCodeCells: 0, - nbRawCells: 0, - nbMarkdownCells: 0, - nbCells: 0, - }; - for (let [id, cell] of selectors.notebook.cellMap(model)) { - switch (cell.cell_type) { - case "code": - dataToLog.nbCodeCells++; - break; - case "markdown": - dataToLog.nbMarkdownCells++; - break; - case "raw": - dataToLog.nbRawCells++; - break; - } - dataToLog.nbCells++; - } - - return of( - cdbActions.traceNotebookTelemetry({ - action: TelemetryAction.NotebooksFetched, - actionModifier: ActionModifiers.Mark, - data: dataToLog, - }), - ); - }), - ); -}; - -/** - * Log Kernel spec to start - * @param action$ - * @param state$ - */ -const traceNotebookKernelEpic = ( - action$: Observable, - state$: StateObservable, -): Observable => { - return action$.pipe( - ofType(actions.LAUNCH_KERNEL_SUCCESSFUL) as any, - mergeMap((action: { payload: any; type: string }) => { - return of( - cdbActions.traceNotebookTelemetry({ - action: TelemetryAction.NotebooksKernelSpecName, - actionModifier: ActionModifiers.Mark, - data: { - kernelSpecName: action.payload.kernel.name, - }, - }), - ); - }), - ); -}; - -const resetCellStatusOnExecuteCanceledEpic = ( - action$: Observable, - state$: StateObservable, -): Observable => { - return action$.pipe( - ofType(actions.EXECUTE_CANCELED) as any, - mergeMap((action: any) => { - const contentRef = action.payload.contentRef; - const model = state$.value.core.entities.contents.byRef.get(contentRef).model; - let busyCellIds: string[] = []; - - if (model.type === "notebook") { - const cellMap = model.transient.get("cellMap"); - if (cellMap) { - for (const entry of cellMap.toArray()) { - const cellId = entry[0]; - const status = model.transient.getIn(["cellMap", cellId, "status"]); - if (status === "busy") { - busyCellIds.push(cellId); - } - } - } - } - - return from(busyCellIds).pipe( - map((busyCellId) => { - return actions.updateCellStatus({ id: busyCellId, contentRef, status: undefined }); - }), - ); - }), - ); -}; - -const { selector: autoSaveInterval } = defineConfigOption({ - key: "autoSaveInterval", - label: "Auto-save interval", - defaultValue: 120_000, -}); - -/** - * Override autoSaveCurrentContentEpic to disable auto save for notebooks under temporary workspace. - * @param action$ - */ -export function autoSaveCurrentContentEpic( - action$: Observable, - state$: StateObservable, -): Observable { - return state$.pipe( - map((state) => autoSaveInterval(state)) as any, - switchMap((time) => interval(time as number)) as any, - mergeMap(() => { - const state = state$.value; - return from( - selectors - .contentByRef(state) - .filter( - /* - * Only save contents that are files or notebooks with - * a filepath already set. - */ - (content) => (content.type === "file" || content.type === "notebook") && content.filepath !== "", - ) - .keys(), - ); - }) as any, - filter((contentRef: ContentRef) => { - const model = selectors.model(state$.value, { contentRef }); - const content = selectors.content(state$.value, { contentRef }); - if ( - model && - model.type === "notebook" && - NotebookUtil.getContentProviderType(content.filepath) !== NotebookContentProviderType.JupyterContentProviderType - ) { - return selectors.notebook.isDirty(model as never); - } - return false; - }) as any, - map((contentRef: ContentRef) => actions.save({ contentRef })) as any, - ) as any; -} - -export const allEpics = [ - addInitialCodeCellEpic, - focusInitialCodeCellEpic, - notificationsToUserEpic, - launchWebSocketKernelEpic, - changeWebSocketKernelEpic, - acquireKernelInfoEpic, - handleKernelConnectionLostEpic, - cleanKernelOnConnectionLostEpic, - executeFocusedCellAndFocusNextEpic, - closeUnsupportedMimetypesEpic, - closeContentFailedToFetchEpic, - restartWebSocketKernelEpic, - traceNotebookTelemetryEpic, - traceNotebookInfoEpic, - traceNotebookKernelEpic, - resetCellStatusOnExecuteCanceledEpic, - autoSaveCurrentContentEpic, -]; diff --git a/src/Explorer/Notebook/NotebookComponent/loadTransform.ts b/src/Explorer/Notebook/NotebookComponent/loadTransform.ts deleted file mode 100644 index 7dcc4219f8..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/loadTransform.ts +++ /dev/null @@ -1,35 +0,0 @@ -// This replicates transform loading from: -// https://github.com/nteract/nteract/blob/master/applications/jupyter-extension/nteract_on_jupyter/app/contents/notebook.tsx - -export default (props: { addTransform: (component: any) => void }) => { - import(/* webpackChunkName: "plotly" */ "@nteract/transform-plotly").then((module) => { - props.addTransform(module.default); - props.addTransform(module.PlotlyNullTransform); - }); - - import(/* webpackChunkName: "tabular-dataresource" */ "@nteract/data-explorer").then((module) => { - props.addTransform(module.default); - }); - - import(/* webpackChunkName: "jupyter-widgets" */ "@nteract/jupyter-widgets").then((module) => { - props.addTransform(module.WidgetDisplay); - }); - - import("@nteract/transform-model-debug").then((module) => { - props.addTransform(module.default); - }); - - import(/* webpackChunkName: "vega-transform" */ "@nteract/transform-vega").then((module) => { - props.addTransform(module.VegaLite1); - props.addTransform(module.VegaLite2); - props.addTransform(module.VegaLite3); - props.addTransform(module.VegaLite4); - props.addTransform(module.Vega2); - props.addTransform(module.Vega3); - props.addTransform(module.Vega4); - props.addTransform(module.Vega5); - }); - - // TODO: The geojson transform will likely need some work because of the basemap URL(s) - // import GeoJSONTransform from "@nteract/transform-geojson"; -}; diff --git a/src/Explorer/Notebook/NotebookComponent/reducers.ts b/src/Explorer/Notebook/NotebookComponent/reducers.ts deleted file mode 100644 index a1259a4a54..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/reducers.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { actions, CoreRecord, reducers as nteractReducers } from "@nteract/core"; -import { Action } from "redux"; -import * as cdbActions from "./actions"; -import { CdbRecord } from "./types"; - -export const coreReducer = (state: CoreRecord, action: Action) => { - let typedAction; - switch (action.type) { - case cdbActions.CLOSE_NOTEBOOK: { - typedAction = action as cdbActions.CloseNotebookAction; - return state.setIn( - ["entities", "contents", "byRef"], - state.entities.contents.byRef.delete(typedAction.payload.contentRef), - ); - } - case actions.CHANGE_KERNEL_BY_NAME: { - // Update content metadata - typedAction = action as actions.ChangeKernelByName; - const kernelSpecName = typedAction.payload.kernelSpecName; - - if (!state.currentKernelspecsRef) { - return state; - } - - const currentKernelspecs = state.entities.kernelspecs.byRef.get(state.currentKernelspecsRef); - if (!currentKernelspecs) { - return state; - } - - // Find kernelspecs by name - const kernelspecs = currentKernelspecs.byName.get(kernelSpecName); - if (!kernelspecs) { - return state; - } - - const path = [ - "entities", - "contents", - "byRef", - typedAction.payload.contentRef, - "model", - "notebook", - "metadata", - "kernelspec", - ]; - // Update metadata - return state - .setIn(path.concat("name"), kernelspecs.name) - .setIn(path.concat("displayName"), kernelspecs.displayName) - .setIn(path.concat("language"), kernelspecs.language); - } - default: - return nteractReducers.core(state as any, action as any); - } -}; - -export const cdbReducer = (state: CdbRecord, action: Action) => { - if (!state) { - return null; - } - - switch (action.type) { - case cdbActions.UPDATE_KERNEL_RESTART_DELAY: { - const typedAction = action as cdbActions.UpdateKernelRestartDelayAction; - return state.set("kernelRestartDelayMs", typedAction.payload.delayMs); - } - - case cdbActions.SET_HOVERED_CELL: { - const typedAction = action as cdbActions.SetHoveredCellAction; - return state.set("hoveredCellId", typedAction.payload.cellId); - } - - case cdbActions.STORE_CELL_OUTPUT_SNAPSHOT: { - const typedAction = action as cdbActions.StoreCellOutputSnapshotAction; - state.cellOutputSnapshots.set(typedAction.payload.cellId, typedAction.payload.snapshot); - // TODO Simpler datastructure to instantiate new Map? - return state.set("cellOutputSnapshots", new Map(state.cellOutputSnapshots)); - } - - case cdbActions.STORE_NOTEBOOK_SNAPSHOT: { - const typedAction = action as cdbActions.StoreNotebookSnapshotAction; - // Clear pending request - return state.set("notebookSnapshot", typedAction.payload).set("pendingSnapshotRequest", undefined); - } - - case cdbActions.TAKE_NOTEBOOK_SNAPSHOT: { - const typedAction = action as cdbActions.TakeNotebookSnapshotAction; - // Clear previous snapshots - return state - .set("cellOutputSnapshots", new Map()) - .set("notebookSnapshot", undefined) - .set("notebookSnapshotError", undefined) - .set("pendingSnapshotRequest", typedAction.payload); - } - - case cdbActions.NOTEBOOK_SNAPSHOT_ERROR: { - const typedAction = action as cdbActions.NotebookSnapshotErrorAction; - return state.set("notebookSnapshotError", typedAction.payload.error); - } - } - return state; -}; diff --git a/src/Explorer/Notebook/NotebookComponent/store.test.ts b/src/Explorer/Notebook/NotebookComponent/store.test.ts deleted file mode 100644 index d0a0ad82d1..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/store.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getCoreEpics } from "./store"; -import { epics } from "@nteract/core"; - -describe("configure redux store", () => { - it("configures store with correct epic if based on autoStartKernelOnNotebookOpen", () => { - // For now, assume launchKernelWhenNotebookSetEpic is the last epic - let filteredEpics = getCoreEpics(true); - expect(filteredEpics.pop()).toEqual(epics.launchKernelWhenNotebookSetEpic); - - filteredEpics = getCoreEpics(false); - expect(filteredEpics.pop()).not.toEqual(epics.launchKernelWhenNotebookSetEpic); - }); -}); diff --git a/src/Explorer/Notebook/NotebookComponent/store.ts b/src/Explorer/Notebook/NotebookComponent/store.ts deleted file mode 100644 index 47fa91c029..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/store.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { AppState, epics as coreEpics, IContentProvider, reducers } from "@nteract/core"; -import { configuration } from "@nteract/mythic-configuration"; -import { makeConfigureStore } from "@nteract/myths"; -import { stringifyError } from "Common/stringifyError"; -import { AnyAction, compose, Dispatch, Middleware, MiddlewareAPI, Store } from "redux"; -import { Epic } from "redux-observable"; -import { Observable } from "rxjs"; -import { catchError } from "rxjs/operators"; -import { allEpics } from "./epics"; -import { cdbReducer, coreReducer } from "./reducers"; -import { CdbAppState } from "./types"; - -const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; - -export default function configureStore( - initialState: Partial, - contentProvider: IContentProvider, - onTraceFailure: (title: string, message: string) => void, - customMiddlewares?: Middleware<{}, any, Dispatch>[], - autoStartKernelOnNotebookOpen?: boolean, -): Store { - /** - * Catches errors in reducers - */ - const catchErrorMiddleware: Middleware = - , S extends AppState>({ dispatch, getState }: MiddlewareAPI) => - (next: Dispatch) => -
(action: A): any => { - try { - next(action); - } catch (error) { - traceFailure("Reducer failure", error); - } - }; - - const protect = (epic: Epic) => { - return (action$: Observable, state$: any, dependencies: any) => - epic(action$ as any, state$, dependencies).pipe( - catchError((error, caught) => { - traceFailure("Epic failure", error); - return caught; - }) as any, - ); - }; - - const traceFailure = (title: string, error: any) => { - if (error instanceof Error) { - onTraceFailure(title, `${error.message} ${stringifyError(error.stack)}`); - console.error(error); - } else { - onTraceFailure(title, error.message); - } - }; - - const protectEpics = (epics: Epic[]): Epic[] => { - return epics.map((epic) => protect(epic)) as any; - }; - - const filteredCoreEpics = getCoreEpics(autoStartKernelOnNotebookOpen); - - const mythConfigureStore = makeConfigureStore()({ - packages: [configuration], - reducers: { - app: reducers.app, - core: coreReducer as any, - cdb: cdbReducer, - }, - epics: protectEpics([...filteredCoreEpics, ...allEpics] as any), - epicDependencies: { contentProvider }, - epicMiddleware: customMiddlewares.concat(catchErrorMiddleware), - enhancer: composeEnhancers, - }); - - const store = mythConfigureStore(initialState as any); - - // TODO Fix typing issue here: createStore() output type doesn't quite match AppState - // return store as Store; - return store as any; -} - -export const getCoreEpics = (autoStartKernelOnNotebookOpen: boolean): Epic[] => { - // This list needs to be consistent and in sync with core.allEpics until we figure - // out how to safely filter out the ones we are overriding here. - const filteredCoreEpics = [ - coreEpics.executeCellEpic, - coreEpics.executeFocusedCellEpic, - coreEpics.executeCellAfterKernelLaunchEpic, - coreEpics.sendExecuteRequestEpic, - coreEpics.updateDisplayEpic, - coreEpics.executeAllCellsEpic, - coreEpics.commListenEpic, - coreEpics.interruptKernelEpic, - coreEpics.lazyLaunchKernelEpic, - coreEpics.killKernelEpic, - coreEpics.watchExecutionStateEpic, - coreEpics.restartKernelEpic, - coreEpics.fetchKernelspecsEpic, - coreEpics.fetchContentEpic, - coreEpics.updateContentEpic, - coreEpics.saveContentEpic, - coreEpics.publishToBookstore, - coreEpics.publishToBookstoreAfterSave, - coreEpics.sendInputReplyEpic, - ]; - - if (autoStartKernelOnNotebookOpen) { - filteredCoreEpics.push(coreEpics.launchKernelWhenNotebookSetEpic); - } - - return filteredCoreEpics as any; -}; diff --git a/src/Explorer/Notebook/NotebookComponent/types.ts b/src/Explorer/Notebook/NotebookComponent/types.ts deleted file mode 100644 index dc7795b0a2..0000000000 --- a/src/Explorer/Notebook/NotebookComponent/types.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { CellId } from "@nteract/commutable"; -import { AppState } from "@nteract/core"; -import { MessageType } from "@nteract/messaging"; -import * as Immutable from "immutable"; -import { Notebook } from "../../../Common/Constants"; - -export interface SnapshotFragment { - image: HTMLImageElement; - boundingClientRect: DOMRect; - requestId: string; -} - -export type SnapshotRequest = NotebookSnapshotRequest | CellSnapshotRequest; -interface NotebookSnapshotRequestBase { - requestId: string; - aspectRatio: number; - notebookContentRef: string; // notebook redux contentRef - downloadFilename?: string; // Optional: will download as a file -} - -interface NotebookSnapshotRequest extends NotebookSnapshotRequestBase { - type: "notebook"; -} - -interface CellSnapshotRequest extends NotebookSnapshotRequestBase { - type: "celloutput"; - cellId: string; -} - -export interface CdbRecordProps { - databaseAccountName: string | undefined; - defaultExperience: string | undefined; - kernelRestartDelayMs: number; - hoveredCellId: CellId | undefined; - cellOutputSnapshots: Map; - notebookSnapshot?: { imageSrc: string; requestId: string }; - pendingSnapshotRequest?: SnapshotRequest; - notebookSnapshotError?: string; -} - -export type CdbRecord = Immutable.RecordOf; - -export interface CdbAppState extends AppState { - cdb: CdbRecord; -} - -export const makeCdbRecord = Immutable.Record({ - databaseAccountName: undefined, - defaultExperience: undefined, - kernelRestartDelayMs: Notebook.kernelRestartInitialDelayMs, - hoveredCellId: undefined, - cellOutputSnapshots: new Map(), - notebookSnapshot: undefined, - pendingSnapshotRequest: undefined, - notebookSnapshotError: undefined, -}); - -export interface JupyterMessage { - header: JupyterMessageHeader; - parent_header: - | JupyterMessageHeader - | { - msg_id?: string; - }; - metadata: object; - content: C; - channel: string; - buffers?: Uint8Array | null; -} - -export interface JupyterMessageHeader { - msg_id: string; - username: string; - date: string; - msg_type: MT; - version: string; - session: string; - token: string; -} diff --git a/src/Explorer/Notebook/NotebookContentClient.ts b/src/Explorer/Notebook/NotebookContentClient.ts deleted file mode 100644 index 91ddcc7ada..0000000000 --- a/src/Explorer/Notebook/NotebookContentClient.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { stringifyNotebook } from "@nteract/commutable"; -import { FileType, IContent, IContentProvider, IEmptyContent, ServerConfig } from "@nteract/core"; -import { cloneDeep } from "lodash"; -import { AjaxResponse } from "rxjs/ajax"; -import * as StringUtils from "../../Utils/StringUtils"; -import * as FileSystemUtil from "./FileSystemUtil"; -import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentItem"; -import { NotebookUtil } from "./NotebookUtil"; -import { useNotebook } from "./useNotebook"; - -export class NotebookContentClient { - constructor(private contentProvider: IContentProvider) {} - - /** - * This updates the item and points all the children's parent to this item - * @param item - */ - public async updateItemChildren(item: NotebookContentItem): Promise { - const subItems = await this.fetchNotebookFiles(item.path); - const clonedItem = cloneDeep(item); - subItems.forEach((subItem) => (subItem.parent = clonedItem)); - clonedItem.children = subItems; - - return clonedItem; - } - - // TODO: Delete this function when ResourceTreeAdapter is removed. - public async updateItemChildrenInPlace(item: NotebookContentItem): Promise { - return this.fetchNotebookFiles(item.path).then((subItems) => { - item.children = subItems; - subItems.forEach((subItem) => (subItem.parent = item)); - }); - } - - /** - * - * @param parent parent folder - */ - public async createNewNotebookFile( - parent: NotebookContentItem, - isGithubTree?: boolean, - ): Promise { - if (!parent || parent.type !== NotebookContentItemType.Directory) { - throw new Error(`Parent must be a directory: ${parent}`); - } - - const type = "notebook"; - return this.contentProvider - .create<"notebook">(this.getServerConfig(), parent.path, { type }) - .toPromise() - .then((xhr: AjaxResponse) => { - if (typeof xhr.response === "string") { - throw new Error(`jupyter server response invalid: ${xhr.response}`); - } - - if (xhr.response.type !== type) { - throw new Error(`jupyter server response not for notebook: ${xhr.response}`); - } - - const notebookFile = xhr.response; - - const item = NotebookUtil.createNotebookContentItem(notebookFile.name, notebookFile.path, notebookFile.type); - useNotebook.getState().insertNotebookItem(parent, cloneDeep(item), isGithubTree); - // TODO: delete when ResourceTreeAdapter is removed - if (parent.children) { - item.parent = parent; - parent.children.push(item); - } - - return item; - }); - } - - public async deleteContentItem(item: NotebookContentItem, isGithubTree?: boolean): Promise { - const path = await this.deleteNotebookFile(item.path); - useNotebook.getState().deleteNotebookItem(item, isGithubTree); - - // TODO: Delete once old resource tree is removed - if (!path || path !== item.path) { - throw new Error("No path provided"); - } - - if (item.parent && item.parent.children) { - // Remove deleted child - const newChildren = item.parent.children.filter((child) => child.path !== path); - item.parent.children = newChildren; - } - } - - /** - * - * @param name file name - * @param content file content string - * @param parent parent folder - */ - public async uploadFileAsync( - name: string, - content: string, - parent: NotebookContentItem, - isGithubTree?: boolean, - ): Promise { - if (!parent || parent.type !== NotebookContentItemType.Directory) { - throw new Error(`Parent must be a directory: ${parent}`); - } - const filepath = NotebookUtil.getFilePath(parent.path, name); - if (await this.checkIfFilepathExists(filepath)) { - throw new Error(`File already exists: ${filepath}`); - } - - const model: Partial> = { - content, - format: "text", - name, - type: "file", - }; - - return this.contentProvider - .save(this.getServerConfig(), filepath, model) - .toPromise() - .then((xhr: AjaxResponse) => { - const notebookFile = xhr.response; - const item = NotebookUtil.createNotebookContentItem(notebookFile.name, notebookFile.path, notebookFile.type); - useNotebook.getState().insertNotebookItem(parent, cloneDeep(item), isGithubTree); - // TODO: delete when ResourceTreeAdapter is removed - if (parent.children) { - item.parent = parent; - parent.children.push(item); - } - return item; - }); - } - - private async checkIfFilepathExists(filepath: string): Promise { - const parentDirPath = NotebookUtil.getParentPath(filepath); - if (parentDirPath) { - const items = await this.fetchNotebookFiles(parentDirPath); - return items.some((value) => FileSystemUtil.isPathEqual(value.path, filepath)); - } - return false; - } - - /** - * - * @param sourcePath - * @param targetName is not prefixed with path - */ - public renameNotebook( - item: NotebookContentItem, - targetName: string, - isGithubTree?: boolean, - ): Promise { - const sourcePath = item.path; - // Match extension - if (sourcePath.indexOf(".") !== -1) { - const extension = `.${sourcePath.split(".").pop()}`; - if (!StringUtils.endsWith(targetName, extension)) { - targetName += extension; - } - } - const targetPath = NotebookUtil.replaceName(sourcePath, targetName); - return this.contentProvider - .update<"file" | "notebook" | "directory">(this.getServerConfig(), sourcePath, { path: targetPath }) - .toPromise() - .then((xhr) => { - if (typeof xhr.response === "string") { - throw new Error(`jupyter server response invalid: ${xhr.response}`); - } - - if (xhr.response.type !== "file" && xhr.response.type !== "notebook" && xhr.response.type !== "directory") { - throw new Error(`jupyter server response not for notebook/file/directory: ${xhr.response}`); - } - - const notebookFile = xhr.response; - item.name = notebookFile.name; - item.path = notebookFile.path; - item.timestamp = NotebookUtil.getCurrentTimestamp(); - - useNotebook.getState().updateNotebookItem(item, isGithubTree); - - return item; - }); - } - - /** - * - * @param parent - * @param newDirectoryName basename of the new directory - */ - public async createDirectory( - parent: NotebookContentItem, - newDirectoryName: string, - isGithubTree?: boolean, - ): Promise { - if (parent.type !== NotebookContentItemType.Directory) { - throw new Error(`Parent is not a directory: ${parent.path}`); - } - - const targetPath = `${parent.path}/${newDirectoryName}`; - - // Reject if already exists - if (await this.checkIfFilepathExists(targetPath)) { - throw new Error(`Directory already exists: ${targetPath}`); - } - - const type = "directory"; - return this.contentProvider - .save<"directory">(this.getServerConfig(), targetPath, { type, path: targetPath }) - .toPromise() - .then((xhr: AjaxResponse) => { - if (typeof xhr.response === "string") { - throw new Error(`jupyter server response invalid: ${xhr.response}`); - } - - if (xhr.response.type !== type) { - throw new Error(`jupyter server response not for creating directory: ${xhr.response}`); - } - - const dir = xhr.response; - const item = NotebookUtil.createNotebookContentItem(dir.name, dir.path, dir.type); - useNotebook.getState().insertNotebookItem(parent, cloneDeep(item), isGithubTree); - // TODO: delete when ResourceTreeAdapter is removed - item.parent = parent; - parent.children?.push(item); - - return item; - }); - } - - public async readFileContent(filePath: string): Promise { - const xhr = await this.contentProvider.get(this.getServerConfig(), filePath, { content: 1 }).toPromise(); - const content = (xhr.response as any).content; - if (!content) { - throw new Error("No content read"); - } - - const format = (xhr.response as any).format; - switch (format) { - case "text": - return content; - case "base64": - return atob(content); - case "json": - return stringifyNotebook(content); - default: - throw new Error(`Unsupported content format ${format}`); - } - } - - private deleteNotebookFile(path: string): Promise { - return this.contentProvider - .remove(this.getServerConfig(), path) - .toPromise() - .then(() => path); - } - - /** - * Convert rx-jupyter type to our type - * @param type - */ - public static getType(type: FileType): NotebookContentItemType { - switch (type) { - case "directory": - return NotebookContentItemType.Directory; - case "notebook": - return NotebookContentItemType.Notebook; - case "file": - return NotebookContentItemType.File; - default: - throw new Error(`Unknown file type: ${type}`); - } - } - - private fetchNotebookFiles(path: string): Promise { - return this.contentProvider - .get(this.getServerConfig(), path, { - type: "directory", - }) - .toPromise() - .then((xhr) => { - if (xhr.status !== 200) { - throw new Error(JSON.stringify(xhr.response)); - } - - if (typeof xhr.response === "string") { - throw new Error(`jupyter server response invalid: ${xhr.response}`); - } - - if (xhr.response.type !== "directory") { - throw new Error(`jupyter server response not for directory: ${xhr.response}`); - } - - const list = xhr.response.content as IEmptyContent[]; - return list.map( - (item: IEmptyContent): NotebookContentItem => ({ - name: item.name, - path: item.path, - type: NotebookUtil.getType(item.type), - }), - ); - }); - } - - private getServerConfig(): ServerConfig { - const notebookServerInfo = useNotebook.getState().notebookServerInfo; - return { - endpoint: notebookServerInfo?.notebookServerEndpoint, - token: notebookServerInfo?.authToken, - crossDomain: true, - }; - } -} diff --git a/src/Explorer/Notebook/NotebookManager.tsx b/src/Explorer/Notebook/NotebookManager.tsx index a58272e70d..1be3cb096a 100644 --- a/src/Explorer/Notebook/NotebookManager.tsx +++ b/src/Explorer/Notebook/NotebookManager.tsx @@ -2,37 +2,22 @@ * Contains all notebook related stuff meant to be dynamically loaded by explorer */ -import { ImmutableNotebook } from "@nteract/commutable"; -import type { IContentProvider } from "@nteract/core"; import React from "react"; -import { contents } from "rx-jupyter"; -import { Areas, HttpStatusCodes } from "../../Common/Constants"; +import { HttpStatusCodes } from "../../Common/Constants"; import { getErrorMessage } from "../../Common/ErrorHandlingUtils"; import * as Logger from "../../Common/Logger"; import { GitHubClient } from "../../GitHub/GitHubClient"; -import { GitHubContentProvider } from "../../GitHub/GitHubContentProvider"; import { GitHubOAuthService } from "../../GitHub/GitHubOAuthService"; import { useSidePanel } from "../../hooks/useSidePanel"; import { JunoClient } from "../../Juno/JunoClient"; -import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants"; -import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { userContext } from "../../UserContext"; import { useDialog } from "../Controls/Dialog"; import Explorer from "../Explorer"; -import { CopyNotebookPane } from "../Panes/CopyNotebookPane/CopyNotebookPane"; import { GitHubReposPanel } from "../Panes/GitHubReposPanel/GitHubReposPanel"; import { ResourceTreeAdapter } from "../Tree/ResourceTreeAdapter"; -import { InMemoryContentProvider } from "./NotebookComponent/ContentProviders/InMemoryContentProvider"; -import { NotebookContentProvider } from "./NotebookComponent/NotebookContentProvider"; import { NotebookContainerClient } from "./NotebookContainerClient"; -import { NotebookContentClient } from "./NotebookContentClient"; -import { SchemaAnalyzerNotebook } from "./SchemaAnalyzer/SchemaAnalyzerUtils"; import { useNotebook } from "./useNotebook"; -type NotebookPaneContent = string | ImmutableNotebook; - -export type { NotebookPaneContent }; - export interface NotebookManagerOptions { container: Explorer; resourceTree: ResourceTreeAdapter; @@ -44,12 +29,8 @@ export default class NotebookManager { private params: NotebookManagerOptions; public junoClient: JunoClient; - public notebookContentProvider: IContentProvider; public notebookClient: NotebookContainerClient; - public notebookContentClient: NotebookContentClient; - private inMemoryContentProvider: InMemoryContentProvider; - private gitHubContentProvider: GitHubContentProvider; public gitHubOAuthService: GitHubOAuthService; public gitHubClient: GitHubClient; @@ -60,30 +41,10 @@ export default class NotebookManager { this.gitHubOAuthService = new GitHubOAuthService(this.junoClient); this.gitHubClient = new GitHubClient(this.onGitHubClientError); - this.inMemoryContentProvider = new InMemoryContentProvider({ - [SchemaAnalyzerNotebook.path]: { - readonly: true, - content: SchemaAnalyzerNotebook, - }, - }); - - this.gitHubContentProvider = new GitHubContentProvider({ - gitHubClient: this.gitHubClient, - promptForCommitMsg: this.promptForCommitMsg, - }); - - this.notebookContentProvider = new NotebookContentProvider( - this.inMemoryContentProvider, - this.gitHubContentProvider, - contents.JupyterContentProvider, - ); - this.notebookClient = new NotebookContainerClient(() => this.params.container.initNotebooks(userContext?.databaseAccount), ); - this.notebookContentClient = new NotebookContentClient(this.notebookContentProvider); - this.gitHubOAuthService.getTokenObservable().subscribe((token) => { this.gitHubClient.setToken(token?.access_token); if (this?.gitHubOAuthService.isLoggedIn()) { @@ -121,22 +82,6 @@ export default class NotebookManager { } } - public openCopyNotebookPane(name: string, content: string): void { - const { container } = this.params; - useSidePanel - .getState() - .openSidePanel( - "Copy Notebook", - , - ); - } - // Octokit's error handler uses any // eslint-disable-next-line @typescript-eslint/no-explicit-any private onGitHubClientError = (error: any): void => { @@ -167,36 +112,4 @@ export default class NotebookManager { ); } }; - - private promptForCommitMsg = (title: string, primaryButtonLabel: string) => { - return new Promise((resolve, reject) => { - let commitMsg = "Committed from Azure Cosmos DB Notebooks"; - useDialog.getState().showOkCancelModalDialog( - title || "Commit", - undefined, - primaryButtonLabel || "Commit", - () => { - TelemetryProcessor.trace(Action.NotebooksGitHubCommit, ActionModifiers.Mark, { - dataExplorerArea: Areas.Notebook, - }); - resolve(commitMsg); - }, - "Cancel", - () => reject(new Error("Commit dialog canceled")), - undefined, - undefined, - { - label: "Commit message", - autoAdjustHeight: true, - multiline: true, - defaultValue: commitMsg, - rows: 3, - onChange: (_: unknown, newValue: string) => { - commitMsg = newValue; - }, - }, - !commitMsg, - ); - }); - }; } diff --git a/src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx b/src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx deleted file mode 100644 index a93b2e3f41..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/AzureTheme.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { createGlobalStyle } from "styled-components"; - -const AzureTheme = createGlobalStyle` -:root { - /* --theme-primary-bg-hover: #0078d4; - --theme-primary-bg-focus: #0078d4; - --theme-primary-shadow-hover: #0078d4; */ - - --theme-app-bg: white; - --theme-app-fg: var(--nt-color-midnight); - --theme-app-border: var(--nt-color-grey-light); - - --theme-primary-bg: var(--nt-color-grey-lightest); - --theme-primary-bg-hover: var(--nt-color-grey-lighter); - --theme-primary-bg-focus: var(--nt-color-grey-light); - - --theme-primary-fg: var(--nt-color-midnight-light); - --theme-primary-fg-hover: var(--nt-color-midnight); - --theme-primary-fg-focus: var(--theme-app-fg); - - --theme-secondary-bg: var(--theme-primary-bg); - --theme-secondary-bg-hover: var(--theme-primary-bg-hover); - --theme-secondary-bg-focus: var(--theme-primary-bg-focus); - - --theme-secondary-fg: var(--nt-color-midnight-lighter); - --theme-secondary-fg-hover: var(--nt-color-midnight-light); - --theme-secondary-fg-focus: var(--theme-primary-fg); - - /* --theme-primary-shadow-hover: 0px 2px 4px rgba(0, 0, 0, 0.1); - --theme-primary-shadow-focus: 0px 2px 4px rgba(0, 0, 0, 0.1); */ - - --theme-title-bar-bg: var(--theme-primary-bg-hover); - - --theme-menu-bg: var(--theme-primary-bg); - --theme-menu-bg-hover: var(--theme-primary-bg-hover); - --theme-menu-bg-focus: var(--theme-primary-bg-focus); - /* --theme-menu-shadow: var(--theme-primary-shadow-hover); */ - - --theme-menu-fg: var(--theme-app-fg); - --theme-menu-fg-hover: var(--theme-app-fg); - --theme-menu-fg-focus: var(--theme-app-fg); - - --theme-cell-bg: var(--theme-app-bg); - /* --theme-cell-shadow-hover: var(--theme-primary-shadow-hover); */ - /* --theme-cell-shadow-focus: var(--theme-primary-shadow-focus); */ - - --theme-cell-prompt-bg: var(--theme-primary-bg); - --theme-cell-prompt-bg-hover: var(--theme-primary-bg-hover); - --theme-cell-prompt-bg-focus: var(--theme-primary-bg-focus); - - --theme-cell-prompt-fg: var(--theme-secondary-fg); - --theme-cell-prompt-fg-hover: var(--theme-secondary-fg-hover); - --theme-cell-prompt-fg-focus: var(--theme-secondary-fg-focus); - - --theme-cell-toolbar-bg: var(--theme-primary-bg); - --theme-cell-toolbar-bg-hover: var(--theme-primary-bg-hover); - --theme-cell-toolbar-bg-focus: var(--theme-primary-bg-focus); - - --theme-cell-toolbar-fg: var(--theme-secondary-fg); - --theme-cell-toolbar-fg-hover: var(--theme-secondary-fg-hover); - --theme-cell-toolbar-fg-focus: var(--theme-secondary-fg-focus); - - --theme-cell-menu-bg: var(--theme-primary-bg); - --theme-cell-menu-bg-hover: var(--theme-primary-bg-hover); - --theme-cell-menu-bg-focus: var(--theme-primary-bg-focus); - - --theme-cell-menu-fg: var(--theme-primary-fg); - --theme-cell-menu-fg-hover: var(--theme-primary-fg-hover); - --theme-cell-menu-fg-focus: var(--theme-primary-fg-focus); - - --theme-cell-input-bg: var(--theme-secondary-bg); - --theme-cell-input-fg: var(--theme-app-fg); - - --theme-cell-output-bg: var(--theme-app-bg); - --theme-cell-output-fg: var(--theme-primary-fg); - - --theme-cell-creator-bg: var(--theme-app-bg); - - --theme-cell-creator-fg: var(--theme-secondary-fg); - --theme-cell-creator-fg-hover: var(--theme-secondary-fg-hover); - --theme-cell-creator-fg-focus: var(--theme-secondary-fg-focus); - - --theme-pager-bg: #fafafa; - - --cm-background: #fafafa; - --cm-color: black; - - --cm-gutter-bg: white; - - --cm-comment: #a86; - --cm-keyword: blue; - --cm-string: #a22; - --cm-builtin: #077; - --cm-special: #0aa; - --cm-variable: black; - --cm-number: #3a3; - --cm-meta: #555; - --cm-link: #3a3; - --cm-operator: black; - --cm-def: black; - - --cm-activeline-bg: #e8f2ff; - --cm-matchingbracket-outline: grey; - --cm-matchingbracket-color: black; - - --cm-hint-color: var(--cm-color); - --cm-hint-color-active: var(--cm-color); - --cm-hint-bg: var(--theme-app-bg); - --cm-hint-bg-active: #abd1ff; - - --status-bar: #eeedee; -} -`; - -export { AzureTheme }; diff --git a/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.less b/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.less deleted file mode 100644 index d124cd0f2a..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.less +++ /dev/null @@ -1,68 +0,0 @@ -.NotebookReadOnlyRender { - .nteract-cell-container { - margin-bottom: 10px; - } - - .nteract-cell { - padding: 0.5px; - border: 1px solid #ffffff; - border-left: 3px solid #ffffff; - } - - .CodeMirror-scroll { - overflow: hidden !important; - } - - .CodeMirror-lines { - cursor: default; - } - - .CodeMirror { - height: inherit; - } - - .CodeMirror-scroll, - .CodeMirror-linenumber, - .CodeMirror-gutters { - background-color: #f5f5f5; - } - - .nteract-cell:hover { - border: 1px solid #0078d4; - border-left: 3px solid #0078d4; - - .CodeMirror-scroll, - .CodeMirror-linenumber, - .CodeMirror-gutters { - background-color: #ffffff; - } - - .nteract-cell-outputs { - border-top: 1px solid #d7d7d7; - } - - .nteract-md-cell { - background-color: #ffffff; - } - } - - .nteract-cell-outputs { - padding: 10px; - border-top: 1px solid #ffffff; - - pre { - background-color: #ffffff; - border: none; - padding: 0px; - margin: 0px; - } - } - - .nteract-md-cell { - background-color: #f5f5f5; - } - - .nteract-cell:hover.nteract-md-cell { - background-color: #ffffff; - } -} diff --git a/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx b/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx deleted file mode 100644 index 6ad9970548..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { actions, ContentRef } from "@nteract/core"; -import { Cells, CodeCell, RawCell } from "@nteract/stateful-components"; -import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-editors/codemirror"; -import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; -import Prompt, { PassedPromptProps } from "@nteract/stateful-components/lib/inputs/prompt"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { userContext } from "../../../UserContext"; -import loadTransform from "../NotebookComponent/loadTransform"; -import { AzureTheme } from "./AzureTheme"; -import "./base.css"; -import "./default.css"; -import MarkdownCell from "./markdown-cell"; -import "./NotebookReadOnlyRenderer.less"; -import SandboxOutputs from "./outputs/SandboxOutputs"; - -export interface NotebookRendererProps { - contentRef: ContentRef; - hideInputs?: boolean; - hidePrompts?: boolean; - addTransform: (component: React.ComponentType & { MIMETYPE: string }) => void; -} - -/** - * This is the class that uses nteract to render a read-only notebook. - */ -class NotebookReadOnlyRenderer extends React.Component { - componentDidMount() { - if (!userContext.features.sandboxNotebookOutputs) { - loadTransform(this.props as NotebookRendererProps); - } - } - - private renderPrompt(id: string, contentRef: string): JSX.Element { - if (this.props.hidePrompts) { - return <>; - } - - return ( - - {(props: PassedPromptProps) => { - if (props.status === "busy") { - return {"[*]"}; - } - if (props.status === "queued") { - return {"[…]"}; - } - if (typeof props.executionCount === "number") { - return {`[${props.executionCount}]`}; - } - return {"[ ]"}; - }} - - ); - } - - render(): JSX.Element { - return ( -
- - {{ - code: ({ id, contentRef }: { id: string; contentRef: ContentRef }) => ( - - {{ - prompt: (props: { id: string; contentRef: string }) => this.renderPrompt(props.id, props.contentRef), - outputs: userContext.features.sandboxNotebookOutputs - ? () => - : undefined, - editor: { - codemirror: (props: PassedEditorProps) => - this.props.hideInputs ? <> : , - }, - }} - - ), - markdown: ({ id, contentRef }: { id: string; contentRef: ContentRef }) => ( - - {{ - editor: {}, - }} - - ), - raw: ({ id, contentRef }: { id: string; contentRef: ContentRef }) => ( - - {{ - editor: { - codemirror: (props: PassedEditorProps) => - this.props.hideInputs ? <> : , - }, - }} - - ), - }} - - -
- ); - } -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: NotebookRendererProps) => { - const mapDispatchToProps = (dispatch: Dispatch) => { - return { - addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => { - return dispatch( - actions.addTransform({ - mediaType: transform.MIMETYPE, - component: transform, - }), - ); - }, - }; - }; - return mapDispatchToProps; -}; - -export default connect(undefined, makeMapDispatchToProps)(NotebookReadOnlyRenderer); diff --git a/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.less b/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.less deleted file mode 100644 index ed77b9fb7a..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.less +++ /dev/null @@ -1,124 +0,0 @@ -// CommandBar -@HoverColor: #d7d7d7; -@HighlightColor: #0078d4; - -.NotebookRendererContainer { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; -} - -.NotebookRenderer { - overflow: auto; - flex-grow: 1; - - .nteract-cells { - padding-top: 0px; - } - - .nteract-cell-container { - margin-bottom: 10px; - - .nteract-cell { - padding: 0.5px; - border: 1px solid #ffffff; - border-left: 3px solid #ffffff; - - .CellContextMenuButton { - position: sticky; - z-index: 1; - top: 0px; - right: 0px; - margin: 0px 0px 0px -100%; - float: right; - visibility: hidden; - } - } - - .CodeMirror-scroll { - overflow: hidden !important; - } - - .CodeMirror-scroll, - .CodeMirror-linenumber, - .CodeMirror-gutters { - background-color: #f5f5f5; - } - - .CodeMirror { - height: inherit; - } - - .nteract-cell:hover { - border: 1px solid @HoverColor; - border-left: 3px solid @HoverColor; - - .CellContextMenuButton { - visibility: visible; - } - } - } - - .nteract-cell-container.selected { - .nteract-cell { - border: 1px solid @HighlightColor; - border-left: 3px solid @HighlightColor; - } - } - - // White background when hovered or selected - .nteract-cell:hover, - .nteract-cell-container.selected .nteract-cell { - .CodeMirror-scroll, - .CodeMirror-linenumber, - .CodeMirror-gutters { - background-color: #ffffff; - } - - .CodeMirror-linenumber { - color: #015cda; - } - - .nteract-cell-outputs { - border-top: 1px solid @HoverColor; - } - - .nteract-md-cell { - background-color: #ffffff; - } - } - - .nteract-cell-outputs { - padding: 10px; - border-top: 1px solid #ffffff; - - pre { - background-color: #ffffff; - border: none; - padding: 0px; - margin: 0px; - } - } - - .nteract-md-cell { - background-color: #f5f5f5; - } - - .nteract-cell:hover.nteract-md-cell { - background-color: #ffffff; - } - - .nteract-md-cell .ntreact-cell-source { - width: 100%; - } -} - -// Undo tree.less -.expanded::before { - content: ""; -} - -.monaco-editor .monaco-list .main { - background-color: transparent; -} diff --git a/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx b/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx deleted file mode 100644 index 085144871c..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { CellId } from "@nteract/commutable"; -import { CellType } from "@nteract/commutable/src"; -import { actions, ContentRef, selectors } from "@nteract/core"; -import { Cells, CodeCell, RawCell } from "@nteract/stateful-components"; -import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-editors/codemirror"; -import { PassedEditorProps } from "@nteract/stateful-components/lib/inputs/editor"; -import * as React from "react"; -import { DndProvider } from "react-dnd"; -import { HTML5Backend } from "react-dnd-html5-backend"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { userContext } from "../../../UserContext"; -import * as cdbActions from "../NotebookComponent/actions"; -import loadTransform from "../NotebookComponent/loadTransform"; -import { CdbAppState, SnapshotFragment, SnapshotRequest } from "../NotebookComponent/types"; -import { NotebookUtil } from "../NotebookUtil"; -import SecurityWarningBar from "../SecurityWarningBar/SecurityWarningBar"; -import { AzureTheme } from "./AzureTheme"; -import "./base.css"; -import CellCreator from "./decorators/CellCreator"; -import CellLabeler from "./decorators/CellLabeler"; -import HoverableCell from "./decorators/HoverableCell"; -import KeyboardShortcuts from "./decorators/kbd-shortcuts"; -import "./default.css"; -import MarkdownCell from "./markdown-cell"; -import "./NotebookRenderer.less"; -import SandboxOutputs from "./outputs/SandboxOutputs"; -import Prompt from "./Prompt"; -import { promptContent } from "./PromptContent"; -import StatusBar from "./StatusBar"; -import CellToolbar from "./Toolbar"; - -export interface NotebookRendererBaseProps { - contentRef: any; -} - -interface NotebookRendererDispatchProps { - storeNotebookSnapshot: (imageSrc: string, requestId: string) => void; - notebookSnapshotError: (error: string) => void; -} - -interface StateProps { - pendingSnapshotRequest: SnapshotRequest; - cellOutputSnapshots: Map; - notebookSnapshot: { imageSrc: string; requestId: string }; - nbCodeCells: number; -} - -type NotebookRendererProps = NotebookRendererBaseProps & NotebookRendererDispatchProps & StateProps; - -const decorate = (id: string, contentRef: ContentRef, cell_type: CellType, children: React.ReactNode) => { - const Cell = () => ( - // TODO Draggable and HijackScroll not working anymore. Fix or remove when reworking MarkdownCell. - // - // - - - - {children} - - - - // - // - ); - - Cell.defaultProps = { cell_type }; - return ; -}; - -class BaseNotebookRenderer extends React.Component { - private notebookRendererRef = React.createRef(); - - componentDidMount() { - if (!userContext.features.sandboxNotebookOutputs) { - loadTransform(this.props as any); - } - } - - async componentDidUpdate(): Promise { - // Take a snapshot if there's a pending request and all the outputs are also saved - if ( - this.props.pendingSnapshotRequest && - this.props.pendingSnapshotRequest.type === "notebook" && - this.props.pendingSnapshotRequest.notebookContentRef === this.props.contentRef && - (!this.props.notebookSnapshot || - this.props.pendingSnapshotRequest.requestId !== this.props.notebookSnapshot.requestId) && - this.props.cellOutputSnapshots.size === this.props.nbCodeCells - ) { - try { - // Use Html2Canvas because it is much more reliable and fast than dom-to-file - const result = await NotebookUtil.takeScreenshotHtml2Canvas( - this.notebookRendererRef.current, - this.props.pendingSnapshotRequest.aspectRatio, - [...this.props.cellOutputSnapshots.values()], - this.props.pendingSnapshotRequest.downloadFilename, - ); - this.props.storeNotebookSnapshot(result.imageSrc, this.props.pendingSnapshotRequest.requestId); - } catch (error) { - this.props.notebookSnapshotError(error.message); - } finally { - this.setState({ processedSnapshotRequest: undefined }); - } - } - } - - render(): JSX.Element { - return ( - <> -
- -
- - - - {{ - code: ({ id, contentRef }: { id: CellId; contentRef: ContentRef }) => - decorate( - id, - contentRef, - "code", - - {{ - editor: { - codemirror: (props: PassedEditorProps) => ( - - ), - }, - prompt: ({ id, contentRef }: { id: CellId; contentRef: ContentRef }) => ( - - {promptContent} - - ), - toolbar: () => , - outputs: userContext.features.sandboxNotebookOutputs - ? () => - : undefined, - }} - , - ), - markdown: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => - decorate( - id, - contentRef, - "markdown", - - {{ - editor: { - codemirror: (props: PassedEditorProps) => ( - - ), - }, - toolbar: () => , - }} - , - ), - - raw: ({ id, contentRef }: { id: any; contentRef: ContentRef }) => - decorate( - id, - contentRef, - "raw", - - {{ - editor: { - codemirror: (props: PassedEditorProps) => ( - - ), - }, - toolbar: () => , - }} - , - ), - }} - - - - -
- -
- - ); - } -} - -export const makeMapStateToProps = ( - initialState: CdbAppState, - ownProps: NotebookRendererProps, -): ((state: CdbAppState) => StateProps) => { - const mapStateToProps = (state: CdbAppState): StateProps => { - const { contentRef } = ownProps; - const model = selectors.model(state, { contentRef }); - - let nbCodeCells; - if (model && model.type === "notebook") { - nbCodeCells = NotebookUtil.findCodeCellWithDisplay(model.notebook).length; - } - const { pendingSnapshotRequest, cellOutputSnapshots, notebookSnapshot } = state.cdb; - return { pendingSnapshotRequest, cellOutputSnapshots, notebookSnapshot, nbCodeCells }; - }; - return mapStateToProps; -}; - -const makeMapDispatchToProps = (initialDispatch: Dispatch, initialProps: NotebookRendererBaseProps) => { - const mapDispatchToProps = (dispatch: Dispatch) => { - return { - addTransform: (transform: React.ComponentType & { MIMETYPE: string }) => - dispatch( - actions.addTransform({ - mediaType: transform.MIMETYPE, - component: transform, - }), - ), - storeNotebookSnapshot: (imageSrc: string, requestId: string) => - dispatch(cdbActions.storeNotebookSnapshot({ imageSrc, requestId })), - notebookSnapshotError: (error: string) => dispatch(cdbActions.notebookSnapshotError({ error })), - }; - }; - return mapDispatchToProps; -}; - -export default connect(makeMapStateToProps, makeMapDispatchToProps)(BaseNotebookRenderer); diff --git a/src/Explorer/Notebook/NotebookRenderer/Prompt.less b/src/Explorer/Notebook/NotebookRenderer/Prompt.less deleted file mode 100644 index ec840fe7bf..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/Prompt.less +++ /dev/null @@ -1,36 +0,0 @@ -@import "../../../../less/Common/Constants"; - -.runCellButton { - max-height: 100%; - width: 100%; - position: sticky; - z-index: 300; - left: 0; - top: 0; - - .ms-Button-flexContainer { - align-items: start; - padding-top: 11px; - - .ms-Button-icon { - color: #0078D4; - font-size: 20px; - } - } -} - -.disabledRunCellButton { - .runCellButton .ms-Button-flexContainer .ms-Button-icon { - color: @BaseMediumHigh; - } -} - -.greyStopButton { - .runCellButton .ms-Button-flexContainer .ms-Button-icon { - color: @BaseMediumHigh; - } - - .ms-Spinner .ms-Spinner-circle { - border-top-color: @BaseMediumHigh; - } -} \ No newline at end of file diff --git a/src/Explorer/Notebook/NotebookRenderer/Prompt.tsx b/src/Explorer/Notebook/NotebookRenderer/Prompt.tsx deleted file mode 100644 index bfd16676a3..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/Prompt.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { actions, ContentRef, selectors } from "@nteract/core"; -import React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; -import * as cdbActions from "../NotebookComponent/actions"; -import { CdbAppState } from "../NotebookComponent/types"; -import { NotebookUtil } from "../NotebookUtil"; - -export interface PassedPromptProps { - id: string; - contentRef: ContentRef; - status?: string; - executionCount?: number; - isHovered?: boolean; - isRunDisabled?: boolean; - runCell?: () => void; - stopCell?: () => void; -} - -interface ComponentProps { - id: string; - contentRef: ContentRef; - isHovered?: boolean; - isNotebookUntrusted?: boolean; - children: (props: PassedPromptProps) => React.ReactNode; -} - -interface StateProps { - status?: string; - executionCount?: number; -} - -interface DispatchProps { - executeCell: () => void; - stopExecution: () => void; -} - -type Props = StateProps & DispatchProps & ComponentProps; - -export class PromptPure extends React.Component { - render() { - return ( -
- {this.props.children({ - id: this.props.id, - contentRef: this.props.contentRef, - status: this.props.status, - executionCount: this.props.executionCount, - runCell: this.props.executeCell, - stopCell: this.props.stopExecution, - isHovered: this.props.isHovered, - isRunDisabled: this.props.isNotebookUntrusted, - })} -
- ); - } -} - -const makeMapStateToProps = (_state: CdbAppState, ownProps: ComponentProps): ((state: CdbAppState) => StateProps) => { - const mapStateToProps = (state: CdbAppState) => { - const { contentRef, id } = ownProps; - const model = selectors.model(state, { contentRef }); - - let status; - let executionCount; - - if (model && model.type === "notebook") { - status = model.transient.getIn(["cellMap", id, "status"]); - const cell = selectors.notebook.cellById(model, { id }); - if (cell) { - executionCount = cell.get("execution_count", undefined); - } - } - - const isHovered = state.cdb.hoveredCellId === id; - - return { - status, - executionCount, - isHovered, - isNotebookUntrusted: NotebookUtil.isNotebookUntrusted(state, contentRef), - }; - }; - return mapStateToProps; -}; - -const mapDispatchToProps = ( - dispatch: Dispatch, - { id, contentRef }: { id: string; contentRef: ContentRef }, -): DispatchProps => ({ - executeCell: () => { - dispatch(actions.executeCell({ id, contentRef })); - dispatch( - cdbActions.traceNotebookTelemetry({ - action: Action.ExecuteCellPromptBtn, - actionModifier: ActionModifiers.Mark, - }), - ); - }, - stopExecution: () => dispatch(actions.interruptKernel({})), -}); - -export default connect(makeMapStateToProps, mapDispatchToProps)(PromptPure); diff --git a/src/Explorer/Notebook/NotebookRenderer/PromptContent.test.tsx b/src/Explorer/Notebook/NotebookRenderer/PromptContent.test.tsx deleted file mode 100644 index 83dc8a256e..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/PromptContent.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { shallow } from "enzyme"; -import { PassedPromptProps } from "./Prompt"; -import { promptContent } from "./PromptContent"; - -describe("PromptContent", () => { - it("renders for busy status", () => { - const props: PassedPromptProps = { - id: "id", - contentRef: "contentRef", - status: "busy", - }; - const wrapper = shallow(promptContent(props)); - - expect(wrapper).toMatchSnapshot(); - }); - - it("renders when hovered", () => { - const props: PassedPromptProps = { - id: "id", - contentRef: "contentRef", - isHovered: true, - }; - const wrapper = shallow(promptContent(props)); - - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/src/Explorer/Notebook/NotebookRenderer/PromptContent.tsx b/src/Explorer/Notebook/NotebookRenderer/PromptContent.tsx deleted file mode 100644 index 618f9d5905..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/PromptContent.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { IconButton, Spinner, SpinnerSize } from "@fluentui/react"; -import * as React from "react"; -import { NotebookUtil } from "../NotebookUtil"; -import { PassedPromptProps } from "./Prompt"; -import "./Prompt.less"; - -export const promptContent = (props: PassedPromptProps): JSX.Element => { - if (props.status === "busy") { - const stopButtonText = "Stop cell execution"; - return ( -
- - -
- ); - } else if (props.isHovered) { - const playButtonText = props.isRunDisabled ? NotebookUtil.UntrustedNotebookRunHint : "Run cell"; - return ( -
- -
- ); - } else { - return
{promptText(props)}
; - } -}; - -/** - * Generate what text goes inside the prompt based on the props to the prompt - */ -const promptText = (props: PassedPromptProps): string => { - if (props.status === "busy") { - return "[*]"; - } - if (props.status === "queued") { - return "[…]"; - } - if (typeof props.executionCount === "number") { - return `[${props.executionCount}]`; - } - return "[ ]"; -}; diff --git a/src/Explorer/Notebook/NotebookRenderer/StatusBar.test.tsx b/src/Explorer/Notebook/NotebookRenderer/StatusBar.test.tsx deleted file mode 100644 index a2a41974a1..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/StatusBar.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { shallow } from "enzyme"; -import React from "react"; -import { StatusBar } from "./StatusBar"; - -describe("StatusBar", () => { - test("can render on a dummyNotebook", () => { - const lastSaved = new Date(); - const kernelSpecDisplayName = "python3"; - - const component = shallow( - , - ); - - expect(component).not.toBeNull(); - }); - test("Update if kernelSpecDisplayName has changed", () => { - const lastSaved = new Date(); - const kernelSpecDisplayName = "python3"; - - const component = shallow( - , - ); - - const shouldUpdate = component.instance().shouldComponentUpdate( - { - lastSaved, - kernelSpecDisplayName: "javascript", - kernelStatus: "kernelStatus", - }, - undefined, - undefined, - ); - expect(shouldUpdate).toBe(true); - }); - test("update if kernelStatus has changed", () => { - const lastSaved = new Date(); - const kernelSpecDisplayName = "python3"; - - const component = shallow( - , - ); - - const shouldUpdate = component.instance().shouldComponentUpdate( - { - lastSaved: new Date(), - kernelSpecDisplayName: "python3", - kernelStatus: "kernelStatus", - }, - undefined, - undefined, - ); - expect(shouldUpdate).toBe(true); - }); -}); diff --git a/src/Explorer/Notebook/NotebookRenderer/StatusBar.tsx b/src/Explorer/Notebook/NotebookRenderer/StatusBar.tsx deleted file mode 100644 index 806e49b719..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/StatusBar.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { AppState, ContentRef, selectors } from "@nteract/core"; -import distanceInWordsToNow from "date-fns/distance_in_words_to_now"; -import React from "react"; -import { connect } from "react-redux"; -import styled from "styled-components"; -import { StyleConstants } from "../../../Common/StyleConstants"; - -interface Props { - lastSaved?: Date | null; - kernelSpecDisplayName: string; - kernelStatus: string; -} - -const NOT_CONNECTED = "not connected"; - -export const LeftStatus = styled.div` - float: left; - display: block; - padding-left: 10px; -`; -export const RightStatus = styled.div` - float: right; - padding-right: 10px; - display: block; -`; - -export const Bar = styled.div` - padding: 8px 0px 2px; - border-top: 1px solid ${StyleConstants.BaseMedium}; - border-left: 1px solid ${StyleConstants.BaseMedium}; - width: 100%; - height: 100%; - font-size: 12px; - line-height: 0.5em; - background: var(--status-bar); - z-index: 99; - @media print { - display: none; - } -`; - -const BarContainer = styled.div` - padding-left: 4px; -`; - -export class StatusBar extends React.Component { - shouldComponentUpdate(nextProps: Props): boolean { - if (this.props.lastSaved !== nextProps.lastSaved || this.props.kernelStatus !== nextProps.kernelStatus) { - return true; - } - return false; - } - - render() { - const name = this.props.kernelSpecDisplayName || "Loading..."; - - return ( - - - - {this.props.lastSaved ? ( -

Last saved {distanceInWordsToNow(this.props.lastSaved)}

- ) : ( -

Not saved yet

- )} -
- -

- {name} | {this.props.kernelStatus} -

-
-
-
- ); - } -} - -interface InitialProps { - contentRef: ContentRef; -} - -const makeMapStateToProps = (_initialState: AppState, initialProps: InitialProps): ((state: AppState) => Props) => { - const { contentRef } = initialProps; - - const mapStateToProps = (state: AppState) => { - const content = selectors.content(state, { contentRef }); - - if (!content || content.type !== "notebook") { - return { - kernelStatus: NOT_CONNECTED, - kernelSpecDisplayName: "no kernel", - lastSaved: undefined, - }; - } - - const kernelRef = content.model.kernelRef; - let kernel; - if (kernelRef) { - kernel = selectors.kernel(state, { kernelRef }); - } - - const lastSaved = content && content.lastSaved ? content.lastSaved : undefined; - - const kernelStatus = kernel?.status || NOT_CONNECTED; - - // TODO: We need kernels associated to the kernelspec they came from - // so we can pluck off the display_name and provide it here - let kernelSpecDisplayName = " "; - if (kernelStatus === NOT_CONNECTED) { - kernelSpecDisplayName = "no kernel"; - } else if (kernel?.kernelSpecName) { - kernelSpecDisplayName = kernel.kernelSpecName; - } else if (content && content.type === "notebook") { - // TODO Fix typing here - kernelSpecDisplayName = selectors.notebook.displayName(content.model as never) || " "; - } - - return { - kernelSpecDisplayName, - kernelStatus, - lastSaved, - }; - }; - - return mapStateToProps; -}; - -export default connect(makeMapStateToProps)(StatusBar); diff --git a/src/Explorer/Notebook/NotebookRenderer/Toolbar.tsx b/src/Explorer/Notebook/NotebookRenderer/Toolbar.tsx deleted file mode 100644 index 5b3b52f7ff..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/Toolbar.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import { ContextualMenuItemType, DirectionalHint, IconButton, IContextualMenuItem } from "@fluentui/react"; -import { CellId, CellType, ImmutableCodeCell } from "@nteract/commutable"; -import { actions, AppState, DocumentRecordProps } from "@nteract/core"; -import * as selectors from "@nteract/selectors"; -import { CellToolbarContext } from "@nteract/stateful-components"; -import { ContentRef } from "@nteract/types"; -import { RecordOf } from "immutable"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; -import * as cdbActions from "../NotebookComponent/actions"; -import { SnapshotRequest } from "../NotebookComponent/types"; -import { NotebookUtil } from "../NotebookUtil"; - -export interface ComponentProps { - contentRef: ContentRef; - id: CellId; -} - -interface DispatchProps { - executeCell: () => void; - insertCodeCellAbove: () => void; - insertCodeCellBelow: () => void; - insertTextCellAbove: () => void; - insertTextCellBelow: () => void; - moveCell: (destinationId: CellId, above: boolean) => void; - clearOutputs: () => void; - deleteCell: () => void; - traceNotebookTelemetry: (action: Action, actionModifier?: string, data?: string) => void; - takeNotebookSnapshot: (payload: SnapshotRequest) => void; -} - -interface StateProps { - cellType: CellType; - cellIdAbove: CellId; - cellIdBelow: CellId; - hasCodeOutput: boolean; - isNotebookUntrusted: boolean; -} - -class BaseToolbar extends React.PureComponent { - static contextType = CellToolbarContext; - - render(): JSX.Element { - let items: IContextualMenuItem[] = []; - const isNotebookUntrusted = this.props.isNotebookUntrusted; - const runTooltip = isNotebookUntrusted ? NotebookUtil.UntrustedNotebookRunHint : undefined; - - if (this.props.cellType === "code") { - items = items.concat([ - { - key: "Run", - text: "Run", - title: runTooltip, - disabled: isNotebookUntrusted, - onClick: () => { - this.props.executeCell(); - this.props.traceNotebookTelemetry(Action.NotebooksExecuteCellFromMenu, ActionModifiers.Mark); - }, - }, - { - key: "Clear Outputs", - text: "Clear Outputs", - onClick: () => { - this.props.clearOutputs(); - this.props.traceNotebookTelemetry(Action.NotebooksClearOutputsFromMenu, ActionModifiers.Mark); - }, - }, - ]); - - if (this.props.hasCodeOutput) { - items.push({ - key: "Export output to image", - text: "Export output to image", - onClick: () => { - this.props.takeNotebookSnapshot({ - requestId: new Date().getTime().toString(), - aspectRatio: undefined, - type: "celloutput", - cellId: this.props.id, - notebookContentRef: this.props.contentRef, - downloadFilename: `celloutput-${this.props.contentRef}_${this.props.id}.png`, - }); - }, - }); - } - - items.push({ - key: "Divider", - itemType: ContextualMenuItemType.Divider, - }); - } - - items = items.concat([ - { - key: "Divider2", - itemType: ContextualMenuItemType.Divider, - }, - { - key: "Insert Code Cell Above", - text: "Insert Code Cell Above", - onClick: () => { - this.props.insertCodeCellAbove(); - this.props.traceNotebookTelemetry(Action.NotebooksInsertCodeCellAboveFromMenu, ActionModifiers.Mark); - }, - }, - { - key: "Insert Code Cell Below", - text: "Insert Code Cell Below", - onClick: () => { - this.props.insertCodeCellBelow(); - this.props.traceNotebookTelemetry(Action.NotebooksInsertCodeCellBelowFromMenu, ActionModifiers.Mark); - }, - }, - { - key: "Insert Text Cell Above", - text: "Insert Text Cell Above", - onClick: () => { - this.props.insertTextCellAbove(); - this.props.traceNotebookTelemetry(Action.NotebooksInsertTextCellAboveFromMenu, ActionModifiers.Mark); - }, - }, - { - key: "Insert Text Cell Below", - text: "Insert Text Cell Below", - onClick: () => { - this.props.insertTextCellBelow(); - this.props.traceNotebookTelemetry(Action.NotebooksInsertTextCellBelowFromMenu, ActionModifiers.Mark); - }, - }, - { - key: "Divider3", - itemType: ContextualMenuItemType.Divider, - }, - ]); - - const moveItems: IContextualMenuItem[] = []; - if (this.props.cellIdAbove !== undefined) { - moveItems.push({ - key: "Move Cell Up", - text: "Move Cell Up", - onClick: () => { - this.props.moveCell(this.props.cellIdAbove, true); - this.props.traceNotebookTelemetry(Action.NotebooksMoveCellUpFromMenu, ActionModifiers.Mark); - }, - }); - } - - if (this.props.cellIdBelow !== undefined) { - moveItems.push({ - key: "Move Cell Down", - text: "Move Cell Down", - onClick: () => { - this.props.moveCell(this.props.cellIdBelow, false); - this.props.traceNotebookTelemetry(Action.NotebooksMoveCellDownFromMenu, ActionModifiers.Mark); - }, - }); - } - - if (moveItems.length > 0) { - moveItems.push({ - key: "Divider4", - itemType: ContextualMenuItemType.Divider, - }); - items = items.concat(moveItems); - } - - items.push({ - key: "Delete Cell", - text: "Delete Cell", - onClick: () => { - this.props.deleteCell(); - this.props.traceNotebookTelemetry(Action.DeleteCellFromMenu, ActionModifiers.Mark); - }, - }); - - const menuItemLabel = "More"; - return ( - - ); - } -} - -const mapDispatchToProps = ( - dispatch: Dispatch, - { id, contentRef }: { id: CellId; contentRef: ContentRef }, -): DispatchProps => ({ - executeCell: () => dispatch(actions.executeCell({ id, contentRef })), - insertCodeCellAbove: () => dispatch(actions.createCellAbove({ id, contentRef, cellType: "code" })), - insertCodeCellBelow: () => dispatch(actions.createCellBelow({ id, contentRef, cellType: "code" })), - insertTextCellAbove: () => dispatch(actions.createCellAbove({ id, contentRef, cellType: "markdown" })), - insertTextCellBelow: () => dispatch(actions.createCellBelow({ id, contentRef, cellType: "markdown" })), - moveCell: (destinationId: CellId, above: boolean) => - dispatch(actions.moveCell({ id, contentRef, destinationId, above })), - clearOutputs: () => dispatch(actions.clearOutputs({ id, contentRef })), - deleteCell: () => dispatch(actions.deleteCell({ id, contentRef })), - traceNotebookTelemetry: (action: Action, actionModifier?: string, data?: string) => - dispatch(cdbActions.traceNotebookTelemetry({ action, actionModifier, data })), - takeNotebookSnapshot: (request: SnapshotRequest) => dispatch(cdbActions.takeNotebookSnapshot(request)), -}); - -const makeMapStateToProps = (state: AppState, ownProps: ComponentProps): ((state: AppState) => StateProps) => { - const mapStateToProps = (state: AppState) => { - const cell = selectors.cell.cellFromState(state, { id: ownProps.id, contentRef: ownProps.contentRef }); - const cellType = cell.cell_type; - const model = selectors.model(state, { contentRef: ownProps.contentRef }); - const cellOrder = selectors.notebook.cellOrder(model as RecordOf); - const cellIndex = cellOrder.indexOf(ownProps.id); - const cellIdAbove = cellIndex ? cellOrder.get(cellIndex - 1, undefined) : undefined; - const cellIdBelow = cellIndex !== undefined ? cellOrder.get(cellIndex + 1, undefined) : undefined; - - return { - cellType, - cellIdAbove, - cellIdBelow, - hasCodeOutput: cellType === "code" && NotebookUtil.hasCodeCellOutput(cell as ImmutableCodeCell), - isNotebookUntrusted: NotebookUtil.isNotebookUntrusted(state, ownProps.contentRef), - }; - }; - return mapStateToProps; -}; - -export default connect(makeMapStateToProps, mapDispatchToProps)(BaseToolbar); diff --git a/src/Explorer/Notebook/NotebookRenderer/__snapshots__/PromptContent.test.tsx.snap b/src/Explorer/Notebook/NotebookRenderer/__snapshots__/PromptContent.test.tsx.snap deleted file mode 100644 index 514ff6d6b6..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/__snapshots__/PromptContent.test.tsx.snap +++ /dev/null @@ -1,60 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PromptContent renders for busy status 1`] = ` -
- - -
-`; - -exports[`PromptContent renders when hovered 1`] = ` -
- -
-`; diff --git a/src/Explorer/Notebook/NotebookRenderer/base.css b/src/Explorer/Notebook/NotebookRenderer/base.css deleted file mode 100644 index fbf2b3f10f..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/base.css +++ /dev/null @@ -1,143 +0,0 @@ -.nteract-cell-prompt { - font-size: 12px; - line-height: 22px; - /* For creating a buffer area for */ - min-height: 22px; - width: var(--prompt-width, 50px); - padding: 2px 0; - text-align: center; -} - -.nteract-cell-outputs { - padding: 10px 10px 10px calc(var(--prompt-width, 50px) + 10px); - word-wrap: break-word; - overflow-y: hidden; - outline: none; - /* When expanded, this is overtaken to 100% */ - text-overflow: ellipsis; -} - -.nteract-cell-outputs:empty { - display: none; -} - -.nteract-cell-outputs code { - white-space: pre-wrap; - font-size: 14px; -} - -.nteract-cell-outputs pre { - white-space: pre-wrap; - font-size: 14px; - word-wrap: break-word; -} - -.nteract-cell-outputs img { - display: block; - max-width: 100%; -} - -.nteract-cell { - position: relative; - transition: all 0.1s ease-in-out; -} - -.nteract-cells { - padding-bottom: 10px; - padding: var(--nt-spacing-m, 10px); -} - -.nteract-cell-input .nteract-cell-source { - flex: 1 1 auto; - overflow: visible; -} - -/** Adaptation for the R kernel's inline lists **/ -.nteract-cell-outputs .list-inline li { - display: inline; - padding-right: 20px; - text-align: center; -} - -.nteract-cell-input { - display: flex; - flex-direction: row; -} -.nteract-cell-input.invisible { - height: 34px; -} -.nteract-cell-input .nteract-cell-prompt { - flex: 0 0 auto; -} - -/* for nested paragraphs in block quotes */ -.nteract-cell-outputs blockquote p { - display: inline; -} -.nteract-cell-outputs dd { - display: block; - -webkit-margin-start: 40px; -} -.nteract-cell-outputs dl { - display: block; - -webkit-margin-before: 1__qem; - -webkit-margin-after: 1em; - -webkit-margin-start: 0; - -webkit-margin-end: 0; -} -.nteract-cell-outputs dt { - display: block; -} -.nteract-cell-outputs dl { - width: 100%; - overflow: hidden; - padding: 0; - margin: 0; -} -.nteract-cell-outputs dt { - font-weight: bold; - float: left; - width: 20%; - /* adjust the width; make sure the total of both is 100% */ - padding: 0; - margin: 0; -} -.nteract-cell-outputs dd { - float: left; - width: 80%; - /* adjust the width; make sure the total of both is 100% */ - padding: 0; - margin: 0; -} - -.nteract-cell-outputs kbd { - display: inline-block; - padding: 0.1em 0.5em; - margin: 0 0.2em; -} - -.nteract-cell-outputs table { - border-collapse: collapse; -} - -.nteract-cell-outputs th { - text-align: left; -} - -.nteract-cell-outputs th, - .nteract-cell-outputs td, - /* for legacy output handling */ - .nteract-cell-outputs .th, - .nteract-cell-outputs .td { - padding: 0.5em 1em; -} - -.nteract-cell-outputs blockquote { - padding: 0.75em 0.5em 0.75em 1em; -} - -.nteract-cell-outputs blockquote::before { - display: block; - height: 0; - margin-left: -0.95em; -} diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx b/src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx deleted file mode 100644 index 429c069a0d..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/CellCreator.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import { actions, selectors, ContentRef, AppState } from "@nteract/core"; -import { CellType } from "@nteract/commutable"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; - -import styled from "styled-components"; -import AddCodeCellIcon from "../../../../../images/notebook/add-code-cell.svg"; -import AddTextCellIcon from "../../../../../images/notebook/add-text-cell.svg"; - -interface ComponentProps { - id: string; - contentRef: ContentRef; - children: React.ReactNode; -} - -interface StateProps { - isFirstCell: boolean; -} - -interface DispatchProps { - createCellAppend: (payload: { cellType: CellType; contentRef: ContentRef }) => void; - createCellAbove: (payload: { cellType: CellType; id?: string; contentRef: ContentRef }) => void; - createCellBelow: (payload: { cellType: CellType; id?: string; source: string; contentRef: ContentRef }) => void; -} - -export const CellCreatorMenu = styled.div` - display: none; - pointer-events: all; - position: relative; - top: 0px; - /** - * Now that the cell-creator is added as a decorator we need - * this x-index to ensure that it is always shown on the top - * of other cells. - */ - z-index: 50; - - button:first-child { - margin-right: 8px; - } - - button { - display: inline-block; - - width: 109px; - height: 24px; - padding: 0px 4px; - - text-align: center; - font-size: 12px; - line-height: 16px; - - border: 1px solid #0078d4; - outline: none; - background: var(--theme-cell-creator-bg); - color: #0078d4; - } - - button span { - color: var(--theme-cell-creator-fg); - } - - button span:hover { - color: var(--theme-cell-creator-fg-hover); - } - - button:hover { - background-color: #f0f0f0; - } - - .octicon { - transition: color 0.5s; - margin-right: 12px; - } - - img { - height: 12px; - } -`; - -export const Divider = styled.div` - display: none; - position: relative; - top: 12px; - height: 1px; - width: 100%; - border-top: 1px solid rgba(204, 204, 204, 0.8); -`; - -const CreatorHoverMask = styled.div` - display: block; - position: relative; - overflow: visible; - height: 0px; - - @media print { - display: none; - } -`; -const CreatorHoverRegion = styled.div` - position: relative; - overflow: visible; - top: 5px; - height: 30px; - text-align: center; - - &:hover ${CellCreatorMenu} { - display: inline-block; - } - - &:hover ${Divider} { - display: inherit; - } -`; - -const FirstCreatorContainer = styled.div` - height: 20px; -`; - -interface CellCreatorProps { - above: boolean; - createCell: (type: "markdown" | "code", above: boolean) => void; -} - -export class PureCellCreator extends React.PureComponent { - createMarkdownCell = () => { - this.props.createCell("markdown", this.props.above); - }; - - createCodeCell = () => { - this.props.createCell("code", this.props.above); - }; - - render() { - return ( - - - - - - - - - - ); - } -} - -class CellCreator extends React.PureComponent { - createCell = (type: "code" | "markdown", above: boolean): void => { - const { createCellBelow, createCellAppend, createCellAbove, id, contentRef } = this.props; - - if (id === undefined || typeof id !== "string") { - createCellAppend({ cellType: type, contentRef }); - return; - } - - above - ? createCellAbove({ cellType: type, id, contentRef }) - : createCellBelow({ cellType: type, id, source: "", contentRef }); - }; - - render() { - return ( - - {this.props.isFirstCell && ( - - - - )} - {this.props.children} - - - ); - } -} - -const mapStateToProps = (state: AppState, ownProps: ComponentProps) => { - const { id, contentRef } = ownProps; - const model = selectors.model(state, { contentRef }); - let isFirstCell = false; - - if (model && model.type === "notebook") { - const cellOrder = selectors.notebook.cellOrder(model); - const cellIndex = cellOrder.findIndex((cellId) => cellId === id); - isFirstCell = cellIndex === 0; - } - - return { - isFirstCell, - }; -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - createCellAbove: (payload: { cellType: CellType; id?: string; contentRef: ContentRef }) => - dispatch(actions.createCellAbove(payload)), - createCellAppend: (payload: { cellType: CellType; contentRef: ContentRef }) => - dispatch(actions.createCellAppend(payload)), - createCellBelow: (payload: { cellType: CellType; id?: string; source: string; contentRef: ContentRef }) => - dispatch(actions.createCellBelow(payload)), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(CellCreator); diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/CellLabeler.less b/src/Explorer/Notebook/NotebookRenderer/decorators/CellLabeler.less deleted file mode 100644 index 11e9db2dd3..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/CellLabeler.less +++ /dev/null @@ -1,8 +0,0 @@ -@import "../../../../../less/Common/Constants.less"; - -.CellLabeler .CellLabel { - margin-left: 5px; - font-family: @DataExplorerFont; - font-size: 12px; - margin-bottom: 5px; -} \ No newline at end of file diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/CellLabeler.tsx b/src/Explorer/Notebook/NotebookRenderer/decorators/CellLabeler.tsx deleted file mode 100644 index cfef390260..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/CellLabeler.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { AppState, ContentRef, DocumentRecordProps, selectors } from "@nteract/core"; -import { RecordOf } from "immutable"; -import React from "react"; -import { connect } from "react-redux"; -import "./CellLabeler.less"; - -interface ComponentProps { - id: string; - contentRef: ContentRef; // TODO: Make this per contentRef? - children: React.ReactNode; -} - -interface StateProps { - cellIndex: number; -} - -/** - * Displays "Cell " - */ -class CellLabeler extends React.Component { - render() { - return ( -
-
Cell {this.props.cellIndex + 1}
- {this.props.children} -
- ); - } -} - -const makeMapStateToProps = (_state: AppState, ownProps: ComponentProps): ((state: AppState) => StateProps) => { - const mapStateToProps = (state: AppState) => { - const model = selectors.model(state, { contentRef: ownProps.contentRef }); - const cellOrder = selectors.notebook.cellOrder(model as RecordOf); - const cellIndex = cellOrder.indexOf(ownProps.id); - - return { - cellIndex, - }; - }; - return mapStateToProps; -}; - -export default connect(makeMapStateToProps, undefined)(CellLabeler); diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/HoverableCell.tsx b/src/Explorer/Notebook/NotebookRenderer/decorators/HoverableCell.tsx deleted file mode 100644 index 5454de1067..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/HoverableCell.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ContentRef } from "@nteract/core"; -import React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import * as actions from "../../NotebookComponent/actions"; - -interface ComponentProps { - id: string; - contentRef: ContentRef; // TODO: Make this per contentRef? - children: React.ReactNode; -} - -interface DispatchProps { - hover: () => void; - unHover: () => void; -} - -/** - * HoverableCell sets the hovered cell - */ -class HoverableCell extends React.Component { - render() { - return ( -
- {this.props.children} -
- ); - } -} - -const mapDispatchToProps = (dispatch: Dispatch, { id }: { id: string }): DispatchProps => ({ - hover: () => dispatch(actions.setHoveredCell({ cellId: id })), - unHover: () => dispatch(actions.setHoveredCell({ cellId: undefined })), -}); - -export default connect(undefined, mapDispatchToProps)(HoverableCell); diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx b/src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx deleted file mode 100644 index ab9c6dfa6c..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx +++ /dev/null @@ -1,234 +0,0 @@ -import { actions, ContentRef } from "@nteract/core"; -import React from "react"; -import { - ConnectDragPreview, - ConnectDragSource, - ConnectDropTarget, - DragSource, - DragSourceConnector, - DragSourceMonitor, - DropTarget, - DropTargetConnector, - DropTargetMonitor, -} from "react-dnd"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import styled, { StyledComponent } from "styled-components"; - -/** - The cell drag preview image is just a little stylized version of - - [ ] - - It matches nteract's default light theme - - */ -const cellDragPreviewImage = [ - "data:image/png;base64,", - "iVBORw0KGgoAAAANSUhEUgAAADsAAAAzCAYAAAApdnDeAAAAAXNSR0IArs4c6QAA", - "AwNJREFUaAXtmlFL3EAUhe9MZptuoha3rLWgYC0W+lj/T3+26INvXbrI2oBdE9km", - "O9Nzxu1S0LI70AQScyFmDDfkfvdMZpNwlCCccwq7f21MaVM4FPtkU0o59RdoJBMx", - "WZINBg+DQWGKCAk+2kIKFh9JlSzLYVmOilEpR1Kh/iUbQFiNQTSbzWJrbYJximOJ", - "cSaulpVRoqh4K8JhjprIVJWqFlCpQNG51roYj8cLjJcGf5RMZWC1TYw1o2LxcEmy", - "0jeEo3ZFWVHIx0ji4eeKHFOx8l4sVVVZnBE6tWLHq7xO7FY86YpPeVjeo5y61tlR", - "JyhXEOQhF/lw6BGWixHvUWXVTpdgyUMu8q1h/ZJbqQhdiLsESx4FLvL9gcV6q3Cs", - "0liq2IHuBHjItYIV3rMvJnrYrkrdK9sr24EO9NO4AyI+i/CilOXbTi1xeXXFTyAS", - "GSOfzs42XmM+v5fJ5JvP29/fl8PDw43nhCbUpuzFxYXs7OxKmqZb1WQGkc/P80K+", - "T6dbnROaVJuyfPY+Pj7aup7h66HP/1Uu5O7u59bnhSTWpmxIEU3l9rBNdbrp6/TK", - "Nt3xpq7XK9tUp5u+Tm2/s/jYJdfX12LwBHVycrKRK89zmeJhYnZ7K3Fcz3e/2mDP", - "z7/waZEf8zaC+gSkKa3l4OBA3uztbXdOYFZtsKcfToNKSZNUPp6GnRN0AST3C1Ro", - "x9qS3yvbFqVC6+yVDe1YW/J7ZduiVGidvbKhHWtLfq9sW5QKrdMri9cxB6OFhQmO", - "TrDuBHjIRT5CEZZj0i7xOkYnWGeCPOQiHqC8lc/R60cLnNPuvjOkns7dk4t8/Jfv", - "s46mRlWqQiudxebVV3gAj7C9hXsmgZeztnfe/91YODEr3IoF/JY/sE2gbGaVLci3", - "hh0tRtWNvsm16JmNcOs6N9dW72LP7yOtWbEhjAUkZ+icoJ5HbE6+NSxMjKWe6cKb", - "GkUWgMwiFbXSlRpFkXelUlF4F70rVd7Bd4oZ/LL8xiDmtPV2Nwyf2zOlTfHERY7i", - "Haa1+w2+iFqx0aIgvgAAAABJRU5ErkJggg==", -].join(""); - -interface Props { - focusCell: (payload: any) => void; - id: string; - moveCell: (payload: any) => void; - children: React.ReactNode; - contentRef: ContentRef; -} - -interface DnDSourceProps { - connectDragPreview: ConnectDragPreview; - connectDragSource: ConnectDragSource; - isDragging: boolean; -} - -interface DnDTargetProps { - connectDropTarget: ConnectDropTarget; - isOver: boolean; -} - -interface State { - hoverUpperHalf: boolean; -} - -const cellSource = { - beginDrag(props: Props) { - return { - id: props.id, - }; - }, -}; - -const DragHandle = styled.div.attrs({ - role: "presentation", -})` - position: absolute; - z-index: 200; - width: var(--prompt-width, 50px); - height: 20px; - cursor: move; -`; - -interface DragAreaProps { - isDragging: boolean; - isOver: boolean; - hoverUpperHalf: boolean; -} - -const DragArea = styled.div.attrs((props) => ({ - style: { - opacity: props.isDragging ? 0.25 : 1, - borderTop: props.isOver && props.hoverUpperHalf ? "3px lightgray solid" : "3px transparent solid", - borderBottom: props.isOver && !props.hoverUpperHalf ? "3px lightgray solid" : "3px transparent solid", - }, -}))` - padding: 10px; - margin-top: -15px; -` as StyledComponent<"div", any, DragAreaProps, never>; // Somehow setting the type on `attrs` isn't propagating properly; - -// This is the div that DragHandle's absolute position will anchor -const DragHandleAnchor = styled.div` - position: relative; -`; - -export function isDragUpper(props: Props, monitor: DropTargetMonitor, el: HTMLElement): boolean { - const hoverBoundingRect = el.getBoundingClientRect(); - const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; - - const clientOffset = monitor.getClientOffset(); - const hoverClientY = clientOffset!.y - hoverBoundingRect.top; - - return hoverClientY < hoverMiddleY; -} - -export const cellTarget = { - drop(props: Props, monitor: DropTargetMonitor, component: any): void { - if (monitor) { - const hoverUpperHalf = isDragUpper(props, monitor, component.el); - const item: Props = monitor.getItem(); - // DropTargetSpec monitor definition could be undefined. we'll need a check for monitor in order to pass validation. - props.moveCell({ - id: item.id, - destinationId: props.id, - above: hoverUpperHalf, - contentRef: props.contentRef, - }); - } - }, - - hover(props: Props, monitor: DropTargetMonitor, component: any): void { - if (monitor) { - component.setState({ - hoverUpperHalf: isDragUpper(props, monitor, component.el), - }); - } - }, -}; - -function collectSource( - connect: DragSourceConnector, - monitor: DragSourceMonitor, -): { - connectDragSource: ConnectDragSource; - isDragging: boolean; - connectDragPreview: ConnectDragPreview; -} { - return { - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging(), - connectDragPreview: connect.dragPreview(), - }; -} - -function collectTarget( - connect: DropTargetConnector, - monitor: DropTargetMonitor, -): { - connectDropTarget: ConnectDropTarget; - isOver: boolean; -} { - return { - connectDropTarget: connect.dropTarget(), - isOver: monitor.isOver(), - }; -} - -export class DraggableCellView extends React.Component { - el?: HTMLDivElement | null; - - state = { - hoverUpperHalf: true, - }; - - componentDidMount(): void { - const connectDragPreview = this.props.connectDragPreview; - const img = new (window as any).Image(); - - img.src = cellDragPreviewImage; - - img.onload = /*dragImageLoaded*/ () => { - connectDragPreview(img); - }; - } - - selectCell = () => { - const { focusCell, id, contentRef } = this.props; - focusCell({ id, contentRef }); - }; - - render() { - return this.props.connectDropTarget( - // Sadly connectDropTarget _has_ to take a React element for a DOM element (no styled-divs) -
- { - this.el = el; - }} - > - - {this.props.connectDragSource( - // Same thing with connectDragSource... It also needs a React Element that matches a DOM element -
- -
, - )} - {this.props.children} -
-
-
, - ); - } -} - -const source = DragSource("CELL", cellSource, collectSource); -const target = DropTarget("CELL", cellTarget, collectTarget); - -export const makeMapDispatchToProps = (initialDispatch: Dispatch) => { - const mapDispatchToProps = (dispatch: Dispatch) => ({ - moveCell: (payload: actions.MoveCell["payload"]) => dispatch(actions.moveCell(payload)), - focusCell: (payload: actions.FocusCell["payload"]) => dispatch(actions.focusCell(payload)), - }); - return mapDispatchToProps; -}; - -export default connect(null, makeMapDispatchToProps)(source(target(DraggableCellView))); diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx b/src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx deleted file mode 100644 index d45e79b01c..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* eslint jsx-a11y/no-static-element-interactions: 0 */ -/* eslint jsx-a11y/click-events-have-key-events: 0 */ - -import { actions, AppState, ContentRef, selectors } from "@nteract/core"; -import React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; - -interface ComponentProps { - id: string; - contentRef: ContentRef; - children: React.ReactNode; -} - -interface StateProps { - focused: boolean; -} - -interface DispatchProps { - selectCell: () => void; -} - -type Props = ComponentProps & DispatchProps & StateProps; - -export class HijackScroll extends React.Component { - el: HTMLDivElement | null = null; - - scrollIntoViewIfNeeded(prevFocused?: boolean): void { - // Check if the element is being hovered over. - const hovered = this.el && this.el.parentElement && this.el.parentElement.querySelector(":hover") === this.el; - - if ( - this.props.focused && - prevFocused !== this.props.focused && - // Don't scroll into view if already hovered over, this prevents - // accidentally selecting text within the codemirror area - !hovered - ) { - if (this.el && "scrollIntoViewIfNeeded" in this.el) { - // This is only valid in Chrome, WebKit - (this.el as any).scrollIntoViewIfNeeded(); - } else if (this.el) { - // Make a best guess effort for older platforms - this.el.scrollIntoView(); - } - } - } - - componentDidUpdate(prevProps: Props) { - this.scrollIntoViewIfNeeded(prevProps.focused); - } - - componentDidMount(): void { - this.scrollIntoViewIfNeeded(); - } - - render() { - return ( -
{ - this.el = el; - }} - > - {this.props.children} -
- ); - } -} - -const makeMapStateToProps = (_initialState: AppState, ownProps: ComponentProps) => { - const mapStateToProps = (state: AppState) => { - const { id, contentRef } = ownProps; - const model = selectors.model(state, { contentRef }); - let focused = false; - - if (model && model.type === "notebook") { - focused = model.cellFocused === id; - } - - return { - focused, - }; - }; - return mapStateToProps; -}; - -const makeMapDispatchToProps = (_initialDispatch: Dispatch, ownProps: ComponentProps) => { - const mapDispatchToProps = (dispatch: Dispatch) => ({ - selectCell: () => dispatch(actions.focusCell({ id: ownProps.id, contentRef: ownProps.contentRef })), - }); - return mapDispatchToProps; -}; - -export default connect(makeMapStateToProps, makeMapDispatchToProps)(HijackScroll); diff --git a/src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/index.tsx b/src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/index.tsx deleted file mode 100644 index 98e79d23e3..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/decorators/kbd-shortcuts/index.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { CellId } from "@nteract/commutable"; -import { actions, AppState, ContentRef, selectors } from "@nteract/core"; -import Immutable from "immutable"; -import React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { NotebookUtil } from "../../../NotebookUtil"; - -interface ComponentProps { - contentRef: ContentRef; - children: React.ReactNode; -} - -interface StateProps { - cellMap: Immutable.Map; - cellOrder: Immutable.List; - focusedCell?: string | null; - isNotebookUntrusted: boolean; -} - -interface DispatchProps { - executeFocusedCell: (payload: { contentRef: ContentRef }) => void; - focusNextCell: (payload: { id?: CellId; createCellIfUndefined: boolean; contentRef: ContentRef }) => void; - focusNextCellEditor: (payload: { id?: CellId; contentRef: ContentRef }) => void; -} - -type Props = ComponentProps & StateProps & DispatchProps; - -export class KeyboardShortcuts extends React.Component { - constructor(props: Props) { - super(props); - this.keyDown = this.keyDown.bind(this); - } - - shouldComponentUpdate(nextProps: Props) { - const newContentRef = this.props.contentRef !== nextProps.contentRef; - const newFocusedCell = this.props.focusedCell !== nextProps.focusedCell; - const newCellOrder = this.props.cellOrder && this.props.cellOrder.size !== nextProps.cellOrder.size; - return newContentRef || newFocusedCell || newCellOrder; - } - - componentDidMount(): void { - document.addEventListener("keydown", this.keyDown); - } - - componentWillUnmount(): void { - document.removeEventListener("keydown", this.keyDown); - } - - keyDown(e: KeyboardEvent): void { - // If enter is not pressed, do nothing - if (e.key !== "Enter") { - return; - } - - const { - executeFocusedCell, - focusNextCell, - focusNextCellEditor, - contentRef, - cellOrder, - focusedCell, - cellMap, - isNotebookUntrusted, - } = this.props; - - if (isNotebookUntrusted) { - return; - } - - let ctrlKeyPressed = e.ctrlKey; - // Allow cmd + enter (macOS) to operate like ctrl + enter - if (process.platform === "darwin") { - ctrlKeyPressed = (e.metaKey || e.ctrlKey) && !(e.metaKey && e.ctrlKey); - } - - const shiftXORctrl = (e.shiftKey || ctrlKeyPressed) && !(e.shiftKey && ctrlKeyPressed); - if (!shiftXORctrl) { - return; - } - - e.preventDefault(); - - if (focusedCell) { - // NOTE: Order matters here because we need it to execute _before_ we - // focus the next cell - executeFocusedCell({ contentRef }); - - if (e.shiftKey) { - /** Get the next cell and check if it is a markdown cell. */ - const focusedCellIndex = cellOrder.indexOf(focusedCell); - const nextCellId = cellOrder.get(focusedCellIndex + 1); - const nextCell = nextCellId ? cellMap.get(nextCellId) : undefined; - - /** Always focus the next cell. */ - focusNextCell({ - id: undefined, - createCellIfUndefined: true, - contentRef, - }); - - /** Only focus the next editor if it is a code cell or a cell - * created at the bottom of the notebook. */ - if (nextCell === undefined || (nextCell && nextCell.get("cell_type") === "code")) { - focusNextCellEditor({ id: focusedCell, contentRef }); - } - } - } - } - - render() { - return {this.props.children}; - } -} - -export const makeMapStateToProps = (_state: AppState, ownProps: ComponentProps) => { - const { contentRef } = ownProps; - const mapStateToProps = (state: AppState) => { - const model = selectors.model(state, { contentRef }); - - let cellOrder = Immutable.List(); - let cellMap = Immutable.Map(); - let focusedCell; - - if (model && model.type === "notebook") { - cellOrder = model.notebook.cellOrder; - cellMap = selectors.notebook.cellMap(model); - focusedCell = selectors.notebook.cellFocused(model); - } - - return { - cellOrder, - cellMap, - focusedCell, - isNotebookUntrusted: NotebookUtil.isNotebookUntrusted(state, contentRef), - }; - }; - return mapStateToProps; -}; - -export const mapDispatchToProps = (dispatch: Dispatch) => ({ - executeFocusedCell: (payload: { contentRef: ContentRef }) => dispatch(actions.executeFocusedCell(payload)), - focusNextCell: (payload: { id?: CellId; createCellIfUndefined: boolean; contentRef: ContentRef }) => - dispatch(actions.focusNextCell(payload)), - focusNextCellEditor: (payload: { id?: CellId; contentRef: ContentRef }) => - dispatch(actions.focusNextCellEditor(payload)), -}); - -export default connect(makeMapStateToProps, mapDispatchToProps)(KeyboardShortcuts); diff --git a/src/Explorer/Notebook/NotebookRenderer/default.css b/src/Explorer/Notebook/NotebookRenderer/default.css deleted file mode 100644 index a406b66dea..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/default.css +++ /dev/null @@ -1,181 +0,0 @@ -.nteract-cell-prompt { - font-family: monospace; - color: var(--theme-cell-prompt-fg, black); - background-color: var(--theme-cell-prompt-bg, #fafafa); -} - -.nteract-cell-pagers { - background-color: var(--theme-pager-bg, #fafafa); -} - -.nteract-cell-outputs a { - color: var(--link-color-unvisited, blue); -} - -.nteract-cell-outputs a:visited { - color: var(--link-color-visited, blue); -} - -.nteract-cell-outputs code { - font-family: "Source Code Pro", monospace; -} - -.nteract-cell-outputs kbd { - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff inset; - background-color: #f7f7f7; -} - -.nteract-cell-outputs th, - .nteract-cell-outputs td, - /* for legacy output handling */ - .nteract-cell-outputs .th, - .nteract-cell-outputs .td { - border: 1px solid var(--theme-app-border, #cbcbcb); -} - -.nteract-cell-outputs blockquote { - padding: 0.75em 0.5em 0.75em 1em; -} - -.nteract-cell-outputs blockquote::before { - display: block; - height: 0; - margin-left: -0.95em; -} - -.nteract-cell-input .nteract-cell-source { - background-color: var(--theme-cell-input-bg, #fafafa); -} - -.nteract-cells { - font-family: "Source Sans Pro", Helvetica Neue, Helvetica, sans-serif; - font-size: 16px; - background-color: var(--theme-app-bg); - color: var(--theme-app-fg); -} - -.nteract-cell { - background: var(--theme-cell-bg, white); -} - -.nteract-cell-container.selected .nteract-cell-prompt { - background-color: var(--theme-cell-prompt-bg-focus, hsl(0, 0%, 90%)); - color: var(--theme-cell-prompt-fg-focus, hsl(0, 0%, 51%)); -} - -.nteract-cell-container:hover:not(.selected) .nteract-cell-prompt, -.nteract-cell-container:active:not(.selected) .nteract-cell-prompt { - background-color: var(--theme-cell-prompt-bg-hover, hsl(0, 0%, 94%)); - color: var(--theme-cell-prompt-fg-hover, hsl(0, 0%, 15%)); -} - -.nteract-cell-outputs { - background-color: var(--theme-cell-output-bg); -} - -.nteract-cell-container.selected .nteract-cell-outputs { - background-color: var(--theme-cell-output-bg-focus); -} - -.nteract-cell-container:hover:not(.selected) .nteract-cell-outputs, -.nteract-cell-container:active:not(.selected) .nteract-cell-outputs { - background-color: var(--theme-cell-output-bg-hover); -} - -.nteract-cell:focus .nteract-cell-prompt { - background-color: var(--theme-cell-prompt-bg-focus, hsl(0, 0%, 90%)); - color: var(--theme-cell-prompt-fg-focus, hsl(0, 0%, 51%)); -} - -@media print { - /* make sure all cells look the same in print regarless of focus */ - .nteract-cell-container .nteract-cell-prompt, - .nteract-cell-container.selected .nteract-cell-prompt, - .nteract-cell-container:focus .nteract-cell-prompt, - .nteract-cell-container:hover:not(.selected) .nteract-cell-prompt { - background-color: var(--theme-cell-prompt-bg, white); - color: var(--theme-cell-prompt-fg, black); - } -} - -.nteract-cell-toolbar { - opacity: 0.4; - transition: opacity 0.4s; -} - -.nteract-cell-container:not(.selected) .nteract-cell-toolbar { - display: none; -} - -.nteract-cell-container:hover:not(.selected) .nteract-cell-toolbar, -.nteract-cell-container.selected .nteract-cell-toolbar { - display: inline-block; -} - -.nteract-cell-toolbar > div { - display: inline-block; -} - -.nteract-cell-toolbar:hover { - opacity: 1; -} - -@media print { - .nteract-cell-toolbar { - display: none; - } -} - -.nteract-cell-toolbar button { - display: inline-block; - - width: 22px; - height: 20px; - padding: 0px 4px; - - text-align: center; - - border: none; - outline: none; - background: none; -} - -.nteract-cell-toolbar span { - font-size: 15px; - line-height: 1; - color: var(--theme-cell-toolbar-fg); -} - -.nteract-cell-toolbar button span:hover { - color: var(--theme-cell-toolbar-fg-hover); -} - -.nteract-cell-toolbar .octicon { - transition: color 0.5s; -} - -.nteract-cell-toolbar span.spacer { - display: inline-block; - vertical-align: middle; - margin: 1px 5px 3px 5px; - height: 11px; -} - -.nteract-cell-toolbar { - z-index: 9; - position: sticky; /* keep visible with large code cells that need scrolling */ - float: right; - top: 0; - right: 0; - height: 34px; - margin: 0 0 0 -100%; /* allow code cell to completely overlap (underlap?) */ - padding: 0 0 0 50px; /* give users extra room to move their mouse to the - toolbar without causing the cell to go out of - focus/hide the toolbar before they get there */ -} - -.nteract-cell.hidden .nteract-cell-toolbar { - display: none; -} diff --git a/src/Explorer/Notebook/NotebookRenderer/markdown-cell.tsx b/src/Explorer/Notebook/NotebookRenderer/markdown-cell.tsx deleted file mode 100644 index 36cf18564c..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/markdown-cell.tsx +++ /dev/null @@ -1,160 +0,0 @@ -// TODO The purpose of importing this source file https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/cells/markdown-cell.tsx -// into our source is to be able to overwrite the version of react-markdown which has this fix ("escape html to false") -// https://github.com/nteract/markdown/commit/e19c7cc590a4379fc507f67a7b4228363b9d8631 without having to upgrade -// @nteract/stateful-component which causes runtime issues. - -import { ImmutableCell } from "@nteract/commutable/src"; -import { actions, AppState, ContentRef, selectors } from "@nteract/core"; -import { MarkdownPreviewer } from "@nteract/markdown"; -import { defineConfigOption } from "@nteract/mythic-configuration"; -import { Source as BareSource } from "@nteract/presentational-components"; -import Editor, { EditorSlots } from "@nteract/stateful-components/lib/inputs/editor"; -import React from "react"; -import { ReactMarkdownProps } from "react-markdown"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import styled from "styled-components"; - -const { selector: markdownConfig } = defineConfigOption({ - key: "markdownOptions", - label: "Markdown Editor Options", - defaultValue: {}, -}); - -interface NamedMDCellSlots { - editor?: EditorSlots; - toolbar?: () => JSX.Element; -} - -interface ComponentProps { - id: string; - contentRef: ContentRef; - cell_type?: "markdown"; - children?: NamedMDCellSlots; -} - -interface StateProps { - isCellFocused: boolean; - isEditorFocused: boolean; - cell?: ImmutableCell; - markdownOptions: ReactMarkdownProps; -} - -interface DispatchProps { - focusAboveCell: () => void; - focusBelowCell: () => void; - focusEditor: () => void; - unfocusEditor: () => void; -} - -// Add missing style to make the editor show https://github.com/nteract/nteract/commit/7fa580011578350e56deac81359f6294fdfcad20#diff-07829a1908e4bf98d4420f868a1c6f890b95d77297b9805c9590d2dba11e80ce -export const Source = styled(BareSource)` - width: 100%; - width: -webkit-fill-available; - width: -moz-available; -`; -export class PureMarkdownCell extends React.Component { - render() { - const { contentRef, id, cell, children } = this.props; - - const { isEditorFocused, isCellFocused, markdownOptions } = this.props; - - const { focusAboveCell, focusBelowCell, focusEditor, unfocusEditor } = this.props; - - /** - * We don't set the editor slots as defaults to support dynamic imports - * Users can continue to add the editorSlots as children - */ - const editor = children?.editor; - const toolbar = children?.toolbar; - - const source = cell ? cell.get("source", "") : ""; - - return ( -
-
-
{toolbar && toolbar()}
-
- - - - {editor} - - - -
-
-
- ); - } -} - -export const makeMapStateToProps = ( - initialState: AppState, - ownProps: ComponentProps, -): ((state: AppState) => StateProps) => { - const { id, contentRef } = ownProps; - const mapStateToProps = (state: AppState): StateProps => { - const model = selectors.model(state, { contentRef }); - let isCellFocused = false; - let isEditorFocused = false; - let cell; - - if (model && model.type === "notebook") { - cell = selectors.notebook.cellById(model, { id }); - isCellFocused = model.cellFocused === id; - isEditorFocused = model.editorFocused === id; - } - - const markdownOptionsDefaults = { - linkTarget: "_blank", - }; - const currentMarkdownOptions = markdownConfig(state); - - const markdownOptions = Object.assign({}, markdownOptionsDefaults, currentMarkdownOptions); - - return { - cell, - isCellFocused, - isEditorFocused, - markdownOptions, - }; - }; - - return mapStateToProps; -}; - -const makeMapDispatchToProps = ( - initialDispatch: Dispatch, - ownProps: ComponentProps, -): ((dispatch: Dispatch) => DispatchProps) => { - const { id, contentRef } = ownProps; - - const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ - focusAboveCell: () => { - dispatch(actions.focusPreviousCell({ id, contentRef })); - dispatch(actions.focusPreviousCellEditor({ id, contentRef })); - }, - focusBelowCell: () => { - dispatch(actions.focusNextCell({ id, createCellIfUndefined: true, contentRef })); - dispatch(actions.focusNextCellEditor({ id, contentRef })); - }, - focusEditor: () => dispatch(actions.focusCellEditor({ id, contentRef })), - unfocusEditor: () => dispatch(actions.focusCellEditor({ id: undefined, contentRef })), - }); - - return mapDispatchToProps; -}; - -const MarkdownCell = connect(makeMapStateToProps, makeMapDispatchToProps)(PureMarkdownCell); - -export default MarkdownCell; diff --git a/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx b/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx deleted file mode 100644 index fb6555ba9d..0000000000 --- a/src/Explorer/Notebook/NotebookRenderer/outputs/SandboxOutputs.tsx +++ /dev/null @@ -1,213 +0,0 @@ -import { JSONObject } from "@nteract/commutable"; -import { outputToJS } from "@nteract/commutable/lib/v4"; -import { actions, AppState, ContentRef, selectors } from "@nteract/core"; -import IframeResizer from "iframe-resizer-react"; -import Immutable from "immutable"; -import postRobot from "post-robot"; -import React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { CellOutputViewerProps, SnapshotResponse } from "../../../../CellOutputViewer/CellOutputViewer"; -import * as cdbActions from "../../NotebookComponent/actions"; -import { CdbAppState, SnapshotFragment, SnapshotRequest } from "../../NotebookComponent/types"; - -// Adapted from https://github.com/nteract/nteract/blob/main/packages/stateful-components/src/outputs/index.tsx -// to add support for sandboxing using