├── .codeclimate.json ├── .eslintrc.js ├── .github ├── CODEOWNERS └── workflows │ ├── changelog-config.json │ ├── continuous-integration.yml │ ├── create-release-pr.yml │ ├── cross-platform-test.yml │ ├── dvc-cli-output-test.yml │ ├── end-to-end-test.yml │ └── publish.yml ├── .gitignore ├── .gitmodules ├── .husky ├── .gitignore └── pre-commit ├── .lintstagedrc.js ├── .prettierignore ├── .prettierrc.json ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── extension ├── .eslintrc.js ├── .gitignore ├── .lintstagedrc.js ├── .prettierignore ├── .vscodeignore ├── docs │ ├── dvc.png │ ├── overview.gif │ └── walkthroughs.png ├── jest.config.js ├── package.json ├── resources │ ├── dark │ │ ├── beaker.svg │ │ ├── checkbox-checked.svg │ │ ├── checkbox-empty.svg │ │ ├── checkbox-indeterminate.svg │ │ ├── clock.svg │ │ ├── dvc-color.svg │ │ ├── queue-experiment.svg │ │ └── scatter-graph.svg │ ├── dvc-monochrome.svg │ ├── experiments │ │ ├── circle-filled-#13adc7.svg │ │ ├── circle-filled-#4299e1.svg │ │ ├── circle-filled-#48bb78.svg │ │ ├── circle-filled-#945dd6.svg │ │ ├── circle-filled-#ed8936.svg │ │ ├── circle-filled-#f46837.svg │ │ ├── circle-filled-#f56565.svg │ │ ├── loading-spin-#13adc7.svg │ │ ├── loading-spin-#4299e1.svg │ │ ├── loading-spin-#48bb78.svg │ │ ├── loading-spin-#945dd6.svg │ │ ├── loading-spin-#ed8936.svg │ │ ├── loading-spin-#f46837.svg │ │ └── loading-spin-#f56565.svg │ ├── light │ │ ├── beaker.svg │ │ ├── checkbox-checked.svg │ │ ├── checkbox-empty.svg │ │ ├── checkbox-indeterminate.svg │ │ ├── clock.svg │ │ ├── dvc-color.svg │ │ ├── queue-experiment.svg │ │ └── scatter-graph.svg │ ├── plots │ │ ├── circle.svg │ │ ├── diamond.svg │ │ ├── square.svg │ │ ├── stroke-dash-1-0.svg │ │ ├── stroke-dash-1-1.svg │ │ ├── stroke-dash-2-1.svg │ │ ├── stroke-dash-4-2.svg │ │ ├── stroke-dash-4-4.svg │ │ ├── stroke-dash-8-4.svg │ │ ├── stroke-dash-8-8.svg │ │ └── triangle.svg │ └── walkthrough │ │ ├── command-palette.md │ │ ├── dvc-tracked-files.md │ │ ├── experiments-table.md │ │ ├── images │ │ ├── available-commands-command-palette.png │ │ ├── click-the-circle-beside-experiment-name.png │ │ ├── dvc-tracked-explorer.png │ │ ├── dvc-tracked-scm.png │ │ ├── experiments-table-context-menus.png │ │ ├── experiments-table.png │ │ ├── install-dvc-setup-wizard.png │ │ ├── live-plots-updates.gif │ │ ├── plots-custom.png │ │ ├── plots-data-series.png │ │ ├── plots-images.png │ │ ├── plots-view-icon.png │ │ ├── run-experiments-command-palette.png │ │ ├── run-experiments-params-buttons.png │ │ ├── run-experiments-table-buttons.png │ │ ├── setup-view.png │ │ ├── view-container-support.png │ │ ├── view-container-welcome.png │ │ └── view-container.png │ │ ├── live-plots.md │ │ ├── more-resources.md │ │ ├── plots.md │ │ ├── run-experiments.md │ │ ├── setup.md │ │ ├── troubleshoot.md │ │ └── view-container.md ├── scripts │ ├── coverIntegrationTests.js │ └── ensureYarnLock.js ├── snippets │ ├── dvc-yaml.code-snippets │ └── dvclive.code-snippets ├── src │ ├── __mocks__ │ │ ├── @hediet │ │ │ └── std │ │ │ │ └── disposable.ts │ │ ├── @vscode │ │ │ └── extension-telemetry.ts │ │ └── vscode.ts │ ├── class │ │ ├── deferred.test.ts │ │ ├── deferred.ts │ │ └── dispose.ts │ ├── cli │ │ ├── command.ts │ │ ├── constants.ts │ │ ├── dvc │ │ │ ├── actions.ts │ │ │ ├── config.test.ts │ │ │ ├── config.ts │ │ │ ├── constants.ts │ │ │ ├── contract.ts │ │ │ ├── cwd.ts │ │ │ ├── discovery.ts │ │ │ ├── executor.test.ts │ │ │ ├── executor.ts │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── options.test.ts │ │ │ ├── options.ts │ │ │ ├── output.ts │ │ │ ├── reader.test.ts │ │ │ ├── reader.ts │ │ │ ├── runner.ts │ │ │ ├── version.test.ts │ │ │ ├── version.ts │ │ │ └── viewer.ts │ │ ├── error.ts │ │ ├── git │ │ │ ├── constants.ts │ │ │ ├── executor.ts │ │ │ ├── index.ts │ │ │ ├── options.ts │ │ │ ├── reader.test.ts │ │ │ └── reader.ts │ │ ├── index.ts │ │ ├── util.ts │ │ └── viewable.ts │ ├── commands │ │ ├── external.ts │ │ ├── internal.test.ts │ │ ├── internal.ts │ │ └── util.ts │ ├── common │ │ ├── logger.test.ts │ │ └── logger.ts │ ├── config.ts │ ├── context.ts │ ├── data │ │ └── index.ts │ ├── env.ts │ ├── experiments │ │ ├── columns │ │ │ ├── collect │ │ │ │ ├── deps.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── metricsAndParams.ts │ │ │ │ ├── order.ts │ │ │ │ └── util.ts │ │ │ ├── constants.ts │ │ │ ├── extract.test.ts │ │ │ ├── extract.ts │ │ │ ├── like.ts │ │ │ ├── model.test.ts │ │ │ ├── model.ts │ │ │ ├── paths.test.ts │ │ │ ├── paths.ts │ │ │ ├── quickPick.test.ts │ │ │ ├── quickPick.ts │ │ │ ├── tree.test.ts │ │ │ ├── tree.ts │ │ │ ├── util.test.ts │ │ │ └── util.ts │ │ ├── commands │ │ │ ├── index.ts │ │ │ └── register.ts │ │ ├── context.ts │ │ ├── data │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ ├── constants.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── model │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ ├── decorationProvider.ts │ │ │ ├── filterBy │ │ │ │ ├── collect.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── date.test.ts │ │ │ │ ├── date.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── quickPick.test.ts │ │ │ │ ├── quickPick.ts │ │ │ │ ├── tree.test.ts │ │ │ │ └── tree.ts │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── modify │ │ │ │ ├── collect.test.ts │ │ │ │ ├── collect.ts │ │ │ │ ├── quickPick.test.ts │ │ │ │ └── quickPick.ts │ │ │ ├── quickPick.test.ts │ │ │ ├── quickPick.ts │ │ │ ├── sortBy │ │ │ │ ├── constants.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── index.ts │ │ │ │ ├── quickPick.test.ts │ │ │ │ ├── quickPick.ts │ │ │ │ ├── tree.test.ts │ │ │ │ └── tree.ts │ │ │ ├── status │ │ │ │ ├── collect.test.ts │ │ │ │ ├── collect.ts │ │ │ │ ├── colors.ts │ │ │ │ ├── index.test.ts │ │ │ │ └── index.ts │ │ │ ├── tree.test.ts │ │ │ ├── tree.ts │ │ │ └── util.ts │ │ ├── processExecution │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ └── index.ts │ │ ├── studio.ts │ │ ├── webview │ │ │ ├── contract.ts │ │ │ └── messages.ts │ │ ├── workspace.test.ts │ │ └── workspace.ts │ ├── extension.ts │ ├── extensions │ │ ├── git.ts │ │ ├── python.test.ts │ │ └── python.ts │ ├── fileSystem │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── path.ts │ │ ├── relativePattern.ts │ │ ├── util.ts │ │ ├── watcher.test.ts │ │ ├── watcher.ts │ │ ├── workspace.test.ts │ │ └── workspace.ts │ ├── interfaces.ts │ ├── languageClient │ │ └── index.ts │ ├── path │ │ └── selection │ │ │ ├── model.ts │ │ │ ├── quickPick.test.ts │ │ │ ├── quickPick.ts │ │ │ └── tree.ts │ ├── persistence │ │ ├── constants.ts │ │ ├── model.ts │ │ ├── register.ts │ │ ├── util.test.ts │ │ └── util.ts │ ├── pipeline │ │ ├── context.ts │ │ ├── data.ts │ │ ├── index.ts │ │ ├── model │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ └── index.ts │ │ ├── quickPick.test.ts │ │ ├── quickPick.ts │ │ ├── register.ts │ │ └── workspace.ts │ ├── plots │ │ ├── commands │ │ │ └── register.ts │ │ ├── data │ │ │ ├── collect.ts │ │ │ └── index.ts │ │ ├── errors │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ └── model.ts │ │ ├── index.ts │ │ ├── model │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ ├── custom.test.ts │ │ │ ├── custom.ts │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── quickPick.test.ts │ │ │ ├── quickPick.ts │ │ │ └── util.ts │ │ ├── multiSource │ │ │ └── collect.ts │ │ ├── paths │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ ├── model.test.ts │ │ │ ├── model.ts │ │ │ ├── tree.test.ts │ │ │ └── tree.ts │ │ ├── quickPick.test.ts │ │ ├── quickPick.ts │ │ ├── util.test.ts │ │ ├── util.ts │ │ ├── vega │ │ │ ├── util.test.ts │ │ │ └── util.ts │ │ ├── webview │ │ │ ├── contract.ts │ │ │ └── messages.ts │ │ └── workspace.ts │ ├── process │ │ ├── execution.test.ts │ │ ├── execution.ts │ │ └── manager.ts │ ├── python │ │ ├── index.test.ts │ │ ├── index.ts │ │ └── path.ts │ ├── repository │ │ ├── commands │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ └── register.ts │ │ ├── constants.ts │ │ ├── data │ │ │ ├── index.test.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── model │ │ │ ├── collect.test.ts │ │ │ ├── collect.ts │ │ │ ├── error.ts │ │ │ ├── index.test.ts │ │ │ ├── index.ts │ │ │ ├── tree.test.ts │ │ │ └── tree.ts │ │ ├── sourceControlManagement │ │ │ ├── decorationProvider.test.ts │ │ │ ├── decorationProvider.ts │ │ │ └── index.ts │ │ └── workspace.ts │ ├── resourceLocator.test.ts │ ├── resourceLocator.ts │ ├── setup │ │ ├── autoInstall.ts │ │ ├── collect.test.ts │ │ ├── collect.ts │ │ ├── commands │ │ │ ├── index.ts │ │ │ └── register.ts │ │ ├── index.ts │ │ ├── inputBox.test.ts │ │ ├── inputBox.ts │ │ ├── quickPick.test.ts │ │ ├── quickPick.ts │ │ ├── runner.test.ts │ │ ├── runner.ts │ │ ├── studio.ts │ │ ├── util.test.ts │ │ ├── util.ts │ │ └── webview │ │ │ ├── contract.ts │ │ │ └── messages.ts │ ├── status.ts │ ├── telemetry │ │ ├── collect.ts │ │ ├── constants.ts │ │ ├── index.test.ts │ │ ├── index.ts │ │ ├── uuid.test.ts │ │ └── uuid.ts │ ├── test │ │ ├── cli │ │ │ ├── constants.ts │ │ │ ├── dataStatus.test.ts │ │ │ ├── expShow.test.ts │ │ │ ├── index.ts │ │ │ ├── plotsDiff.test.ts │ │ │ └── util.ts │ │ ├── e2e │ │ │ ├── extension.test.ts │ │ │ ├── package.json │ │ │ ├── pageObjects │ │ │ │ ├── baseWebview.ts │ │ │ │ ├── experimentsWebview.ts │ │ │ │ ├── locators.ts │ │ │ │ └── plotsWebview.ts │ │ │ ├── tsconfig.json │ │ │ ├── util.ts │ │ │ └── wdio.conf.ts │ │ ├── fixtures │ │ │ ├── expShow │ │ │ │ ├── base │ │ │ │ │ ├── columns.ts │ │ │ │ │ ├── customPlots.ts │ │ │ │ │ ├── gitLog.ts │ │ │ │ │ ├── noErrors.ts │ │ │ │ │ ├── output.ts │ │ │ │ │ ├── remoteExpRefs.ts │ │ │ │ │ ├── rowOrder.ts │ │ │ │ │ ├── rows.ts │ │ │ │ │ ├── tableData.ts │ │ │ │ │ └── workspaceChanges.ts │ │ │ │ ├── dataTypes │ │ │ │ │ ├── columns.ts │ │ │ │ │ ├── output.ts │ │ │ │ │ ├── rows.ts │ │ │ │ │ └── tableData.ts │ │ │ │ ├── deeplyNested │ │ │ │ │ ├── columns.ts │ │ │ │ │ ├── maxHeight.ts │ │ │ │ │ ├── output.ts │ │ │ │ │ ├── rows.ts │ │ │ │ │ └── tableData.ts │ │ │ │ ├── sorted │ │ │ │ │ ├── columns.ts │ │ │ │ │ ├── rows.ts │ │ │ │ │ └── tableData.ts │ │ │ │ ├── survival │ │ │ │ │ ├── columns.ts │ │ │ │ │ ├── output.ts │ │ │ │ │ ├── rows.ts │ │ │ │ │ └── tableData.ts │ │ │ │ └── uncommittedDeps │ │ │ │ │ └── output.ts │ │ │ └── plotsDiff │ │ │ │ ├── comparison │ │ │ │ ├── index.ts │ │ │ │ └── vscode.ts │ │ │ │ ├── index.ts │ │ │ │ ├── output │ │ │ │ ├── index.ts │ │ │ │ └── minimal.ts │ │ │ │ ├── revisions.ts │ │ │ │ ├── staticImages │ │ │ │ ├── 1ba7bcd_plots_acc.png │ │ │ │ ├── 1ba7bcd_plots_heatmap.png │ │ │ │ ├── 1ba7bcd_plots_loss.png │ │ │ │ ├── 42b8736_plots_acc.png │ │ │ │ ├── 42b8736_plots_heatmap.png │ │ │ │ ├── 42b8736_plots_loss.png │ │ │ │ ├── 4fb124a_plots_acc.png │ │ │ │ ├── 4fb124a_plots_heatmap.png │ │ │ │ ├── 4fb124a_plots_loss.png │ │ │ │ ├── 53c3851_plots_acc.png │ │ │ │ ├── 53c3851_plots_heatmap.png │ │ │ │ ├── 53c3851_plots_loss.png │ │ │ │ ├── image │ │ │ │ │ ├── 0.jpg │ │ │ │ │ ├── 1.jpg │ │ │ │ │ ├── 10.jpg │ │ │ │ │ ├── 11.jpg │ │ │ │ │ ├── 12.jpg │ │ │ │ │ ├── 13.jpg │ │ │ │ │ ├── 14.jpg │ │ │ │ │ ├── 2.jpg │ │ │ │ │ ├── 3.jpg │ │ │ │ │ ├── 4.jpg │ │ │ │ │ ├── 5.jpg │ │ │ │ │ ├── 6.jpg │ │ │ │ │ ├── 7.jpg │ │ │ │ │ ├── 8.jpg │ │ │ │ │ └── 9.jpg │ │ │ │ ├── workspace_plots_acc.png │ │ │ │ ├── workspace_plots_heatmap.png │ │ │ │ └── workspace_plots_loss.png │ │ │ │ ├── template │ │ │ │ ├── index.ts │ │ │ │ ├── virtualization.ts │ │ │ │ └── webview.ts │ │ │ │ ├── templates │ │ │ │ ├── barHorizontal.ts │ │ │ │ ├── confusion.ts │ │ │ │ ├── confusionNormalized.ts │ │ │ │ ├── linear.ts │ │ │ │ ├── scatter.ts │ │ │ │ └── smooth.ts │ │ │ │ └── vega.ts │ │ ├── runTest.ts │ │ ├── suite │ │ │ ├── cli │ │ │ │ ├── background.ts │ │ │ │ ├── child.ts │ │ │ │ ├── dvc │ │ │ │ │ ├── cwd.test.ts │ │ │ │ │ ├── runner.test.ts │ │ │ │ │ └── viewer.test.ts │ │ │ │ ├── failed.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── util.ts │ │ │ │ └── viewable.test.ts │ │ │ ├── config.test.ts │ │ │ ├── context.test.ts │ │ │ ├── experiments │ │ │ │ ├── columns │ │ │ │ │ └── tree.test.ts │ │ │ │ ├── data │ │ │ │ │ └── index.test.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── model │ │ │ │ │ ├── filterBy │ │ │ │ │ │ ├── tree.test.ts │ │ │ │ │ │ └── util.ts │ │ │ │ │ ├── sortBy │ │ │ │ │ │ └── tree.test.ts │ │ │ │ │ └── tree.test.ts │ │ │ │ ├── util.ts │ │ │ │ └── workspace.test.ts │ │ │ ├── extension.test.ts │ │ │ ├── fileSystem │ │ │ │ ├── index.test.ts │ │ │ │ └── watcher.test.ts │ │ │ ├── index.ts │ │ │ ├── pipeline │ │ │ │ ├── data.test.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── util.ts │ │ │ │ └── workspace.test.ts │ │ │ ├── plots │ │ │ │ ├── data │ │ │ │ │ └── index.test.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── paths │ │ │ │ │ └── tree.test.ts │ │ │ │ ├── util.ts │ │ │ │ └── workspace.test.ts │ │ │ ├── process │ │ │ │ └── manager.test.ts │ │ │ ├── repository │ │ │ │ ├── data │ │ │ │ │ └── index.test.ts │ │ │ │ ├── index.test.ts │ │ │ │ ├── model │ │ │ │ │ └── tree.test.ts │ │ │ │ ├── sourceControlManagement │ │ │ │ │ └── index.test.ts │ │ │ │ ├── util.ts │ │ │ │ └── workspace.test.ts │ │ │ ├── setup │ │ │ │ ├── autoInstall.test.ts │ │ │ │ ├── index.test.ts │ │ │ │ └── util.ts │ │ │ ├── status.test.ts │ │ │ ├── timeouts.ts │ │ │ ├── util.ts │ │ │ └── vscode │ │ │ │ ├── outputChannel.test.ts │ │ │ │ ├── pseudoTerminal.test.ts │ │ │ │ ├── quickPick.test.ts │ │ │ │ ├── recommend.test.ts │ │ │ │ └── walkthrough.test.ts │ │ └── util │ │ │ ├── experiments.ts │ │ │ ├── index.ts │ │ │ ├── jest │ │ │ └── index.ts │ │ │ ├── mocha │ │ │ └── index.ts │ │ │ └── path.ts │ ├── tree │ │ ├── decorationProvider │ │ │ ├── error.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── types.d.ts │ ├── util │ │ ├── appdirs.test.ts │ │ ├── appdirs.ts │ │ ├── array.test.ts │ │ ├── array.ts │ │ ├── date.test.ts │ │ ├── date.ts │ │ ├── disposable.test.ts │ │ ├── disposable.ts │ │ ├── env.ts │ │ ├── json.test.ts │ │ ├── json.ts │ │ ├── map.test.ts │ │ ├── map.ts │ │ ├── math.ts │ │ ├── number.test.ts │ │ ├── number.ts │ │ ├── object.test.ts │ │ ├── object.ts │ │ ├── stdout.test.ts │ │ ├── stdout.ts │ │ ├── string.ts │ │ ├── time.test.ts │ │ └── time.ts │ ├── vscode │ │ ├── clipboard.ts │ │ ├── commands.ts │ │ ├── config.ts │ │ ├── context.test.ts │ │ ├── context.ts │ │ ├── extensions.ts │ │ ├── external.test.ts │ │ ├── external.ts │ │ ├── inputBox.test.ts │ │ ├── inputBox.ts │ │ ├── markdownString.ts │ │ ├── mockModule.ts │ │ ├── modal.test.ts │ │ ├── modal.ts │ │ ├── outputChannel.ts │ │ ├── pseudoTerminal.ts │ │ ├── quickPick.test.ts │ │ ├── quickPick.ts │ │ ├── recommend.test.ts │ │ ├── recommend.ts │ │ ├── resourcePicker.test.ts │ │ ├── resourcePicker.ts │ │ ├── response.ts │ │ ├── title.ts │ │ ├── toast.test.ts │ │ ├── toast.ts │ │ ├── walkthrough.ts │ │ └── workspaceFolders.ts │ ├── webview │ │ ├── constants.ts │ │ ├── contract.ts │ │ ├── factory.ts │ │ ├── index.ts │ │ ├── repository.ts │ │ └── workspace.ts │ └── workspace │ │ ├── index.ts │ │ └── util.ts ├── tsconfig.json └── webpack.config.ts ├── jest.config.js ├── languageServer ├── .eslintrc.js ├── .lintstagedrc.js ├── .prettierignore ├── index.d.ts ├── index.js ├── jest.config.js ├── package.json ├── src │ ├── languageServer.ts │ ├── server.ts │ ├── test │ │ ├── definitions.test.ts │ │ ├── fixtures │ │ │ ├── examples │ │ │ │ ├── invalid │ │ │ │ │ └── index.ts │ │ │ │ └── valid │ │ │ │ │ └── index.ts │ │ │ ├── params │ │ │ │ └── index.ts │ │ │ └── python │ │ │ │ └── index.ts │ │ └── utils │ │ │ ├── openTheseFilesAndNotifyServer.ts │ │ │ ├── requestDefinitions.ts │ │ │ └── setup-test-connections.ts │ └── textDocument.ts ├── tsconfig.json └── webpack.config.ts ├── package.json ├── renovate.json ├── scripts ├── create-svgs.ts ├── install-frozen-lockfile.sh └── virtualenv-install.ts ├── turbo.json ├── webview ├── .eslintrc.js ├── .lintstagedrc.js ├── .prettierignore ├── .storybook │ ├── main.ts │ ├── preview.ts │ └── test-vscode-styles.scss ├── .stylelintrc.js ├── __mocks__ │ └── @vscode │ │ └── webview-ui-toolkit │ │ └── react.tsx ├── icons │ ├── clock.svg │ ├── codicons.mjs │ └── generate.mjs ├── index.d.ts ├── index.js ├── jest.config.js ├── package.json ├── rawLoaderTransformer.js ├── setup-tests.js ├── src │ ├── css.d.ts │ ├── experiments │ │ ├── components │ │ │ ├── AddStage.tsx │ │ │ ├── App.test.tsx │ │ │ ├── App.tsx │ │ │ ├── Experiments.tsx │ │ │ ├── ExperimentsTable.tsx │ │ │ ├── emptyState │ │ │ │ ├── AddColumns.tsx │ │ │ │ └── ErrorState.tsx │ │ │ ├── overflowHoverTooltip │ │ │ │ ├── OverflowHoverTooltip.tsx │ │ │ │ └── useIsFullyContained.ts │ │ │ └── table │ │ │ │ ├── CounterBadge.tsx │ │ │ │ ├── Indicator.tsx │ │ │ │ ├── Indicators.tsx │ │ │ │ ├── Table.test.tsx │ │ │ │ ├── Table.tsx │ │ │ │ ├── body │ │ │ │ ├── Cell.tsx │ │ │ │ ├── CellHintTooltip.tsx │ │ │ │ ├── CellRowAction.tsx │ │ │ │ ├── CellRowActions.tsx │ │ │ │ ├── ClickableTooltipContent.tsx │ │ │ │ ├── CopyStudioLink.tsx │ │ │ │ ├── ExperimentStatusIndicator.tsx │ │ │ │ ├── NestedRow.tsx │ │ │ │ ├── OnRemote.tsx │ │ │ │ ├── Progress.tsx │ │ │ │ ├── Row.tsx │ │ │ │ ├── RowContextMenu.tsx │ │ │ │ ├── RowExpansionButton.tsx │ │ │ │ ├── SortedTableContent.tsx │ │ │ │ ├── StubCell.tsx │ │ │ │ ├── TableBody.tsx │ │ │ │ ├── TableContent.test.tsx │ │ │ │ ├── TableContent.tsx │ │ │ │ ├── WorkspaceRowGroup.tsx │ │ │ │ ├── branchDivider │ │ │ │ │ ├── BranchDivider.tsx │ │ │ │ │ └── styles.module.scss │ │ │ │ ├── columns │ │ │ │ │ └── Columns.tsx │ │ │ │ ├── commits │ │ │ │ │ ├── CommitsButton.tsx │ │ │ │ │ ├── CommitsNavigation.tsx │ │ │ │ │ └── styles.module.scss │ │ │ │ └── util.ts │ │ │ │ ├── content │ │ │ │ ├── BranchCellContent.tsx │ │ │ │ ├── Cell.tsx │ │ │ │ ├── CellContent.tsx │ │ │ │ ├── CellTooltip.tsx │ │ │ │ ├── CommitCell.tsx │ │ │ │ ├── CommitCellContent.tsx │ │ │ │ ├── CommitTooltipContent.tsx │ │ │ │ ├── DateCellContent.tsx │ │ │ │ ├── ErrorCell.tsx │ │ │ │ ├── ExperimentCell.tsx │ │ │ │ ├── ExperimentHeader.tsx │ │ │ │ ├── Header.tsx │ │ │ │ ├── SortedTableHeader.tsx │ │ │ │ ├── TimestampHeader.tsx │ │ │ │ └── UndefinedCell.tsx │ │ │ │ ├── header │ │ │ │ ├── ColumnDragHandle.tsx │ │ │ │ ├── ColumnResizer.tsx │ │ │ │ ├── ContextMenuContent.tsx │ │ │ │ ├── MergeHeaderGroups.tsx │ │ │ │ ├── TableHead.tsx │ │ │ │ ├── TableHeaderCell.tsx │ │ │ │ ├── TableHeaderCellContents.tsx │ │ │ │ ├── WithExpColumnNeedsShadowUpdates.tsx │ │ │ │ └── util.ts │ │ │ │ └── styles.module.scss │ │ ├── fonts │ │ │ ├── Roboto-Light.ttf │ │ │ └── RobotoMono-Light.ttf │ │ ├── index.tsx │ │ ├── state │ │ │ ├── headerDropTargetSlice.ts │ │ │ ├── rowSelectionSlice.ts │ │ │ └── tableDataSlice.ts │ │ ├── store.ts │ │ └── util │ │ │ ├── columns.test.ts │ │ │ ├── columns.ts │ │ │ ├── interfaces.ts │ │ │ ├── messages.ts │ │ │ └── rows.ts │ ├── plots │ │ ├── components │ │ │ ├── App.test.tsx │ │ │ ├── App.tsx │ │ │ ├── DragAndDropGrid.tsx │ │ │ ├── DragAndDropPlot.tsx │ │ │ ├── DropTarget.tsx │ │ │ ├── ErrorsModal.tsx │ │ │ ├── Grid.tsx │ │ │ ├── LoadingSection.tsx │ │ │ ├── NormalGrid.tsx │ │ │ ├── PathHighlight.tsx │ │ │ ├── Plots.tsx │ │ │ ├── PlotsContainer.tsx │ │ │ ├── PlotsContent.tsx │ │ │ ├── SizeSliders.tsx │ │ │ ├── TooManyPlots.tsx │ │ │ ├── ZoomablePlot.tsx │ │ │ ├── ZoomedInPlot.tsx │ │ │ ├── comparisonTable │ │ │ │ ├── ComparisonTable.test.tsx │ │ │ │ ├── ComparisonTable.tsx │ │ │ │ ├── ComparisonTableHead.tsx │ │ │ │ ├── ComparisonTableHeader.test.tsx │ │ │ │ ├── ComparisonTableHeader.tsx │ │ │ │ ├── ComparisonTableRow.test.tsx │ │ │ │ ├── ComparisonTableRow.tsx │ │ │ │ ├── ComparisonTableRows.tsx │ │ │ │ ├── ComparisonTableWrapper.tsx │ │ │ │ ├── DropTarget.tsx │ │ │ │ ├── RowDropTarget.tsx │ │ │ │ ├── cell │ │ │ │ │ ├── ComparisonTableCell.tsx │ │ │ │ │ ├── ComparisonTableLoadingCell.tsx │ │ │ │ │ ├── ComparisonTableMissingCell.tsx │ │ │ │ │ └── ComparisonTableMultiCell.tsx │ │ │ │ ├── comparisonTableSlice.ts │ │ │ │ └── styles.module.scss │ │ │ ├── constants.ts │ │ │ ├── customPlots │ │ │ │ ├── CustomPlots.tsx │ │ │ │ ├── CustomPlotsGrid.tsx │ │ │ │ ├── CustomPlotsWrapper.tsx │ │ │ │ ├── NoPlotsAdded.tsx │ │ │ │ └── customPlotsSlice.ts │ │ │ ├── emptyState │ │ │ │ ├── AddPlots.tsx │ │ │ │ ├── EmptyState.tsx │ │ │ │ ├── ErrorState.tsx │ │ │ │ ├── GetStarted.tsx │ │ │ │ ├── WaitForPlotsInfo.tsx │ │ │ │ ├── Welcome.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── plotDataStore.ts │ │ │ ├── ribbon │ │ │ │ ├── Errors.tsx │ │ │ │ ├── RevisionIcon.tsx │ │ │ │ ├── Ribbon.tsx │ │ │ │ ├── RibbonBlock.tsx │ │ │ │ ├── RibbonBlockTooltip.tsx │ │ │ │ ├── ribbonSlice.ts │ │ │ │ └── styles.module.scss │ │ │ ├── styles.module.scss │ │ │ ├── templatePlots │ │ │ │ ├── AddedSection.tsx │ │ │ │ ├── NoPlotsToDisplay.tsx │ │ │ │ ├── TemplatePlotGroups.tsx │ │ │ │ ├── TemplatePlots.tsx │ │ │ │ ├── TemplatePlotsGrid.tsx │ │ │ │ ├── TemplatePlotsWrapper.tsx │ │ │ │ ├── templatePlotsSlice.ts │ │ │ │ └── util.ts │ │ │ ├── util.ts │ │ │ ├── vegaLite │ │ │ │ ├── ExtendedVegaLite.tsx │ │ │ │ ├── util.test.ts │ │ │ │ └── util.ts │ │ │ └── webviewSlice.ts │ │ ├── hooks │ │ │ ├── useGetPlot.ts │ │ │ ├── useGetTitles.ts │ │ │ ├── useModalOpenClass.ts │ │ │ └── useObserveGridDimensions.ts │ │ ├── index.tsx │ │ ├── store.ts │ │ └── util │ │ │ └── messages.ts │ ├── py.d.ts │ ├── setup │ │ ├── components │ │ │ ├── App.test.tsx │ │ │ ├── App.tsx │ │ │ ├── SetupContainer.tsx │ │ │ ├── dvc │ │ │ │ ├── CliIncompatible.tsx │ │ │ │ ├── CliUnavailable.tsx │ │ │ │ ├── Dvc.tsx │ │ │ │ ├── DvcEnvDetails.tsx │ │ │ │ ├── DvcUnitialized.tsx │ │ │ │ ├── GitUnitialized.tsx │ │ │ │ ├── ProjectUninitialized.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── experiments │ │ │ │ ├── DvcLiveExamples.tsx │ │ │ │ ├── Experiments.tsx │ │ │ │ ├── NeedsGitCommit.tsx │ │ │ │ ├── NoData.tsx │ │ │ │ ├── OtherFrameworks.tsx │ │ │ │ ├── PythonCodeBlock.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── remotes │ │ │ │ ├── AmazonS3.tsx │ │ │ │ ├── AzureBlobStorage.tsx │ │ │ │ ├── Connect.tsx │ │ │ │ ├── DvcUninitialized.tsx │ │ │ │ ├── GoogleCloudStorage.tsx │ │ │ │ ├── MultiProjectRemotes.tsx │ │ │ │ ├── OtherStorageTypes.tsx │ │ │ │ ├── ProjectRemotes.tsx │ │ │ │ ├── RemoteDetails.tsx │ │ │ │ ├── Remotes.tsx │ │ │ │ ├── ShowExtension.tsx │ │ │ │ ├── SupportedStorage.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── shared │ │ │ │ ├── CliIncompatible.tsx │ │ │ │ ├── CodeBlock.tsx │ │ │ │ ├── DetailsTable.tsx │ │ │ │ ├── DetailsTableRow.tsx │ │ │ │ ├── DvcNotSetup.tsx │ │ │ │ ├── ExtensionLink.tsx │ │ │ │ ├── FocusDvcSection.tsx │ │ │ │ ├── InfoText.tsx │ │ │ │ ├── Panels.tsx │ │ │ │ ├── styles.module.scss │ │ │ │ └── util.ts │ │ │ └── studio │ │ │ │ ├── Connect.tsx │ │ │ │ ├── Settings.tsx │ │ │ │ ├── Studio.tsx │ │ │ │ └── styles.module.scss │ │ ├── hooks │ │ │ └── usePrevious.ts │ │ ├── index.tsx │ │ ├── snippets │ │ │ ├── huggingFace.py │ │ │ ├── keras.py │ │ │ ├── pyTorch.py │ │ │ └── pythonApi.py │ │ ├── state │ │ │ ├── dvcSlice.ts │ │ │ ├── experimentsSlice.ts │ │ │ ├── remoteSlice.ts │ │ │ ├── studioSlice.ts │ │ │ └── webviewSlice.ts │ │ ├── store.ts │ │ └── util │ │ │ └── messages.ts │ ├── shared │ │ ├── __mocks__ │ │ │ └── api.ts │ │ ├── api.ts │ │ ├── components │ │ │ ├── Icon.tsx │ │ │ ├── button │ │ │ │ ├── Button.tsx │ │ │ │ ├── IconButton.tsx │ │ │ │ ├── RefreshButton.tsx │ │ │ │ ├── StartButton.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── contextMenu │ │ │ │ └── ContextMenu.tsx │ │ │ ├── copyButton │ │ │ │ ├── CopyButton.test.tsx │ │ │ │ ├── CopyButton.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── dragDrop │ │ │ │ ├── DragDropItemWithTarget.tsx │ │ │ │ ├── Draggable.tsx │ │ │ │ ├── DropTarget.tsx │ │ │ │ ├── GripIcon.tsx │ │ │ │ ├── currentTarget.ts │ │ │ │ ├── dragDropSlice.ts │ │ │ │ ├── styles.module.scss │ │ │ │ └── util.ts │ │ │ ├── emptyState │ │ │ │ ├── EmptyState.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── errorIcon │ │ │ │ ├── ErrorIcon.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── iconMenu │ │ │ │ ├── IconMenu.test.tsx │ │ │ │ ├── IconMenu.tsx │ │ │ │ ├── IconMenuItem.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── icons │ │ │ │ ├── Add.tsx │ │ │ │ ├── ArrowDown.tsx │ │ │ │ ├── ArrowUp.tsx │ │ │ │ ├── Beaker.tsx │ │ │ │ ├── Check.tsx │ │ │ │ ├── ChevronDown.tsx │ │ │ │ ├── ChevronRight.tsx │ │ │ │ ├── Clock.tsx │ │ │ │ ├── Close.tsx │ │ │ │ ├── Cloud.tsx │ │ │ │ ├── CloudUpload.tsx │ │ │ │ ├── Copy.tsx │ │ │ │ ├── Discard.tsx │ │ │ │ ├── Ellipsis.tsx │ │ │ │ ├── Error.tsx │ │ │ │ ├── Extensions.tsx │ │ │ │ ├── Filter.tsx │ │ │ │ ├── GitCommit.tsx │ │ │ │ ├── GitMerge.tsx │ │ │ │ ├── GraphLine.tsx │ │ │ │ ├── GraphScatter.tsx │ │ │ │ ├── Gripper.tsx │ │ │ │ ├── Info.tsx │ │ │ │ ├── Link.tsx │ │ │ │ ├── ListFilter.tsx │ │ │ │ ├── PassFilled.tsx │ │ │ │ ├── Pinned.tsx │ │ │ │ ├── Refresh.tsx │ │ │ │ ├── Remove.tsx │ │ │ │ ├── SortPrecedence.tsx │ │ │ │ ├── StarEmpty.tsx │ │ │ │ ├── StarFull.tsx │ │ │ │ ├── Table.tsx │ │ │ │ ├── Trash.tsx │ │ │ │ ├── Warning.tsx │ │ │ │ └── index.ts │ │ │ ├── messageBand │ │ │ │ ├── MessageBand.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── messagesMenu │ │ │ │ ├── MessagesMenu.tsx │ │ │ │ ├── MessagesMenuOption.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── modal │ │ │ │ ├── Modal.test.tsx │ │ │ │ ├── Modal.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── sectionContainer │ │ │ │ ├── InfoTooltip.tsx │ │ │ │ ├── SectionContainer.tsx │ │ │ │ ├── SectionDescription.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── selectMenu │ │ │ │ ├── SelectMenu.test.tsx │ │ │ │ ├── SelectMenu.tsx │ │ │ │ ├── SelectMenuOption.test.tsx │ │ │ │ ├── SelectMenuOption.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── slider │ │ │ │ ├── Slider.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── tooltip │ │ │ │ ├── ErrorTooltip.tsx │ │ │ │ ├── ErrorTooltipContent.tsx │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── styles.module.scss │ │ │ ├── virtualizedGrid │ │ │ │ ├── VirtualizedGrid.tsx │ │ │ │ └── styles.module.scss │ │ │ └── webviewWrapper │ │ │ │ ├── WebviewWrapper.test.tsx │ │ │ │ ├── WebviewWrapper.tsx │ │ │ │ ├── styles.module.scss │ │ │ │ └── useThemeVariables.ts │ │ ├── dispatchActions.ts │ │ ├── hooks │ │ │ ├── useDeferredDragLeave.ts │ │ │ ├── useDragAndDrop.tsx │ │ │ └── useVsCodeMessaging.ts │ │ ├── mixins.scss │ │ ├── styles.scss │ │ ├── variables.scss │ │ └── vscode.ts │ ├── stories │ │ ├── ComparisonTable.stories.tsx │ │ ├── ContextMenu.stories.tsx │ │ ├── IconMenu.stories.tsx │ │ ├── Icons.stories.tsx │ │ ├── Plots.stories.tsx │ │ ├── Ribbon.stories.tsx │ │ ├── Setup.stories.tsx │ │ ├── Slider.stories.tsx │ │ ├── Table.stories.tsx │ │ ├── Tooltip.stories.tsx │ │ ├── components │ │ │ ├── IconWrapper.tsx │ │ │ ├── IconsWrapper.tsx │ │ │ └── styles.module.scss │ │ └── util.ts │ ├── svg.d.ts │ ├── test │ │ ├── dragDrop.ts │ │ ├── experimentsTable.tsx │ │ ├── mockRowModel.ts │ │ ├── queries.ts │ │ ├── selection.ts │ │ ├── sort.ts │ │ └── tableDataFixture.ts │ └── util │ │ ├── array.test.ts │ │ ├── array.ts │ │ ├── helpers.ts │ │ ├── ids.test.ts │ │ ├── ids.ts │ │ ├── objects.ts │ │ ├── props.ts │ │ ├── strings.ts │ │ ├── styles.test.ts │ │ ├── styles.ts │ │ └── wdyr.ts ├── tsconfig.json └── webpack.config.ts └── yarn.lock /.codeclimate.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2", 3 | "exclude_patterns": [ 4 | "**/*.stories.*", 5 | "**/*.test.*", 6 | "**/constants.ts", 7 | "**/contract.ts", 8 | "demo/", 9 | "extension/src/test/", 10 | "languageServer/src/test/", 11 | "webview/src/shared/components/icons/", 12 | "webview/src/test/mockRowModel.ts" 13 | ], 14 | "checks": { 15 | "file-lines": { 16 | "config": { 17 | "threshold": 300 18 | } 19 | }, 20 | "method-count": { 21 | "config": { 22 | "threshold": 30 23 | } 24 | }, 25 | "method-lines": { 26 | "config": { 27 | "threshold": 40 28 | } 29 | }, 30 | "method-complexity": { 31 | "config": { 32 | "threshold": 6 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @mattseddon @shcheklein 2 | -------------------------------------------------------------------------------- /.github/workflows/changelog-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "categories": [ 3 | { 4 | "title": "### 🚀 New Features and Enhancements", 5 | "labels": ["product"] 6 | }, 7 | { 8 | "title": "### 🐛 Bug Fixes", 9 | "labels": ["bug"] 10 | }, 11 | { 12 | "title": "### 🔨 Maintenance", 13 | "labels": ["🏠 housekeeping"] 14 | } 15 | ], 16 | "pr_template": "- ${{TITLE}} [#${{NUMBER}}](https://github.com/iterative/vscode-dvc/pull/${{NUMBER}}) by [@${{AUTHOR}}](https://github.com/${{AUTHOR}})" 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/dvc-cli-output-test.yml: -------------------------------------------------------------------------------- 1 | name: DVC CLI Output Test 2 | 3 | on: 4 | schedule: 5 | - cron: '0 1 * * *' 6 | 7 | jobs: 8 | test: 9 | runs-on: ${{ matrix.os }} 10 | timeout-minutes: 20 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest, macOS-latest, windows-latest] 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | submodules: true 20 | 21 | - name: Setup Python environment 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: '3.11' 25 | cache: 'pip' 26 | 27 | - name: Setup Node.js environment 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: '16' 31 | cache: yarn 32 | 33 | - run: yarn install 34 | - run: yarn scheduled:cli:test 35 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "demo"] 2 | path = demo 3 | url = https://github.com/iterative/vscode-dvc-demo 4 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname $0)/_/husky.sh" 3 | 4 | yarn run turbo lint:build 5 | yarn lint-staged 6 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '**/*.{js,ts,tsx}': [ 3 | 'eslint --fix', 4 | 'jest --bail --findRelatedTests --passWithNoTests' 5 | ], 6 | '**/*.{js,json,jsx,md,scss,ts,tsx,yaml,yml}': 'prettier --write' 7 | } 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | CHANGELOG.md 2 | .github/workflows/publish.yml 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "avoid", 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "printWidth": 80, 7 | "tabWidth": 2, 8 | "useTabs": false, 9 | "proseWrap": "always" 10 | } 11 | -------------------------------------------------------------------------------- /extension/.gitignore: -------------------------------------------------------------------------------- 1 | *.vsix 2 | .vscode-test 3 | .wdio-vscode-service 4 | screenshots 5 | README.md 6 | LICENSE 7 | CHANGELOG.md 8 | .env 9 | -------------------------------------------------------------------------------- /extension/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../.lintstagedrc') 2 | -------------------------------------------------------------------------------- /extension/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | .vscode-test 3 | .wdio-vscode-service 4 | coverage 5 | src/test/fixtures/plotsDiff/vega.ts 6 | CHANGELOG.md -------------------------------------------------------------------------------- /extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | .cache/** 2 | .turbo/** 3 | .vscode-test/** 4 | .wdio-vscode-service/** 5 | coverage/** 6 | node_modules/** 7 | scripts/** 8 | src/** 9 | .eslintrc.js 10 | .gitignore 11 | .prettierignore 12 | .lintstagedrc.js 13 | .vscodeignore 14 | jest.config.js 15 | tsconfig.json 16 | webpack.config.ts 17 | yarn.lock 18 | -------------------------------------------------------------------------------- /extension/docs/dvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/docs/dvc.png -------------------------------------------------------------------------------- /extension/docs/overview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/docs/overview.gif -------------------------------------------------------------------------------- /extension/docs/walkthroughs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/docs/walkthroughs.png -------------------------------------------------------------------------------- /extension/jest.config.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | 3 | module.exports = { 4 | coverageDirectory: 'coverage/jest', 5 | coveragePathIgnorePatterns: ['/src/test/', '/node_modules/'], 6 | coverageReporters: ['json'], 7 | testEnvironment: 'node', 8 | testPathIgnorePatterns: [ 9 | '/src/test/', 10 | '/dist/', 11 | '/.vscode-test/', 12 | '/.wdio-vscode-service/' 13 | ], 14 | transform: { 15 | '^.+\\.(t|j)sx?$': ['@swc/jest'] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extension/resources/dark/beaker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/dark/checkbox-checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /extension/resources/dark/checkbox-empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/dark/checkbox-indeterminate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /extension/resources/dark/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/dark/queue-experiment.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /extension/resources/dark/scatter-graph.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#13adc7.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#4299e1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#48bb78.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#945dd6.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#ed8936.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#f46837.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/circle-filled-#f56565.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#13adc7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#4299e1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#48bb78.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#945dd6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#ed8936.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#f46837.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/experiments/loading-spin-#f56565.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/light/beaker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/light/checkbox-checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /extension/resources/light/checkbox-empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/light/checkbox-indeterminate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /extension/resources/light/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/light/queue-experiment.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /extension/resources/light/scatter-graph.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /extension/resources/plots/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/diamond.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /extension/resources/plots/square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-1-0.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-1-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-2-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-4-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-4-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-8-4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/stroke-dash-8-8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/plots/triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /extension/resources/walkthrough/command-palette.md: -------------------------------------------------------------------------------- 1 | # Command Palette 2 | 3 | This extension makes extensive use of the 4 | [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette) 5 | which can be accessed via `F1` or ⇧⌃P on Windows/Linux or ⇧⌘P on macOS. 6 | 7 | To see a list of available commands click [here](command:dvc.showCommands) or 8 | type DVC into the Command Palette. 9 | 10 |

11 | DVC Command Palette 13 |

14 | 15 | For further information on specific `dvc` commands, see the 16 | [command-reference docs](https://dvc.org/doc/command-reference). 17 | -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/available-commands-command-palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/available-commands-command-palette.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/click-the-circle-beside-experiment-name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/click-the-circle-beside-experiment-name.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/dvc-tracked-explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/dvc-tracked-explorer.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/dvc-tracked-scm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/dvc-tracked-scm.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/experiments-table-context-menus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/experiments-table-context-menus.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/experiments-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/experiments-table.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/install-dvc-setup-wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/install-dvc-setup-wizard.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/live-plots-updates.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/live-plots-updates.gif -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/plots-custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/plots-custom.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/plots-data-series.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/plots-data-series.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/plots-images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/plots-images.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/plots-view-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/plots-view-icon.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/run-experiments-command-palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/run-experiments-command-palette.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/run-experiments-params-buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/run-experiments-params-buttons.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/run-experiments-table-buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/run-experiments-table-buttons.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/setup-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/setup-view.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/view-container-support.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/view-container-support.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/view-container-welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/view-container-welcome.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/images/view-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/resources/walkthrough/images/view-container.png -------------------------------------------------------------------------------- /extension/resources/walkthrough/setup.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | The extension's key features cannot be accessed until DVC is installed and a DVC 4 | project is available in the workspace. The 5 | [setup page](command:dvc.showDvcSetup) can assist you with setting up your DVC 6 | project, along with configuring additional features such as connecting to DVC 7 | Studio and remotes. 8 | 9 |

10 | DVC setup page 12 |

13 | 14 | Use `DVC: Show Setup` from the 15 | [Command Palette](command:workbench.action.quickOpen?%22>DVC:%20Show%20Experiments%22) 16 | to access it. 17 | -------------------------------------------------------------------------------- /extension/resources/walkthrough/troubleshoot.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | All `DVC` CLI commands generate two entries in this extension's dedicated output 4 | channel. The first shows the command has started. The second is to signify that 5 | it has completed. 6 | 7 | The completion entry will show: 8 | 9 | - The outcome of the command (COMPLETED / FAILED). 10 | - Any non-zero exit codes. 11 | - The length of time that each command took to run (ms). 12 | - Any error message provided by the CLI. 13 | 14 | Don't hesitate to ping us in the [Discord](https://discord.gg/BGCjJHvDHt) 15 | channel or open an issue on 16 | [GitHub](https://github.com/iterative/vscode-dvc/issues). We are happy to help 17 | 🤗. 18 | 19 | Please share with us the output channel logs, the extension version, and the DVC 20 | version (the `dvc version` output). 21 | -------------------------------------------------------------------------------- /extension/scripts/ensureYarnLock.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | const { ensureFileSync } = require('fs-extra') 3 | 4 | const yarnLock = resolve(__dirname, '..', 'yarn.lock') 5 | 6 | ensureFileSync(yarnLock) 7 | -------------------------------------------------------------------------------- /extension/src/__mocks__/@hediet/std/disposable.ts: -------------------------------------------------------------------------------- 1 | export const Disposable = { 2 | fn: jest.fn() 3 | } 4 | -------------------------------------------------------------------------------- /extension/src/__mocks__/@vscode/extension-telemetry.ts: -------------------------------------------------------------------------------- 1 | const TelemetryReporter = jest.fn() 2 | 3 | export default TelemetryReporter 4 | -------------------------------------------------------------------------------- /extension/src/class/deferred.ts: -------------------------------------------------------------------------------- 1 | import { Deferred } from '@hediet/std/synchronization' 2 | import { Disposable } from './dispose' 3 | 4 | export abstract class DeferredDisposable extends Disposable { 5 | protected deferred = new Deferred() 6 | private initialized = this.deferred.promise 7 | 8 | public isReady() { 9 | return this.initialized 10 | } 11 | 12 | public resetDeferred() { 13 | if (this.consumersStillWaiting()) { 14 | return 15 | } 16 | this.deferred = new Deferred() 17 | this.initialized = this.deferred.promise 18 | } 19 | 20 | private consumersStillWaiting() { 21 | return this.deferred.state === 'none' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extension/src/class/dispose.ts: -------------------------------------------------------------------------------- 1 | import { Disposable as StdDisposable } from '@hediet/std/disposable' 2 | 3 | export abstract class Disposable { 4 | public readonly dispose = StdDisposable.fn() 5 | } 6 | -------------------------------------------------------------------------------- /extension/src/cli/command.ts: -------------------------------------------------------------------------------- 1 | import { joinTruthyItems } from '../util/array' 2 | 3 | export const getCommandString = ({ 4 | args, 5 | executable 6 | }: { 7 | args: string[] 8 | executable: string 9 | }): string => { 10 | return `${joinTruthyItems([executable, ...args])}` 11 | } 12 | -------------------------------------------------------------------------------- /extension/src/cli/constants.ts: -------------------------------------------------------------------------------- 1 | import { Args as DvcArgs } from './dvc/constants' 2 | import { Args as GitArgs } from './git/constants' 3 | 4 | export type Args = DvcArgs | GitArgs 5 | -------------------------------------------------------------------------------- /extension/src/cli/dvc/cwd.ts: -------------------------------------------------------------------------------- 1 | import { realpathSync } from 'fs' 2 | 3 | export const getCaseSensitiveCwd = (path: string): string => { 4 | try { 5 | const cwd = realpathSync.native(path) 6 | if (cwd) { 7 | return cwd 8 | } 9 | } catch {} 10 | return path 11 | } 12 | -------------------------------------------------------------------------------- /extension/src/cli/dvc/output.ts: -------------------------------------------------------------------------------- 1 | export const Prompt = { 2 | TRY_FORCE: /Use\s['`](-f|--force).*?['`]\sto\sforce\./ 3 | } as const 4 | -------------------------------------------------------------------------------- /extension/src/cli/error.ts: -------------------------------------------------------------------------------- 1 | import { ProcessOptions } from '../process/execution' 2 | 3 | export interface MaybeConsoleError extends Error { 4 | stderr?: string 5 | exitCode: number 6 | } 7 | 8 | interface CliProcessErrorArgs { 9 | options: ProcessOptions 10 | baseError: MaybeConsoleError 11 | message?: string 12 | } 13 | 14 | export class CliError extends Error { 15 | public readonly options?: ProcessOptions 16 | public readonly baseError: Error 17 | public readonly stderr?: string 18 | public readonly exitCode: number | null 19 | 20 | constructor({ message, options, baseError }: CliProcessErrorArgs) { 21 | super(message || baseError.message) 22 | this.options = options 23 | this.baseError = baseError 24 | this.stderr = baseError.stderr 25 | this.exitCode = baseError.exitCode 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /extension/src/cli/git/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, Flag } from './constants' 2 | import { getOptions } from './options' 3 | import { Cli } from '..' 4 | import { standardizePath } from '../../fileSystem/path' 5 | 6 | export class GitCli extends Cli { 7 | public async getGitRepositoryRoot(cwd: string) { 8 | const options = getOptions({ 9 | args: [Command.REV_PARSE, Flag.SHOW_TOPLEVEL], 10 | cwd 11 | }) 12 | 13 | try { 14 | const path = await this.executeProcess(options) 15 | return standardizePath(path) 16 | } catch {} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extension/src/cli/git/options.ts: -------------------------------------------------------------------------------- 1 | import { Args } from './constants' 2 | import { ProcessOptions } from '../../process/execution' 3 | 4 | export const getOptions = ({ 5 | cwd, 6 | args = [], 7 | env 8 | }: { 9 | cwd: string 10 | args?: Args 11 | env?: NodeJS.ProcessEnv 12 | }): ProcessOptions => { 13 | const options: ProcessOptions = { 14 | args, 15 | cwd, 16 | executable: 'git' 17 | } 18 | 19 | if (env) { 20 | options.env = { ...env, GIT_OPTIONAL_LOCKS: '0' } 21 | } 22 | 23 | return options 24 | } 25 | -------------------------------------------------------------------------------- /extension/src/commands/util.ts: -------------------------------------------------------------------------------- 1 | import { commands } from 'vscode' 2 | import { RegisteredCommands } from './external' 3 | import { Setup } from '../setup' 4 | import { Context } from '../vscode/context' 5 | 6 | export const showSetupOrExecuteCommand = 7 | (setup: Setup, callback: (context: Context) => Promise) => 8 | async (context: Context) => { 9 | await setup.isReady() 10 | const { dvc, experiments } = setup.shouldBeShown() 11 | if (!dvc) { 12 | return commands.executeCommand(RegisteredCommands.SETUP_SHOW_DVC) 13 | } 14 | 15 | if (!experiments) { 16 | return commands.executeCommand(RegisteredCommands.SETUP_SHOW_EXPERIMENTS) 17 | } 18 | 19 | return callback(context) 20 | } 21 | -------------------------------------------------------------------------------- /extension/src/common/logger.ts: -------------------------------------------------------------------------------- 1 | enum Level { 2 | ERROR = 'error', 3 | WARN = 'warn', 4 | INFO = 'info', 5 | LOG = 'log' 6 | } 7 | 8 | export class Logger { 9 | static error = (message: string) => Logger.write(message, Level.ERROR) 10 | static warn = (message: string) => Logger.write(message, Level.WARN) 11 | static info = (message: string) => Logger.write(message, Level.INFO) 12 | static log = (message: string) => Logger.write(message, Level.LOG) 13 | 14 | private static write = (message: string, level: Level): void => { 15 | // eslint-disable-next-line no-console 16 | return console[level](message) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extension/src/env.ts: -------------------------------------------------------------------------------- 1 | export const getProcessEnv = (): NodeJS.ProcessEnv => process.env 2 | export const getProcessPlatform = (): NodeJS.Platform => process.platform 3 | -------------------------------------------------------------------------------- /extension/src/experiments/columns/extract.test.ts: -------------------------------------------------------------------------------- 1 | import { extractColumns } from './extract' 2 | import { generateTestExpData } from '../../test/util/experiments' 3 | 4 | describe('extractColumns', () => { 5 | it('should handle concatenating errors', () => { 6 | const data = generateTestExpData({ 7 | metrics: { 8 | 'summary.json': { 9 | error: { msg: 'metrics file is busted', type: 'fatal' } 10 | } 11 | }, 12 | params: { 13 | 'params.yaml': { 14 | error: { msg: 'this is also broken', type: 'impossible' } 15 | } 16 | } 17 | }) 18 | 19 | const { error } = extractColumns(data) 20 | expect(error).toStrictEqual('metrics file is busted\nthis is also broken') 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /extension/src/experiments/data/constants.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { gitPath } from '../../cli/git/constants' 3 | 4 | export const EXPERIMENTS_GIT_REFS = join(gitPath.GIT_REFS, 'exps') 5 | export const EXPERIMENTS_GIT_LOGS_REFS = join(gitPath.GIT_LOGS_REFS, 'exps') 6 | export const EXPERIMENTS_GIT_REFS_EXEC = join(EXPERIMENTS_GIT_REFS, 'exec') 7 | -------------------------------------------------------------------------------- /extension/src/experiments/model/filterBy/constants.ts: -------------------------------------------------------------------------------- 1 | import { Operator } from '.' 2 | 3 | export const starredFilter = { 4 | operator: Operator.IS_TRUE, 5 | path: 'starred', 6 | value: undefined 7 | } 8 | -------------------------------------------------------------------------------- /extension/src/experiments/model/filterBy/date.ts: -------------------------------------------------------------------------------- 1 | import { Operator } from '.' 2 | import { standardizeDate } from '../../../util/date' 3 | 4 | export const compareDateStrings = ( 5 | baseDateString: string | number | boolean, 6 | operator: Operator.LESS_THAN | Operator.GREATER_THAN | Operator.EQUAL, 7 | comparisonDateString: string | number | boolean 8 | ): boolean => { 9 | if ( 10 | typeof baseDateString !== 'string' || 11 | typeof comparisonDateString !== 'string' 12 | ) { 13 | return false 14 | } 15 | 16 | const epoch = standardizeDate(baseDateString) 17 | const otherEpoch = standardizeDate(comparisonDateString) 18 | 19 | switch (operator) { 20 | case Operator.LESS_THAN: 21 | return epoch < otherEpoch 22 | case Operator.GREATER_THAN: 23 | return epoch > otherEpoch 24 | case Operator.EQUAL: 25 | return epoch === otherEpoch 26 | 27 | default: 28 | return false 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /extension/src/experiments/model/sortBy/constants.ts: -------------------------------------------------------------------------------- 1 | export const starredSort = { descending: true, path: 'starred' } 2 | -------------------------------------------------------------------------------- /extension/src/experiments/model/status/colors.ts: -------------------------------------------------------------------------------- 1 | const colorsList = [ 2 | '#945dd6', 3 | '#13adc7', 4 | '#f46837', 5 | '#48bb78', 6 | '#4299e1', 7 | '#ed8936', 8 | '#f56565' 9 | ] as const 10 | 11 | export type Color = (typeof colorsList)[number] 12 | 13 | export const copyOriginalColors = (): Color[] => [...colorsList] 14 | -------------------------------------------------------------------------------- /extension/src/experiments/processExecution/collect.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { 3 | DVCLIVE_ONLY_RUNNING_SIGNAL_FILE, 4 | EXP_RWLOCK_FILE 5 | } from '../../cli/dvc/constants' 6 | import { getPidFromFile } from '../../fileSystem' 7 | 8 | export const collectDvcRootPids = async ( 9 | acc: Set, 10 | dvcRoot: string 11 | ): Promise => { 12 | for (const file of [ 13 | join(dvcRoot, DVCLIVE_ONLY_RUNNING_SIGNAL_FILE), 14 | join(dvcRoot, EXP_RWLOCK_FILE) 15 | ]) { 16 | const pid = await getPidFromFile(file) 17 | if (!pid) { 18 | continue 19 | } 20 | acc.add(pid) 21 | } 22 | } 23 | 24 | export const collectRunningExperimentPids = async ( 25 | dvcRoots: string[] 26 | ): Promise => { 27 | const acc = new Set() 28 | for (const dvcRoot of dvcRoots) { 29 | await collectDvcRootPids(acc, dvcRoot) 30 | } 31 | return [...acc] 32 | } 33 | -------------------------------------------------------------------------------- /extension/src/experiments/processExecution/index.ts: -------------------------------------------------------------------------------- 1 | import { collectDvcRootPids } from './collect' 2 | import { processExists, stopProcesses } from '../../process/execution' 3 | 4 | export const stopWorkspaceExperiment = async (dvcRoot: string, id: string) => { 5 | const pids = new Set() 6 | 7 | await collectDvcRootPids(pids, dvcRoot) 8 | 9 | const notFound = `process executing ${id} was not found.` 10 | 11 | if (pids.size === 0) { 12 | return notFound 13 | } 14 | 15 | const [pid] = [...pids] 16 | 17 | if (!pid || !(await processExists(pid))) { 18 | return notFound 19 | } 20 | 21 | const failedToStop = `failed to kill ${id}.` 22 | 23 | try { 24 | const stopped = await stopProcesses([pid]) 25 | return stopped ? `${id} has been killed.` : failedToStop 26 | } catch { 27 | return failedToStop 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /extension/src/fileSystem/path.ts: -------------------------------------------------------------------------------- 1 | import { Uri } from 'vscode' 2 | 3 | export const standardizePath = (path: string): string => Uri.file(path).fsPath 4 | 5 | export const standardizePossiblePath = (path?: string): string | undefined => { 6 | if (!path) { 7 | return 8 | } 9 | return standardizePath(path) 10 | } 11 | -------------------------------------------------------------------------------- /extension/src/fileSystem/util.ts: -------------------------------------------------------------------------------- 1 | import { sep, parse } from 'path' 2 | 3 | export const getPathArray = (path: string): string[] => path.split(sep) 4 | 5 | export const ensureOsFileSep = (path: string): string => 6 | path.replace(/\\/g, '/').split('/').join(sep) 7 | 8 | export const getPath = (pathArray: string[], idx: number) => 9 | pathArray.slice(0, idx).join(sep) 10 | 11 | export const getDirectChild = (pathArray: string[], idx: number) => 12 | getPath(pathArray, idx + 1) 13 | 14 | export const getParent = (pathArray: string[], idx: number) => { 15 | const parent = getPath(pathArray, idx - 1) 16 | if (!parent) { 17 | return undefined 18 | } 19 | return parent 20 | } 21 | 22 | export const removeTrailingSlash = (path: string): string => 23 | path.endsWith(sep) ? path.slice(0, -1) : path 24 | 25 | export const getFileNameWithoutExt = (path: string) => parse(path).name 26 | -------------------------------------------------------------------------------- /extension/src/fileSystem/workspace.ts: -------------------------------------------------------------------------------- 1 | import { basename } from 'path' 2 | import { Uri, workspace, WorkspaceEdit } from 'vscode' 3 | 4 | export const deleteTarget = (uri: Uri) => { 5 | const edit = new WorkspaceEdit() 6 | edit.deleteFile(uri, { ignoreIfNotExists: true, recursive: true }) 7 | return workspace.applyEdit(edit) 8 | } 9 | 10 | export const moveTargets = (targets: Uri[], destination: Uri) => { 11 | const edit = new WorkspaceEdit() 12 | for (const uri of targets) { 13 | edit.renameFile(uri, Uri.joinPath(destination, basename(uri.fsPath))) 14 | } 15 | 16 | return workspace.applyEdit(edit) 17 | } 18 | 19 | export const findFiles = async ( 20 | relativeGlob: string, 21 | exclude?: string 22 | ): Promise => { 23 | const files = await workspace.findFiles(relativeGlob, exclude) 24 | return files.map(uri => uri.fsPath) 25 | } 26 | -------------------------------------------------------------------------------- /extension/src/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { SetupSection } from './setup/webview/contract' 2 | 3 | export interface IExtensionSetup { 4 | getCliVersion: ( 5 | cwd: string, 6 | isCliGlobal?: true 7 | ) => Promise 8 | getRoots: () => string[] 9 | hasRoots: () => boolean 10 | isPythonExtensionUsed: () => Promise 11 | 12 | showSetup: (focusedSection?: SetupSection) => void 13 | shouldWarnUserIfCLIUnavailable: () => boolean 14 | 15 | initialize: () => Promise 16 | resetMembers: () => void 17 | 18 | setAvailable: (available: boolean) => void 19 | getAvailable: () => boolean 20 | setCliCompatibleAndVersion: ( 21 | compatible: boolean | undefined, 22 | version: string | undefined 23 | ) => void 24 | setRoots: () => Promise 25 | unsetPythonBinPath: () => void 26 | } 27 | -------------------------------------------------------------------------------- /extension/src/persistence/model.ts: -------------------------------------------------------------------------------- 1 | import { Memento } from 'vscode' 2 | import { PersistenceKey } from './constants' 3 | import { DeferredDisposable } from '../class/deferred' 4 | 5 | export class ModelWithPersistence extends DeferredDisposable { 6 | protected readonly dvcRoot: string 7 | private readonly workspaceState: Memento 8 | 9 | constructor(dvcRoot: string, workspaceState: Memento) { 10 | super() 11 | 12 | this.dvcRoot = dvcRoot 13 | this.workspaceState = workspaceState 14 | } 15 | 16 | protected persist(key: PersistenceKey, value: unknown) { 17 | void this.workspaceState.update(key + this.dvcRoot, value) 18 | } 19 | 20 | protected revive(key: PersistenceKey, defaultValue: T) { 21 | return this.workspaceState.get(key + this.dvcRoot, defaultValue) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extension/src/persistence/register.ts: -------------------------------------------------------------------------------- 1 | import { Memento } from 'vscode' 2 | import { resetPersistedState } from './util' 3 | import { RegisteredCommands } from '../commands/external' 4 | import { InternalCommands } from '../commands/internal' 5 | 6 | export const registerPersistenceCommands = ( 7 | workspaceState: Memento, 8 | globalState: Memento, 9 | internalCommands: InternalCommands 10 | ) => { 11 | internalCommands.registerExternalCommand(RegisteredCommands.RESET_STATE, () => 12 | resetPersistedState(workspaceState, globalState) 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /extension/src/persistence/util.ts: -------------------------------------------------------------------------------- 1 | import { commands, Memento } from 'vscode' 2 | 3 | export const resetPersistedState = async ( 4 | workspaceState: Memento, 5 | globalState: Memento 6 | ) => { 7 | for (const persistenceKey of workspaceState.keys()) { 8 | await workspaceState.update(persistenceKey, undefined) 9 | } 10 | for (const persistenceKey of globalState.keys()) { 11 | await globalState.update(persistenceKey, undefined) 12 | } 13 | 14 | await commands.executeCommand('workbench.action.reloadWindow') 15 | } 16 | -------------------------------------------------------------------------------- /extension/src/pipeline/model/index.ts: -------------------------------------------------------------------------------- 1 | import isEqual from 'lodash.isequal' 2 | import { collectPipelines } from './collect' 3 | import { Disposable } from '../../class/dispose' 4 | 5 | export class PipelineModel extends Disposable { 6 | private pipelines: Set | undefined 7 | 8 | public transformAndSet(data: { [pipeline: string]: string | undefined }) { 9 | if (isEqual(data, {})) { 10 | this.pipelines = undefined 11 | return 12 | } 13 | 14 | const pipelines = collectPipelines(data) 15 | 16 | this.pipelines = pipelines 17 | } 18 | 19 | public getPipelines() { 20 | return this.pipelines 21 | } 22 | 23 | public hasPipeline() { 24 | return !!(this.pipelines && this.pipelines.size > 0) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /extension/src/pipeline/register.ts: -------------------------------------------------------------------------------- 1 | import { WorkspacePipeline } from './workspace' 2 | import { RegisteredCommands } from '../commands/external' 3 | import { InternalCommands } from '../commands/internal' 4 | 5 | export const registerPipelineCommands = ( 6 | pipelines: WorkspacePipeline, 7 | internalCommands: InternalCommands 8 | ): void => { 9 | internalCommands.registerExternalCommand( 10 | RegisteredCommands.PIPELINE_SHOW_DAG, 11 | () => pipelines.showDag() 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /extension/src/plots/model/custom.test.ts: -------------------------------------------------------------------------------- 1 | import { cleanupOldOrderValue } from './custom' 2 | 3 | describe('cleanupOlderValue', () => { 4 | it('should update value if contents are outdated', () => { 5 | const output = cleanupOldOrderValue({ 6 | metric: 'metrics:summary.json:loss', 7 | param: 'params:params.yaml:log_file' 8 | }) 9 | expect(output).toStrictEqual({ 10 | metric: 'summary.json:loss', 11 | param: 'params.yaml:log_file' 12 | }) 13 | }) 14 | 15 | it('should not update value if contents are not outdated', () => { 16 | const value = { 17 | metric: 'summary.json:loss', 18 | param: 'params.yaml:log_file' 19 | } 20 | const output = cleanupOldOrderValue(value) 21 | expect(output).toStrictEqual(value) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /extension/src/plots/model/util.ts: -------------------------------------------------------------------------------- 1 | import { getDataFromColumnPaths } from '../../experiments/model/util' 2 | import { Experiment } from '../../experiments/webview/contract' 3 | import { RevisionSummaryColumns } from '../webview/contract' 4 | 5 | export const getRevisionSummaryColumns = ( 6 | summaryColumns: string[], 7 | experiment: Experiment 8 | ): RevisionSummaryColumns => 9 | getDataFromColumnPaths(experiment, summaryColumns).map( 10 | ({ columnPath: path, value, type }) => ({ 11 | path, 12 | type, 13 | value 14 | }) 15 | ) 16 | -------------------------------------------------------------------------------- /extension/src/plots/quickPick.ts: -------------------------------------------------------------------------------- 1 | import { quickPickValue } from '../vscode/quickPick' 2 | import { Title } from '../vscode/title' 3 | 4 | export const enum PLOT_TYPE { 5 | CUSTOM = 'custom', 6 | DATA_SERIES = 'data-series' 7 | } 8 | 9 | export const pickPlotType = () => 10 | quickPickValue( 11 | [ 12 | { 13 | description: 14 | 'Create a data series plot definition by selecting data from one or more files (To select multiple files, hold the Ctrl/Command key and click on the files.).', 15 | label: 'Data Series', 16 | value: PLOT_TYPE.DATA_SERIES 17 | }, 18 | { 19 | description: 20 | 'Create an extension-only plot by selecting one metric and one param from the experiments table', 21 | label: 'Custom', 22 | value: PLOT_TYPE.CUSTOM 23 | } 24 | ], 25 | { 26 | title: Title.SELECT_PLOT_TYPE 27 | } 28 | ) 29 | -------------------------------------------------------------------------------- /extension/src/plots/util.ts: -------------------------------------------------------------------------------- 1 | import { PlotsOutput, PlotsOutputOrError } from '../cli/dvc/contract' 2 | import { isDvcError } from '../cli/dvc/reader' 3 | import { ensureOsFileSep } from '../fileSystem/util' 4 | 5 | export const ensurePlotsDataPathsOsSep = ( 6 | plotsData: PlotsOutputOrError 7 | ): PlotsOutputOrError => { 8 | if (isDvcError(plotsData)) { 9 | return plotsData 10 | } 11 | const standardisedData: PlotsOutput = { data: {} } 12 | const { data, errors } = plotsData 13 | 14 | for (const path of Object.keys(data)) { 15 | standardisedData.data[ensureOsFileSep(path)] = data[path] 16 | } 17 | 18 | if (!errors) { 19 | return standardisedData 20 | } 21 | standardisedData.errors = errors.map(error => { 22 | if (!error.name) { 23 | return error 24 | } 25 | 26 | return { ...error, name: ensureOsFileSep(error.name) } 27 | }) 28 | 29 | return standardisedData 30 | } 31 | -------------------------------------------------------------------------------- /extension/src/python/path.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { getProcessPlatform } from '../env' 3 | 4 | export const getVenvBinPath = (cwd: string, envDir: string, name: string) => 5 | getProcessPlatform() === 'win32' 6 | ? join(cwd, envDir, 'Scripts', `${name}.exe`) 7 | : join(cwd, envDir, 'bin', name) 8 | -------------------------------------------------------------------------------- /extension/src/repository/constants.ts: -------------------------------------------------------------------------------- 1 | export const DiscardedStatus = { 2 | UNCHANGED: 'unchanged' 3 | } as const 4 | 5 | export const UndecoratedDataStatus = { 6 | TRACKED_DECORATIONS: 'trackedDecorations', 7 | UNTRACKED: 'untracked' 8 | } as const 9 | 10 | export const BaseDataStatus = { 11 | COMMITTED_ADDED: 'committedAdded', 12 | COMMITTED_DELETED: 'committedDeleted', 13 | COMMITTED_MODIFIED: 'committedModified', 14 | COMMITTED_RENAMED: 'committedRenamed', 15 | COMMITTED_UNKNOWN: 'committedUnknown', 16 | NOT_IN_CACHE: 'notInCache', 17 | UNCOMMITTED_ADDED: 'uncommittedAdded', 18 | UNCOMMITTED_DELETED: 'uncommittedDeleted', 19 | UNCOMMITTED_MODIFIED: 'uncommittedModified', 20 | UNCOMMITTED_RENAMED: 'uncommittedRenamed', 21 | UNCOMMITTED_UNKNOWN: 'uncommittedUnknown' 22 | } as const 23 | -------------------------------------------------------------------------------- /extension/src/repository/model/error.ts: -------------------------------------------------------------------------------- 1 | import { PathItem } from './collect' 2 | 3 | export const createTreeFromError = ( 4 | dvcRoot: string, 5 | msg: string 6 | ): Map => 7 | new Map([ 8 | [ 9 | dvcRoot, 10 | [ 11 | { 12 | error: msg 13 | } as PathItem 14 | ] 15 | ] 16 | ]) 17 | -------------------------------------------------------------------------------- /extension/src/setup/inputBox.ts: -------------------------------------------------------------------------------- 1 | import { isStudioAccessToken } from './studio' 2 | 3 | export const validateTokenInput = (input: string | undefined) => { 4 | if (!isStudioAccessToken(input)) { 5 | return 'please enter a valid Studio access token' 6 | } 7 | return null 8 | } 9 | 10 | export const validateUrlInput = (input: string | undefined = '') => { 11 | try { 12 | const validUrl = new URL(input) 13 | if (validUrl) { 14 | return null 15 | } 16 | } catch {} 17 | return 'please enter a valid URL' 18 | } 19 | -------------------------------------------------------------------------------- /extension/src/test/cli/constants.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | 3 | export const ENV_DIR = '.env' 4 | export const TEMP_DIR = resolve(__dirname, '..', '..', '..', '..', '..', 'temp') 5 | -------------------------------------------------------------------------------- /extension/src/test/e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /extension/src/test/e2e/pageObjects/locators.ts: -------------------------------------------------------------------------------- 1 | export const webview = { 2 | innerFrame: '#active-frame', 3 | outerFrame: '.webview.ready' 4 | } 5 | 6 | export const experiments = { 7 | ...webview, 8 | expandRowButton: 'button[title="Expand Row"]', 9 | row: 'tr', 10 | table: 'table' 11 | } 12 | 13 | export const plots = { 14 | ...webview, 15 | vegaVisualization: 'div[aria-label="Vega visualization"]' 16 | } 17 | -------------------------------------------------------------------------------- /extension/src/test/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../../tsconfig.json", 3 | "compilerOptions": { 4 | "moduleResolution": "node", 5 | "module": "ESNext", 6 | "types": [ 7 | "node", 8 | "mocha", 9 | "wdio-vscode-service", 10 | "@wdio/globals/types", 11 | "expect-webdriverio", 12 | "@wdio/mocha-framework" 13 | ], 14 | "target": "es2022" 15 | }, 16 | "include": ["specs", "./wdio.conf.ts"], 17 | "exclude": [] 18 | } 19 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/base/gitLog.ts: -------------------------------------------------------------------------------- 1 | import expShowFixture from './output' 2 | import { COMMITS_SEPARATOR } from '../../../../cli/git/constants' 3 | 4 | const data = `${expShowFixture[1].rev} 5 | github-actions[bot] 6 | 6 hours ago 7 | refNames:HEAD, tag: 0.9.3, origin/main, origin/HEAD, main 8 | message:Update version and CHANGELOG for release (#4022) 9 | 10 | Co-authored-by: Olivaw[bot] ${COMMITS_SEPARATOR}${expShowFixture[2].rev} 11 | Julie G 12 | 6 hours ago 13 | refNames: 14 | message:Improve "Get Started" walkthrough (#4020) 15 | 16 | * don't show walkthrough in sidebar welcome section 17 | * move admonition in command palette walkthrough step${COMMITS_SEPARATOR}${expShowFixture[3].rev} 18 | Matt Seddon 19 | 8 hours ago 20 | refNames: 21 | message:Add capabilities to text mentioning storage provider extensions (#4015) 22 | ` 23 | export default data 24 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/base/noErrors.ts: -------------------------------------------------------------------------------- 1 | import { ExpShowOutput } from '../../../../cli/dvc/contract' 2 | import expShowFixture, { ERROR_SHAS } from './output' 3 | 4 | const excludeErrors = (): ExpShowOutput => { 5 | const expShowFixtureWithoutErrors: ExpShowOutput = [] 6 | 7 | for (const expState of expShowFixture) { 8 | const expStateWithoutErrors = { ...expState } 9 | if (expState.experiments) { 10 | const experiments = [] 11 | for (const exp of expState.experiments) { 12 | if (!ERROR_SHAS.includes(exp.revs[0].rev)) { 13 | experiments.push(exp) 14 | } 15 | } 16 | expStateWithoutErrors.experiments = experiments 17 | } 18 | 19 | expShowFixtureWithoutErrors.push(expStateWithoutErrors) 20 | } 21 | 22 | return expShowFixtureWithoutErrors 23 | } 24 | 25 | const data = excludeErrors() 26 | 27 | export default data 28 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/base/remoteExpRefs.ts: -------------------------------------------------------------------------------- 1 | const data = `42b8736b08170529903cd203a1f40382a4b4a8cd refs/exps/a9/b32d14966b9be1396f2211d9eb743359708a07/test-branch` 2 | 3 | export default data 4 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/base/rowOrder.ts: -------------------------------------------------------------------------------- 1 | const data = [ 2 | { branch: 'main', sha: '53c3851f46955fa3e2b8f6e1c52999acc8c9ea77' }, 3 | { branch: 'main', sha: 'fe2919bb4394b30494bea905c253e10077b9a1bd' }, 4 | { branch: 'main', sha: '7df876cb5147800cd3e489d563bc6dcd67188621' } 5 | ] 6 | 7 | export default data 8 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/base/tableData.ts: -------------------------------------------------------------------------------- 1 | import { TableData } from '../../../../experiments/webview/contract' 2 | import rowsFixture from './rows' 3 | import columnsFixture from './columns' 4 | 5 | const tableDataFixture: TableData = { 6 | changes: [], 7 | cliError: null, 8 | columnOrder: [], 9 | columns: columnsFixture, 10 | columnWidths: {}, 11 | filters: [], 12 | hasBranchesToSelect: true, 13 | hasConfig: true, 14 | hasMoreCommits: { main: true }, 15 | hasRunningWorkspaceExperiment: true, 16 | isShowingMoreCommits: { main: true }, 17 | rows: rowsFixture, 18 | selectedBranches: [], 19 | selectedForPlotsCount: 2, 20 | showOnlyChanged: false, 21 | sorts: [] 22 | } 23 | 24 | export default tableDataFixture 25 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/base/workspaceChanges.ts: -------------------------------------------------------------------------------- 1 | const data = [ 2 | 'metrics:summary.json:accuracy', 3 | 'metrics:summary.json:loss', 4 | 'metrics:summary.json:val_accuracy', 5 | 'metrics:summary.json:val_loss', 6 | 'params:params.yaml:dropout', 7 | 'params:params.yaml:process.threshold' 8 | ] 9 | 10 | export default data 11 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/dataTypes/tableData.ts: -------------------------------------------------------------------------------- 1 | import columns from './columns' 2 | import defaultData from '../base/tableData' 3 | import rows from './rows' 4 | import { TableData } from '../../../../experiments/webview/contract' 5 | 6 | export const data: TableData = { 7 | ...defaultData, 8 | columns, 9 | hasRunningWorkspaceExperiment: false, 10 | rows, 11 | selectedForPlotsCount: 0 12 | } 13 | 14 | export default data 15 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/sorted/tableData.ts: -------------------------------------------------------------------------------- 1 | import columns from './columns' 2 | import rows from './rows' 3 | import defaultData from '../base/tableData' 4 | import { TableData } from '../../../../experiments/webview/contract' 5 | 6 | const data: TableData = { 7 | ...defaultData, 8 | columns, 9 | hasMoreCommits: { 'another-branch': true, main: true, 'other-branch': true }, 10 | rows, 11 | selectedForPlotsCount: 0, 12 | sorts: [ 13 | { 14 | path: 'params:params.yaml:epochs', 15 | descending: true 16 | } 17 | ] 18 | } 19 | 20 | export default data 21 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/expShow/survival/tableData.ts: -------------------------------------------------------------------------------- 1 | import { TableData } from '../../../../experiments/webview/contract' 2 | import rowsFixture from './rows' 3 | import columnsFixture from './columns' 4 | import defaultData from '../base/tableData' 5 | 6 | const data: TableData = { 7 | ...defaultData, 8 | columns: columnsFixture, 9 | rows: rowsFixture 10 | } 11 | 12 | export default data 13 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/comparison/index.ts: -------------------------------------------------------------------------------- 1 | import { getComparisonWebviewMessage } from '..' 2 | 3 | const data = getComparisonWebviewMessage('.') 4 | 5 | export default data 6 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/comparison/vscode.ts: -------------------------------------------------------------------------------- 1 | import { getComparisonWebviewMessage } from '..' 2 | import { Uri, ViewColumn, window } from 'vscode' 3 | import { ViewKey } from '../../../../webview/constants' 4 | import { basePlotsUrl } from '../../../util' 5 | 6 | const webviewPanel = window.createWebviewPanel( 7 | ViewKey.PLOTS, 8 | 'webview for asWebviewUri', 9 | ViewColumn.Active, 10 | { 11 | enableScripts: true 12 | } 13 | ) 14 | 15 | const baseUrl = webviewPanel.webview 16 | .asWebviewUri(Uri.file(basePlotsUrl)) 17 | .toString() 18 | 19 | webviewPanel.dispose() 20 | 21 | const uriJoin = (...segments: string[]) => segments.join('/') 22 | 23 | const data = getComparisonWebviewMessage(baseUrl, uriJoin) 24 | 25 | export default data 26 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/output/index.ts: -------------------------------------------------------------------------------- 1 | import { basePlotsUrl } from '../../../util' 2 | import { getOutput } from '..' 3 | 4 | const data = getOutput(basePlotsUrl) 5 | 6 | export default data 7 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/output/minimal.ts: -------------------------------------------------------------------------------- 1 | import { getMinimalOutput } from '..' 2 | 3 | const data = getMinimalOutput() 4 | 5 | export default data 6 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/revisions.ts: -------------------------------------------------------------------------------- 1 | import { getRevisions } from '.' 2 | 3 | const data = getRevisions() 4 | 5 | export default data 6 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/1ba7bcd_plots_acc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/1ba7bcd_plots_acc.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/1ba7bcd_plots_heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/1ba7bcd_plots_heatmap.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/1ba7bcd_plots_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/1ba7bcd_plots_loss.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/42b8736_plots_acc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/42b8736_plots_acc.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/42b8736_plots_heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/42b8736_plots_heatmap.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/42b8736_plots_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/42b8736_plots_loss.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/4fb124a_plots_acc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/4fb124a_plots_acc.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/4fb124a_plots_heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/4fb124a_plots_heatmap.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/4fb124a_plots_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/4fb124a_plots_loss.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/53c3851_plots_acc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/53c3851_plots_acc.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/53c3851_plots_heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/53c3851_plots_heatmap.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/53c3851_plots_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/53c3851_plots_loss.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/0.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/1.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/10.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/11.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/12.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/13.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/14.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/2.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/3.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/4.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/5.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/6.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/7.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/8.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/image/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/image/9.jpg -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/workspace_plots_acc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/workspace_plots_acc.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/workspace_plots_heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/workspace_plots_heatmap.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/staticImages/workspace_plots_loss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/extension/src/test/fixtures/plotsDiff/staticImages/workspace_plots_loss.png -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/template/index.ts: -------------------------------------------------------------------------------- 1 | import { getTemplateWebviewMessage } from '..' 2 | 3 | const data = getTemplateWebviewMessage() 4 | 5 | export default data 6 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/template/virtualization.ts: -------------------------------------------------------------------------------- 1 | import { getManyTemplatePlotsWebviewMessage } from '..' 2 | 3 | const manyTemplatePlots = (length: number) => 4 | getManyTemplatePlotsWebviewMessage(length) 5 | 6 | export default manyTemplatePlots 7 | -------------------------------------------------------------------------------- /extension/src/test/fixtures/plotsDiff/template/webview.ts: -------------------------------------------------------------------------------- 1 | import { getMinimalWebviewMessage } from '..' 2 | 3 | const data = getMinimalWebviewMessage() 4 | 5 | export default data 6 | -------------------------------------------------------------------------------- /extension/src/test/suite/cli/background.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '../../../common/logger' 2 | import { delay } from '../../../util/time' 3 | 4 | void delay(2000).then(() => { 5 | Logger.log(process.pid.toString()) 6 | void delay(30000) 7 | }) 8 | -------------------------------------------------------------------------------- /extension/src/test/suite/cli/child.ts: -------------------------------------------------------------------------------- 1 | import { getOptions } from './util' 2 | import { Logger } from '../../../common/logger' 3 | import { delay } from '../../../util/time' 4 | 5 | require('../../../vscode/mockModule') 6 | 7 | const importModuleAfterMockingVsCode = () => { 8 | const { Cli } = require('../../../cli') 9 | return { Cli } 10 | } 11 | 12 | const main = async () => { 13 | const { Cli } = importModuleAfterMockingVsCode() 14 | 15 | const cli = new Cli() 16 | 17 | const options = getOptions('background') 18 | const pid = await cli.createBackgroundProcess(options) 19 | 20 | Logger.log(pid) 21 | 22 | return delay(30000) 23 | } 24 | 25 | void main() 26 | -------------------------------------------------------------------------------- /extension/src/test/suite/cli/failed.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '../../../common/logger' 2 | import { delay } from '../../../util/time' 3 | 4 | void delay(2000).then(() => { 5 | Logger.log('this is some stdout') 6 | throw new Error('and this is some stderr') 7 | }) 8 | -------------------------------------------------------------------------------- /extension/src/test/suite/cli/util.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import type { ProcessOptions } from '../../../process/execution' 3 | 4 | export const getOptions = ( 5 | file: 'child' | 'background' | 'failed' 6 | ): ProcessOptions => ({ 7 | args: [join(__dirname, `${file}.js`)], 8 | cwd: __dirname, 9 | executable: 'node' 10 | }) 11 | -------------------------------------------------------------------------------- /extension/src/test/suite/timeouts.ts: -------------------------------------------------------------------------------- 1 | export const WEBVIEW_TEST_TIMEOUT = 16000 2 | 3 | export const WATCHER_TEST_TIMEOUT = 20000 4 | -------------------------------------------------------------------------------- /extension/src/test/util/index.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path' 2 | import { Memento, Uri, workspace, WorkspaceFolder } from 'vscode' 3 | 4 | const dvcRoot = resolve(__dirname, '..', '..', '..', '..', 'demo') 5 | export const dvcDemoPath = Uri.file(dvcRoot).fsPath 6 | export const basePlotsUrl = Uri.file( 7 | resolve(__dirname, '..', 'fixtures', 'plotsDiff', 'staticImages') 8 | ).fsPath 9 | 10 | export const getTestWorkspaceFolder = (): WorkspaceFolder => 11 | (workspace.workspaceFolders as WorkspaceFolder[])[0] 12 | 13 | export const buildMockMemento = ( 14 | values: Record = {} 15 | ): Memento => 16 | ({ 17 | get: (key: string, defaultValue: unknown) => values[key] || defaultValue, 18 | keys: () => Object.keys(values), 19 | update: (key: string, value: unknown) => { 20 | values[key] = value 21 | void Promise.resolve() 22 | } 23 | }) as unknown as Memento 24 | -------------------------------------------------------------------------------- /extension/src/test/util/path.ts: -------------------------------------------------------------------------------- 1 | // These functions mirror the vanilla path ones, but work in the browser for Storybook 2 | import path from 'path' 3 | const sep = path.sep || '/' 4 | export const join = (...segments: string[]): string => segments.join(sep) 5 | 6 | export const makeAbsPathSet = ( 7 | dvcRoot: string, 8 | ...relPaths: string[] 9 | ): Set => 10 | new Set(relPaths.map(relPath => path.resolve(dvcRoot, relPath))) 11 | -------------------------------------------------------------------------------- /extension/src/tree/decorationProvider/error.ts: -------------------------------------------------------------------------------- 1 | import { FileDecoration, Uri } from 'vscode' 2 | import { BaseDecorationProvider } from '.' 3 | import { getDecoratableUri } from '..' 4 | import { uniqueValues } from '../../util/array' 5 | 6 | export class ErrorDecorationProvider extends BaseDecorationProvider { 7 | private errors = new Set() 8 | 9 | public provideFileDecoration(uri: Uri): FileDecoration | undefined { 10 | if (this.errors.has(uri.fsPath)) { 11 | return ErrorDecorationProvider.DecorationError 12 | } 13 | } 14 | 15 | public setState(errors: Set = new Set()) { 16 | const urisToUpdate: Uri[] = [] 17 | 18 | for (const label of uniqueValues([...errors, ...this.errors])) { 19 | urisToUpdate.push(getDecoratableUri(label, this.scheme)) 20 | } 21 | this.errors = errors 22 | this.decorationsChanged.fire(urisToUpdate) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /extension/src/types.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | declare interface ProcessEnv { 3 | VSC_DEBUG?: 'true' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /extension/src/util/appdirs.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path' 2 | import { getProcessPlatform } from '../env' 3 | 4 | const { userConfigDir } = require('appdirs') as { 5 | userConfigDir: (appName: string) => string 6 | } 7 | 8 | export const getIterativeAppDir = (): string => userConfigDir('iterative') 9 | 10 | export const getDVCAppDir = (): string => { 11 | if (getProcessPlatform() === 'win32') { 12 | return join(getIterativeAppDir(), 'dvc') 13 | } 14 | return userConfigDir('dvc') 15 | } 16 | -------------------------------------------------------------------------------- /extension/src/util/disposable.ts: -------------------------------------------------------------------------------- 1 | import { Disposable } from '@hediet/std/disposable' 2 | 3 | export type Disposables = Record 4 | 5 | export const reset = ( 6 | disposables: Disposables, 7 | untrack: (disposable: T) => void 8 | ): Disposables => { 9 | for (const disposable of Object.values(disposables)) { 10 | untrack(disposable) 11 | disposable.dispose() 12 | } 13 | disposables = {} as Disposables 14 | return disposables 15 | } 16 | -------------------------------------------------------------------------------- /extension/src/util/env.ts: -------------------------------------------------------------------------------- 1 | import { delimiter } from 'path' 2 | import { joinTruthyItems } from './array' 3 | export const joinEnvPath = (...pathArray: string[]) => 4 | joinTruthyItems(pathArray, delimiter) 5 | -------------------------------------------------------------------------------- /extension/src/util/json.test.ts: -------------------------------------------------------------------------------- 1 | import { parseNonStandardJson } from './json' 2 | 3 | describe('parseNonStandardJson', () => { 4 | it('should parse NaN', () => { 5 | expect(parseNonStandardJson('{NotANumber:NaN}')).toStrictEqual({ 6 | NotANumber: Number.NaN 7 | }) 8 | }) 9 | 10 | it('should parse Infinity', () => { 11 | expect( 12 | parseNonStandardJson( 13 | '{negativeInfinity:-Infinity, positiveInfinity: Infinity}' 14 | ) 15 | ).toStrictEqual({ 16 | negativeInfinity: Number.NEGATIVE_INFINITY, 17 | positiveInfinity: Number.POSITIVE_INFINITY 18 | }) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /extension/src/util/json.ts: -------------------------------------------------------------------------------- 1 | import JSON5 from 'json5' 2 | 3 | export const parseNonStandardJson = (str: string): T => JSON5.parse(str) 4 | -------------------------------------------------------------------------------- /extension/src/util/map.test.ts: -------------------------------------------------------------------------------- 1 | import { flattenMapValues } from './map' 2 | 3 | describe('flattenMapValues', () => { 4 | it('should flatten a map of arrays', () => { 5 | expect( 6 | flattenMapValues( 7 | new Map([ 8 | ['A', [1]], 9 | ['B', [2, 3, 4]], 10 | ['C', [5, 6, 7, 8, 9, 10]] 11 | ]) 12 | ) 13 | ).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 14 | }) 15 | 16 | it('should return all of the original items', () => { 17 | expect( 18 | flattenMapValues( 19 | new Map([ 20 | ['A', [1]], 21 | ['B', [2, 3, 4, 5, 6, 7, 8]], 22 | ['C', [5, 6, 7, 8, 9, 10]] 23 | ]) 24 | ) 25 | ).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 5, 6, 7, 8, 9, 10]) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /extension/src/util/map.ts: -------------------------------------------------------------------------------- 1 | export const addToMapArray = ( 2 | map: Map, 3 | key: K, 4 | value: V 5 | ): void => { 6 | const existingArray = map.get(key) 7 | if (existingArray) { 8 | existingArray.push(value) 9 | } else { 10 | const newArray = [value] 11 | map.set(key, newArray) 12 | } 13 | } 14 | 15 | export const addToMapSet = ( 16 | map: Map>, 17 | key: K, 18 | value: V 19 | ): void => { 20 | const existingSet = map.get(key) 21 | if (existingSet) { 22 | existingSet.add(value) 23 | } else { 24 | const newSet = new Set([value]) 25 | map.set(key, newSet) 26 | } 27 | } 28 | 29 | export const flattenMapValues = (map: Map): T[] => { 30 | const iterator: IterableIterator = map.values() 31 | return [...iterator].flat() 32 | } 33 | -------------------------------------------------------------------------------- /extension/src/util/math.ts: -------------------------------------------------------------------------------- 1 | export const sum = (values: number[]): number => { 2 | let sum = 0 3 | for (const value of values) { 4 | sum += value 5 | } 6 | return sum 7 | } 8 | -------------------------------------------------------------------------------- /extension/src/util/object.test.ts: -------------------------------------------------------------------------------- 1 | import { createTypedAccumulator } from './object' 2 | 3 | describe('createTypedAccumulator', () => { 4 | it('should create a typed accumulator from an enum like object', () => { 5 | const obj = { A: 'a', B: 'b' } as const 6 | const typedAccumulator: Record<'a' | 'b', number> = 7 | createTypedAccumulator(obj) 8 | 9 | expect(typedAccumulator).toStrictEqual({ a: 0, b: 0 }) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /extension/src/util/object.ts: -------------------------------------------------------------------------------- 1 | export const hasKey = (maybeObject: unknown, key: string): boolean => 2 | typeof maybeObject === 'object' && 3 | Object.prototype.hasOwnProperty.call(maybeObject, key) 4 | 5 | export const createTypedAccumulator = ( 6 | enumLike: Record 7 | ) => { 8 | const acc = {} as Record 9 | for (const count of Object.values(enumLike)) { 10 | acc[count] = 0 11 | } 12 | return acc 13 | } 14 | -------------------------------------------------------------------------------- /extension/src/util/stdout.test.ts: -------------------------------------------------------------------------------- 1 | import { trimAndSplit } from './stdout' 2 | 3 | describe('trimAndSplit', () => { 4 | it('should return an empty array given an empty string', () => { 5 | expect(trimAndSplit('')).toStrictEqual([]) 6 | }) 7 | 8 | it('should return an empty array given an newline', () => { 9 | expect(trimAndSplit('\n')).toStrictEqual([]) 10 | }) 11 | 12 | it('should return an array given a string separated by newlines', () => { 13 | expect(trimAndSplit('a\nb\nc\n')).toStrictEqual(['a', 'b', 'c']) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /extension/src/util/stdout.ts: -------------------------------------------------------------------------------- 1 | export const trimAndSplit = (stdout: string): string[] => 2 | stdout 3 | .trim() 4 | .split('\n') 5 | .map(line => line.trim()) 6 | .filter(Boolean) 7 | -------------------------------------------------------------------------------- /extension/src/util/string.ts: -------------------------------------------------------------------------------- 1 | export const shortenForLabel = ( 2 | strOrNull: T 3 | ): T | string => (strOrNull ? strOrNull.slice(0, 7) : strOrNull) 4 | 5 | export const truncateFromLeft = (str: string, length: number): string => { 6 | return str.length > length ? '...' + str.slice(str.length - length) : str 7 | } 8 | 9 | export const truncate = (str: string, length: number): string => { 10 | return str.length > length ? str.slice(0, length - 1) + '...' : str 11 | } 12 | -------------------------------------------------------------------------------- /extension/src/util/time.ts: -------------------------------------------------------------------------------- 1 | export const delay = (ms: number): Promise => 2 | new Promise(resolve => setTimeout(resolve, ms)) 3 | 4 | export const getCurrentEpoch = (): number => Date.now() 5 | 6 | export class StopWatch { 7 | private started = getCurrentEpoch() 8 | 9 | public getElapsedTime() { 10 | return getCurrentEpoch() - this.started 11 | } 12 | 13 | public reset() { 14 | this.started = getCurrentEpoch() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /extension/src/vscode/clipboard.ts: -------------------------------------------------------------------------------- 1 | import { env } from 'vscode' 2 | import { Toast } from './toast' 3 | 4 | const { clipboard } = env 5 | 6 | export const writeToClipboard = async ( 7 | text: string, 8 | message?: string 9 | ): Promise => { 10 | await clipboard.writeText(text) 11 | void Toast.infoWithOptions(`${message || text} copied to clipboard`) 12 | } 13 | -------------------------------------------------------------------------------- /extension/src/vscode/context.test.ts: -------------------------------------------------------------------------------- 1 | import { commands } from 'vscode' 2 | import { ContextKey, setContextValue } from './context' 3 | 4 | jest.mock('vscode') 5 | 6 | const mockedCommands = jest.mocked(commands) 7 | const mockedExecuteCommand = jest.fn() 8 | mockedCommands.executeCommand = mockedExecuteCommand 9 | 10 | beforeEach(() => { 11 | jest.resetAllMocks() 12 | }) 13 | 14 | describe('setContextValue', () => { 15 | it('should pass the correct details to executeCommand', () => { 16 | const key = 'my important key' as ContextKey 17 | const value = 'value that if not set everything breaks' 18 | void setContextValue(key, value) 19 | expect(mockedExecuteCommand).toHaveBeenCalledWith('setContext', key, value) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /extension/src/vscode/external.ts: -------------------------------------------------------------------------------- 1 | import { env, ProviderResult, Uri, window } from 'vscode' 2 | 3 | export const openUrl = (url: string) => env.openExternal(Uri.parse(url)) 4 | 5 | export const getCallBackUrl = async (path: string) => { 6 | const uri = await env.asExternalUri( 7 | Uri.parse(`${env.uriScheme}://iterative.dvc${path}`) 8 | ) 9 | 10 | return uri.toString() 11 | } 12 | 13 | export const waitForUriResponse = (path: string, onResponse: () => unknown) => 14 | window.registerUriHandler({ 15 | handleUri(uri: Uri): ProviderResult { 16 | if (uri.path === path) { 17 | onResponse() 18 | } 19 | } 20 | }) 21 | -------------------------------------------------------------------------------- /extension/src/vscode/inputBox.test.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { getInput } from './inputBox' 3 | import { Title } from './title' 4 | 5 | jest.mock('vscode') 6 | 7 | const mockedWindow = jest.mocked(window) 8 | const mockedInputBox = mockedWindow.showInputBox 9 | 10 | beforeEach(() => { 11 | jest.resetAllMocks() 12 | }) 13 | 14 | describe('getInput', () => { 15 | it('should call window.showInputBox with the provided title', async () => { 16 | const aggressiveText = 'TELL ME WHAT YOU WANT' as Title 17 | await getInput(aggressiveText) 18 | expect(mockedInputBox).toHaveBeenCalledTimes(1) 19 | expect(mockedInputBox).toHaveBeenCalledWith({ title: aggressiveText }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /extension/src/vscode/markdownString.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownString } from 'vscode' 2 | 3 | export const getMarkdownString = (stringWithCodicons: string) => 4 | new MarkdownString(stringWithCodicons, true) 5 | -------------------------------------------------------------------------------- /extension/src/vscode/mockModule.ts: -------------------------------------------------------------------------------- 1 | import { URI } from 'vscode-uri' 2 | import { stub } from 'sinon' 3 | import mock from 'mock-require' 4 | 5 | class MockEventEmitter { 6 | public fire() { 7 | return stub() 8 | } 9 | 10 | public event() { 11 | return stub() 12 | } 13 | } 14 | 15 | mock('vscode', { 16 | EventEmitter: MockEventEmitter, 17 | Uri: { 18 | // eslint-disable-next-line @typescript-eslint/unbound-method 19 | file: URI.file 20 | }, 21 | commands: { executeCommand: () => undefined } 22 | }) 23 | 24 | mock('@hediet/std/disposable', { 25 | Disposable: { 26 | fn: () => ({ 27 | track: (disposable: T): T => disposable, 28 | untrack: () => undefined 29 | }) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /extension/src/vscode/modal.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { Response } from './response' 3 | 4 | enum Level { 5 | ERROR = 'Error', 6 | INFORMATION = 'Information', 7 | WARNING = 'Warning' 8 | } 9 | 10 | export class Modal { 11 | public static showInformation(text: string, ...items: Response[]) { 12 | return Modal.show(Level.INFORMATION, text, ...items) 13 | } 14 | 15 | public static warnOfConsequences(text: string, ...items: Response[]) { 16 | return Modal.show(Level.WARNING, text, ...items) 17 | } 18 | 19 | public static errorWithOptions(text: string, ...items: Response[]) { 20 | return Modal.show(Level.ERROR, text, ...items) 21 | } 22 | 23 | private static show( 24 | level: Level, 25 | message: string, 26 | ...items: Response[] 27 | ): Thenable { 28 | return window[`show${level}Message`](message, { modal: true }, ...items) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /extension/src/vscode/response.ts: -------------------------------------------------------------------------------- 1 | export enum Response { 2 | CANCEL = 'Cancel', 3 | CLOSE = 'Close', 4 | DISCARD = 'Discard Changes', 5 | FORCE = 'Force', 6 | MOVE = 'Move', 7 | NEVER = "Don't Show Again", 8 | NO = 'No', 9 | REMOVE = 'Remove', 10 | SELECT_MOST_RECENT = 'Select Most Recent', 11 | SHOW_SETUP = 'Setup', 12 | SHOW = 'Show', 13 | TURN_OFF = 'Turn Off', 14 | YES = 'Yes' 15 | } 16 | -------------------------------------------------------------------------------- /extension/src/vscode/walkthrough.ts: -------------------------------------------------------------------------------- 1 | import { commands } from 'vscode' 2 | import { RegisteredCommands } from '../commands/external' 3 | import { InternalCommands } from '../commands/internal' 4 | import { joinTruthyItems } from '../util/array' 5 | 6 | export const registerWalkthroughCommands = ( 7 | internalCommands: InternalCommands, 8 | extensionId: string, 9 | walkthroughId: string 10 | ) => { 11 | internalCommands.registerExternalCommand( 12 | RegisteredCommands.EXTENSION_GET_STARTED, 13 | () => 14 | commands.executeCommand( 15 | 'workbench.action.openWalkthrough', 16 | joinTruthyItems([extensionId, walkthroughId], '#') 17 | ) 18 | ) 19 | 20 | internalCommands.registerExternalCommand( 21 | RegisteredCommands.EXTENSION_SHOW_COMMANDS, 22 | () => commands.executeCommand('workbench.action.quickOpen', '> DVC') 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /extension/src/vscode/workspaceFolders.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from 'vscode' 2 | import { definedAndNonEmpty } from '../util/array' 3 | 4 | export const getWorkspaceFolderCount = () => 5 | (workspace.workspaceFolders || []).length 6 | 7 | export const getWorkspaceFolders = (): string[] => 8 | (workspace.workspaceFolders || []).map( 9 | workspaceFolder => workspaceFolder.uri.fsPath 10 | ) 11 | 12 | export const getWorkspaceRootUris = () => 13 | (workspace.workspaceFolders || []).map(workspaceFolder => workspaceFolder.uri) 14 | 15 | export const getFirstWorkspaceFolder = (): string | undefined => { 16 | const workspaceFolders = getWorkspaceFolders() 17 | return definedAndNonEmpty(workspaceFolders) ? workspaceFolders[0] : undefined 18 | } 19 | -------------------------------------------------------------------------------- /extension/src/workspace/util.ts: -------------------------------------------------------------------------------- 1 | import { quickPickOne } from '../vscode/quickPick' 2 | 3 | export const getOnlyOrPickProject = async ( 4 | dvcRoots: string[] 5 | ): Promise => { 6 | if (dvcRoots.length === 1) { 7 | return dvcRoots[0] 8 | } 9 | 10 | return await quickPickOne(dvcRoots, 'Select a Project to Run Command Against') 11 | } 12 | -------------------------------------------------------------------------------- /extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "module": "commonjs", 5 | "target": "es6", 6 | "outDir": "dist", 7 | "lib": ["es2020"], 8 | "sourceMap": true, 9 | "rootDir": "src", 10 | "strict": true, 11 | "skipLibCheck": true, 12 | "experimentalDecorators": true, 13 | "esModuleInterop": true 14 | }, 15 | "include": ["src/**/*"], 16 | "exclude": ["**/__mocks__/", "src/test/e2e/**"] 17 | } 18 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | projects: [ 3 | '/webview/jest.config.js', 4 | '/extension/jest.config.js', 5 | '/languageServer/jest.config.js' 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /languageServer/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const config = require('../.eslintrc') 2 | 3 | module.exports = { 4 | ...config, 5 | parser: '@typescript-eslint/parser', 6 | ignorePatterns: [...config.ignorePatterns, 'src/test/fixtures/**'], 7 | parserOptions: { 8 | tsconfigRootDir: __dirname, 9 | project: ['./tsconfig.json'] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /languageServer/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('../.lintstagedrc') 2 | -------------------------------------------------------------------------------- /languageServer/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | -------------------------------------------------------------------------------- /languageServer/index.d.ts: -------------------------------------------------------------------------------- 1 | import { DocumentFilter } from 'vscode-languageserver' 2 | 3 | export const serverModule: string 4 | export const documentSelector: DocumentFilter[] 5 | -------------------------------------------------------------------------------- /languageServer/index.js: -------------------------------------------------------------------------------- 1 | /* global module, require, __dirname */ 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | 4 | // This is the entry point when this package is used as library from the extension. 5 | // This package MUST NOT be bundled. 6 | const path = require('path') 7 | 8 | module.exports.serverModule = path.join(__dirname, 'dist', 'server.js') 9 | module.exports.documentSelector = [ 10 | { 11 | language: 'yaml' 12 | }, 13 | { 14 | pattern: '**/*.{dvc,dvc.lock}' 15 | }, 16 | { 17 | language: 'json' 18 | }, 19 | { 20 | language: 'toml' 21 | }, 22 | { 23 | language: 'python' 24 | } 25 | ] 26 | -------------------------------------------------------------------------------- /languageServer/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | coverageDirectory: 'coverage/jest', 3 | coveragePathIgnorePatterns: ['/src/test/', '/node_modules/'], 4 | coverageReporters: ['json'], 5 | testEnvironment: 'node', 6 | testPathIgnorePatterns: ['/dist/'], 7 | transform: { 8 | '^.+\\.(t|j)sx?$': ['@swc/jest'] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /languageServer/src/server.ts: -------------------------------------------------------------------------------- 1 | import { createConnection, ProposedFeatures } from 'vscode-languageserver/node' 2 | import { LanguageServer } from './languageServer' 3 | 4 | const languageServer = new LanguageServer() 5 | 6 | const connection = createConnection(ProposedFeatures.all) 7 | 8 | languageServer.listen(connection) 9 | -------------------------------------------------------------------------------- /languageServer/src/test/fixtures/params/index.ts: -------------------------------------------------------------------------------- 1 | export const params = ` 2 | lr: 0.003 3 | weight_decay: 0 4 | epochs: 15 5 | auc: 0.9 6 | loss: 0.2 7 | ` 8 | -------------------------------------------------------------------------------- /languageServer/src/test/fixtures/python/index.ts: -------------------------------------------------------------------------------- 1 | export const train = `from secret_notebook import model 2 | 3 | def main(): 4 | """Don't tell the boss that the model in production is actually a notebook on my laptop.""" 5 | model.train() 6 | 7 | if __name__ == "__main__": 8 | main() 9 | ` 10 | -------------------------------------------------------------------------------- /languageServer/src/test/utils/requestDefinitions.ts: -------------------------------------------------------------------------------- 1 | import { TextDocument } from 'vscode-languageserver-textdocument' 2 | import { DefinitionParams, DefinitionRequest } from 'vscode-languageserver/node' 3 | import { client } from './setup-test-connections' 4 | 5 | export const requestDefinitions = async ( 6 | textDocument: TextDocument, 7 | substring: string, 8 | offset = 0 9 | ) => { 10 | const text = textDocument.getText() 11 | const uri = textDocument.uri 12 | 13 | const symbolOffset = text.indexOf(substring) 14 | 15 | const { character, line } = textDocument.positionAt(symbolOffset) 16 | 17 | const params: DefinitionParams = { 18 | position: { character: character + offset, line }, 19 | textDocument: { 20 | uri 21 | } 22 | } 23 | return await client.sendRequest(DefinitionRequest.type, params) 24 | } 25 | -------------------------------------------------------------------------------- /languageServer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "resolveJsonModule": true, 4 | "target": "es6", 5 | "lib": ["es2020"], 6 | "module": "commonjs", 7 | "esModuleInterop": true, 8 | "sourceMap": true, 9 | "strict": true, 10 | "outDir": "dist", 11 | "skipLibCheck": true, 12 | "experimentalDecorators": true, 13 | "isolatedModules": true, 14 | "rootDir": "src" 15 | }, 16 | "include": ["src"] 17 | } 18 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignoreDeps": [ 3 | "@types/node", 4 | "@types/vscode", 5 | "@vscode/extension-telemetry", 6 | "execa", 7 | "process-exists", 8 | "webdriverio" 9 | ], 10 | "extends": ["config:base"], 11 | "packageRules": [ 12 | { 13 | "internalChecksFilter": "strict", 14 | "matchDatasources": ["npm"], 15 | "prConcurrentLimit": 1, 16 | "stabilityDays": 7 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /scripts/install-frozen-lockfile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # In yarn v1, --frozen-lockfile is completely 4 | # broken when combined with Yarn workspaces. See https://github.com/yarnpkg/yarn/issues/6291 5 | 6 | CKSUM_BEFORE=$(cksum yarn.lock) 7 | yarn install 8 | CKSUM_AFTER=$(cksum yarn.lock) 9 | 10 | 11 | if [[ "$CKSUM_BEFORE" != "$CKSUM_AFTER" ]]; then 12 | echo "Changes were detected in yarn.lock file after running 'yarn install', which is not expected - terminating." 13 | echo "Please ensure that the correct version of the yarn.lock file has been committed." 14 | exit 1 15 | fi 16 | -------------------------------------------------------------------------------- /scripts/virtualenv-install.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { join, resolve } from 'path' 3 | require('dvc/src/vscode/mockModule') 4 | 5 | const importModuleAfterMockingVsCode = () => { 6 | const { setupTestVenv } = require('dvc/src/python') 7 | 8 | return setupTestVenv 9 | } 10 | 11 | const setupTestVenv = importModuleAfterMockingVsCode() 12 | 13 | const cwd = resolve(__dirname, '..', 'demo') 14 | 15 | setupTestVenv(cwd, '.env', '-r', join('.', 'requirements.txt')) 16 | -------------------------------------------------------------------------------- /webview/.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | const baseLintStaged = require('../.lintstagedrc') 2 | 3 | module.exports = { 4 | ...baseLintStaged, 5 | '**/*.scss': 'stylelint --fix' 6 | } 7 | -------------------------------------------------------------------------------- /webview/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | storybook-static 3 | coverage 4 | -------------------------------------------------------------------------------- /webview/.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'stylelint-config-standard-scss', 3 | rules: { 4 | 'max-nesting-depth': 2, 5 | 'selector-max-attribute': 2, 6 | 'selector-max-class': 2, 7 | 'selector-max-combinators': 3, 8 | 'selector-max-type': 2, 9 | 'selector-pseudo-class-no-unknown': [ 10 | true, 11 | { ignorePseudoClasses: ['global'] } 12 | ], 13 | 'selector-class-pattern': [ 14 | '^[a-z][a-zA-Z0-9]+$', 15 | { 16 | message: selector => 17 | `Expected class selector "${selector}" to be camelCase` 18 | } 19 | ], 20 | 'scss/percent-placeholder-pattern': null, 21 | 'scss/dollar-variable-empty-line-before': [ 22 | 'always', 23 | { 24 | except: ['after-comment', 'after-dollar-variable'] 25 | } 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /webview/icons/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /webview/icons/codicons.mjs: -------------------------------------------------------------------------------- 1 | export const codicons = [ 2 | 'add', 3 | 'arrow-up', 4 | 'arrow-down', 5 | 'beaker', 6 | 'check', 7 | 'chevron-down', 8 | 'chevron-right', 9 | 'close', 10 | 'cloud-upload', 11 | 'cloud', 12 | 'copy', 13 | 'discard', 14 | 'ellipsis', 15 | 'error', 16 | 'extensions', 17 | 'filter', 18 | 'git-commit', 19 | 'git-merge', 20 | 'graph-line', 21 | 'graph-scatter', 22 | 'gripper', 23 | 'info', 24 | 'list-filter', 25 | 'link', 26 | 'pass-filled', 27 | 'pinned', 28 | 'refresh', 29 | 'remove', 30 | 'sort-precedence', 31 | 'star-empty', 32 | 'star-full', 33 | 'table', 34 | 'trash', 35 | 'warning' 36 | ] 37 | -------------------------------------------------------------------------------- /webview/index.d.ts: -------------------------------------------------------------------------------- 1 | export const distPath: string 2 | export const experiments: string 3 | export const plots: string 4 | export const setup: string 5 | export const react: string 6 | -------------------------------------------------------------------------------- /webview/index.js: -------------------------------------------------------------------------------- 1 | /* global module, require, __dirname */ 2 | /* eslint-disable @typescript-eslint/no-var-requires */ 3 | 4 | // This is the entry point when this package is used as library from the extension. 5 | // This package MUST NOT be bundled. 6 | const path = require('path') 7 | 8 | module.exports.distPath = path.join(__dirname, 'dist') 9 | module.exports.experiments = path.join(__dirname, 'dist/experiments.js') 10 | module.exports.plots = path.join(__dirname, 'dist/plots.js') 11 | module.exports.setup = path.join(__dirname, 'dist/setup.js') 12 | module.exports.react = path.join(__dirname, 'dist/react.js') 13 | -------------------------------------------------------------------------------- /webview/jest.config.js: -------------------------------------------------------------------------------- 1 | /* global module */ 2 | 3 | module.exports = { 4 | coverageDirectory: 'coverage/jest', 5 | coveragePathIgnorePatterns: [ 6 | '/src/test/', 7 | '/node_modules/', 8 | '/src/stories/', 9 | '/src/util/wdyr.ts' 10 | ], 11 | coverageReporters: ['json'], 12 | globals: { 13 | __webpack_public_path__: true 14 | }, 15 | moduleNameMapper: { 16 | '\\.(scss|css|less)$': 'identity-obj-proxy' 17 | }, 18 | setupFiles: ['jest-canvas-mock', '/setup-tests.js'], 19 | testEnvironment: 'jsdom', 20 | testTimeout: 20000, 21 | transform: { 22 | '\\.py$': '/rawLoaderTransformer.js', 23 | '^.+\\.(t|j)sx?$': ['@swc/jest'] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webview/rawLoaderTransformer.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | process: content => { 3 | return { code: 'module.exports = ' + JSON.stringify(content) } 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /webview/setup-tests.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-global-assign 2 | window = { 3 | addEventListener: jest.fn(), 4 | dispatchEvent: jest.fn() 5 | } 6 | 7 | for (const observer of ['IntersectionObserver', 'ResizeObserver']) { 8 | global[observer] = jest.fn().mockImplementation(() => { 9 | return { 10 | disconnect: jest.fn(), 11 | observe: jest.fn(), 12 | unobserve: jest.fn() 13 | } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /webview/src/css.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css' 2 | declare module '*.scss' 3 | -------------------------------------------------------------------------------- /webview/src/experiments/components/emptyState/ErrorState.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 3 | import { ErrorIcon } from '../../../shared/components/errorIcon/ErrorIcon' 4 | import { RefreshButton } from '../../../shared/components/button/RefreshButton' 5 | import { refreshData } from '../../util/messages' 6 | 7 | export const ErrorState: React.FC<{ cliError: string }> = ({ cliError }) => ( 8 | 9 | 10 |

No Experiments to Display.

11 | 12 |
13 | ) 14 | -------------------------------------------------------------------------------- /webview/src/experiments/components/overflowHoverTooltip/OverflowHoverTooltip.tsx: -------------------------------------------------------------------------------- 1 | import { TippyProps } from '@tippyjs/react' 2 | import React, { useRef } from 'react' 3 | import { useIsFullyContained } from './useIsFullyContained' 4 | import Tooltip, { 5 | HEADER_TOOLTIP_DELAY 6 | } from '../../../shared/components/tooltip/Tooltip' 7 | 8 | export const OverflowHoverTooltip: React.FC< 9 | Pick 10 | > = ({ children, content }) => { 11 | const wrapperRef = useRef(null) 12 | const isDisabled = useIsFullyContained(wrapperRef) 13 | return ( 14 | 21 | {children} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /webview/src/experiments/components/overflowHoverTooltip/useIsFullyContained.ts: -------------------------------------------------------------------------------- 1 | import { RefObject, useMemo } from 'react' 2 | 3 | export const useIsFullyContained = (wrapperRef: RefObject) => { 4 | const { scrollWidth, clientWidth } = wrapperRef.current || {} 5 | return useMemo( 6 | () => 7 | !( 8 | wrapperRef.current && (clientWidth as number) < (scrollWidth as number) 9 | ), 10 | [wrapperRef, scrollWidth, clientWidth] 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/CounterBadge.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | export type CounterBadgeProps = { 5 | count?: number 6 | } 7 | 8 | export const CounterBadge: React.FC = ({ count }) => { 9 | return count ? ( 10 | 15 | {count} 16 | 17 | ) : null 18 | } 19 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/ClickableTooltipContent.tsx: -------------------------------------------------------------------------------- 1 | import React, { MouseEventHandler } from 'react' 2 | import styles from '../styles.module.scss' 3 | 4 | type ClickableTooltipContentProps = { 5 | clickableText: string 6 | helperText: string 7 | onClick: MouseEventHandler 8 | } 9 | 10 | export const ClickableTooltipContent: React.FC< 11 | ClickableTooltipContentProps 12 | > = ({ clickableText, helperText, onClick }) => ( 13 | 14 | {helperText} 15 |
16 | 19 |
20 | ) 21 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/NestedRow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { TableRow } from './Row' 3 | import styles from '../styles.module.scss' 4 | import { RowProp } from '../../../util/interfaces' 5 | 6 | export const NestedRow: React.FC = ({ 7 | row, 8 | isExpanded 9 | }) => { 10 | return ( 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/OnRemote.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { CopyStudioLink } from './CopyStudioLink' 3 | import { CellHintTooltip } from './CellHintTooltip' 4 | import styles from '../styles.module.scss' 5 | import { Icon } from '../../../../shared/components/Icon' 6 | import { Cloud } from '../../../../shared/components/icons' 7 | 8 | export const OnRemote: React.FC<{ id: string; showLinkIcon: boolean }> = ({ 9 | id, 10 | showLinkIcon 11 | }) => { 12 | if (showLinkIcon) { 13 | return 14 | } 15 | return ( 16 | 17 |
18 | 19 |
20 |
21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/Progress.tsx: -------------------------------------------------------------------------------- 1 | import { VSCodeProgressRing } from '@vscode/webview-ui-toolkit/react' 2 | import React from 'react' 3 | import cx from 'classnames' 4 | import styles from '../styles.module.scss' 5 | 6 | export const Progress: React.FC = () => ( 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/RowExpansionButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | import { RowProp } from '../../../util/interfaces' 4 | 5 | export const RowExpansionButton: React.FC = ({ row }) => 6 | row.getCanExpand() ? ( 7 | 27 | ) : ( 28 | 29 | ) 30 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/SortedTableContent.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment, RefObject } from 'react' 2 | import { Row } from '@tanstack/react-table' 3 | import { Experiment } from 'dvc/src/experiments/webview/contract' 4 | import { TableBody } from './TableBody' 5 | 6 | interface SortedTableContentProps { 7 | rows: Row[] 8 | tableRef: RefObject 9 | tableHeadHeight: number 10 | } 11 | 12 | export const SortedTableContent: React.FC = ({ 13 | rows, 14 | tableHeadHeight, 15 | tableRef 16 | }) => { 17 | return ( 18 | <> 19 | {rows.map((row, i) => ( 20 | 27 | ))} 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/WorkspaceRowGroup.tsx: -------------------------------------------------------------------------------- 1 | import cx from 'classnames' 2 | import React, { PropsWithChildren, ReactNode } from 'react' 3 | import { useInView } from 'react-intersection-observer' 4 | import styles from '../styles.module.scss' 5 | 6 | interface WorkspaceRowGroupProps { 7 | root: HTMLElement | null 8 | tableHeaderHeight: number 9 | children: ReactNode 10 | } 11 | 12 | export const WorkspaceRowGroup: React.FC< 13 | PropsWithChildren 14 | > = ({ children, root, tableHeaderHeight }) => { 15 | const [ref, needsShadow] = useInView({ 16 | root, 17 | rootMargin: `-${tableHeaderHeight + 15}px 0px 0px 0px`, 18 | threshold: 1 19 | }) 20 | 21 | return ( 22 | 26 | {children} 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/branchDivider/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../../../shared/variables'; 2 | 3 | .branchName { 4 | padding: 0 20px; 5 | display: flex; 6 | align-items: center; 7 | gap: 4px; 8 | font-size: 0.6rem; 9 | } 10 | 11 | .icon { 12 | fill: variables.$accent-color; 13 | } 14 | 15 | .branchActions { 16 | text-align: left; 17 | } 18 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/body/commits/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../../../shared/variables'; 2 | 3 | .commitsNav { 4 | text-align: left; 5 | font-size: 0.6rem; 6 | display: flex; 7 | align-items: center; 8 | } 9 | 10 | .commitsButton { 11 | background: transparent; 12 | border: none; 13 | color: variables.$meta-cell-color; 14 | text-decoration: underline; 15 | cursor: pointer; 16 | 17 | &:disabled { 18 | opacity: 0.5; 19 | cursor: auto; 20 | } 21 | 22 | &:hover:not(:disabled) { 23 | text-decoration: none; 24 | } 25 | } 26 | 27 | .commitsIcon { 28 | width: 10px; 29 | height: 10px; 30 | } 31 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/CellContent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | 4 | interface CellContentProps { 5 | children: string 6 | } 7 | 8 | export const CellContents: React.FC = ({ children }) => ( 9 | {children} 10 | ) 11 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/CellTooltip.tsx: -------------------------------------------------------------------------------- 1 | import React, { SyntheticEvent } from 'react' 2 | import styles from '../styles.module.scss' 3 | 4 | interface CellTooltipProps { 5 | children: string 6 | } 7 | 8 | export const CellTooltip: React.FC = ({ children }) => { 9 | const stopPropagation = (e: SyntheticEvent) => e.stopPropagation() 10 | return ( 11 |
18 | {children} 19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/ErrorCell.tsx: -------------------------------------------------------------------------------- 1 | import cx from 'classnames' 2 | import React from 'react' 3 | import { CellContents } from './CellContent' 4 | import { ErrorTooltip } from '../../../../shared/components/tooltip/ErrorTooltip' 5 | import styles from '../styles.module.scss' 6 | 7 | interface ErrorCellProps { 8 | error: string 9 | } 10 | 11 | export const ErrorCell: React.FC = ({ error }) => ( 12 | 13 |
14 | ! 15 |
16 |
17 | ) 18 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/ExperimentHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | 4 | export const ExperimentHeader: React.FC = () => ( 5 |
Experiment
6 | ) 7 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | import { OverflowHoverTooltip } from '../../overflowHoverTooltip/OverflowHoverTooltip' 4 | 5 | interface HeaderProps { 6 | name: string 7 | } 8 | 9 | export const Header: React.FC = ({ name }) => { 10 | return ( 11 | 12 |
13 | {name} 14 |
15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/SortedTableHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | import { Icon } from '../../../../shared/components/Icon' 4 | import { Info } from '../../../../shared/components/icons' 5 | import Tooltip from '../../../../shared/components/tooltip/Tooltip' 6 | 7 | export const SortedTableHeader: React.FC<{ name: string }> = ({ name }) => ( 8 | 9 | 10 | 11 | {name} 12 | 13 | 14 | 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/TimestampHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | 4 | export const TimestampHeader: React.FC = () => ( 5 |
Created
6 | ) 7 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/content/UndefinedCell.tsx: -------------------------------------------------------------------------------- 1 | import { CellContext } from '@tanstack/react-table' 2 | import { Experiment } from 'dvc/src/experiments/webview/contract' 3 | import React from 'react' 4 | import { CellContents } from './CellContent' 5 | import { CellValue } from './Cell' 6 | import styles from '../styles.module.scss' 7 | 8 | interface UndefinedCellProps { 9 | cell: CellContext 10 | } 11 | 12 | export const UndefinedCell: React.FC = ({ cell }) => { 13 | const { 14 | column: { id: columnId }, 15 | row: { 16 | original: { id: rowId } 17 | } 18 | } = cell 19 | return ( 20 |
21 | {columnId === 'Created' && rowId === 'workspace' ? ( 22 | <> 23 | ) : ( 24 | - 25 | )} 26 |
27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /webview/src/experiments/components/table/header/WithExpColumnNeedsShadowUpdates.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, useEffect } from 'react' 2 | import { useInView } from 'react-intersection-observer' 3 | 4 | export const WithExpColumnNeedsShadowUpdates: React.FC<{ 5 | children: ReactNode 6 | setExpColumnNeedsShadow: (needsShadow: boolean) => void 7 | root: HTMLElement | null 8 | }> = ({ root, setExpColumnNeedsShadow, children }) => { 9 | const [ref, needsShadow] = useInView({ 10 | initialInView: true, 11 | root, 12 | rootMargin: '0px 0px 0px -2px', 13 | threshold: 1 14 | }) 15 | 16 | useEffect(() => { 17 | setExpColumnNeedsShadow(needsShadow) 18 | }, [needsShadow, setExpColumnNeedsShadow]) 19 | 20 | return
{children}
21 | } 22 | -------------------------------------------------------------------------------- /webview/src/experiments/fonts/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/webview/src/experiments/fonts/Roboto-Light.ttf -------------------------------------------------------------------------------- /webview/src/experiments/fonts/RobotoMono-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iterative/vscode-dvc/95d8c2f3a192ee7a4acce8c18ffb4c822dc670a8/webview/src/experiments/fonts/RobotoMono-Light.ttf -------------------------------------------------------------------------------- /webview/src/experiments/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import { Provider } from 'react-redux' 4 | import '../shared/styles.scss' 5 | import { App } from './components/App' 6 | import { experimentsStore } from './store' 7 | 8 | const root = ReactDOM.createRoot(document.querySelector('#root') as HTMLElement) 9 | root.render( 10 | 11 | 12 | 13 | ) 14 | -------------------------------------------------------------------------------- /webview/src/experiments/state/headerDropTargetSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit' 2 | 3 | export const headerDropTargetSlice = createSlice({ 4 | initialState: '', 5 | name: 'headerDropTarget', 6 | reducers: { 7 | setDropTarget: (_, action: PayloadAction) => { 8 | return action.payload || '' 9 | } 10 | } 11 | }) 12 | 13 | export const { setDropTarget } = headerDropTargetSlice.actions 14 | 15 | export default headerDropTargetSlice.reducer 16 | -------------------------------------------------------------------------------- /webview/src/experiments/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit' 2 | import tableDataReducer from './state/tableDataSlice' 3 | import rowSelectionReducer from './state/rowSelectionSlice' 4 | import headerDropTargetReducer from './state/headerDropTargetSlice' 5 | 6 | export const experimentsReducers = { 7 | headerDropTarget: headerDropTargetReducer, 8 | rowSelection: rowSelectionReducer, 9 | tableData: tableDataReducer 10 | } 11 | 12 | export const experimentsStore = configureStore({ 13 | reducer: experimentsReducers 14 | }) 15 | 16 | export type ExperimentsState = ReturnType 17 | export type ExperimentsDispatch = typeof experimentsStore.dispatch 18 | -------------------------------------------------------------------------------- /webview/src/experiments/util/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { Experiment } from 'dvc/src/experiments/webview/contract' 2 | import { Cell, Row, Table } from '@tanstack/react-table' 3 | 4 | export interface InstanceProp { 5 | instance: Table 6 | } 7 | 8 | export interface RowProp { 9 | row: Row 10 | hasRunningWorkspaceExperiment?: boolean 11 | } 12 | 13 | export interface CellProp { 14 | cell: Cell 15 | } 16 | -------------------------------------------------------------------------------- /webview/src/experiments/util/rows.ts: -------------------------------------------------------------------------------- 1 | export const getCompositeId = ( 2 | id: string, 3 | branch: string | undefined | null = '' 4 | ) => `${id}-${branch}` 5 | -------------------------------------------------------------------------------- /webview/src/plots/components/DropTarget.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import cx from 'classnames' 3 | import styles from './styles.module.scss' 4 | import { Icon } from '../../shared/components/Icon' 5 | import { GraphLine } from '../../shared/components/icons' 6 | 7 | export const DropTarget: React.FC = () => ( 8 |
13 | 14 |
15 | ) 16 | -------------------------------------------------------------------------------- /webview/src/plots/components/LoadingSection.tsx: -------------------------------------------------------------------------------- 1 | import { Revision } from 'dvc/src/plots/webview/contract' 2 | import React from 'react' 3 | import { EmptyState } from '../../shared/components/emptyState/EmptyState' 4 | 5 | export const sectionIsLoading = ( 6 | selectedRevisions: Revision[], 7 | hasData: boolean 8 | ): boolean => 9 | selectedRevisions.length > 0 && 10 | !selectedRevisions.some(({ fetched }) => fetched) && 11 | !hasData 12 | 13 | export const LoadingSection: React.FC = () => ( 14 | Loading... 15 | ) 16 | -------------------------------------------------------------------------------- /webview/src/plots/components/PathHighlight.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | export const PathHighlight: React.FC = ({ children }) => ( 5 | {children} 6 | ) 7 | -------------------------------------------------------------------------------- /webview/src/plots/components/Plots.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PlotsContent } from './PlotsContent' 3 | import { WebviewWrapper } from '../../shared/components/webviewWrapper/WebviewWrapper' 4 | 5 | export const Plots = () => ( 6 | 7 | 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /webview/src/plots/components/comparisonTable/ComparisonTableWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { PlotsSection } from 'dvc/src/plots/webview/contract' 2 | import React from 'react' 3 | import { useSelector } from 'react-redux' 4 | import { ComparisonTable } from './ComparisonTable' 5 | import { PlotsContainer } from '../PlotsContainer' 6 | import { PlotsState } from '../../store' 7 | 8 | export const ComparisonTableWrapper: React.FC = () => { 9 | const { width, isCollapsed, height, plots } = useSelector( 10 | (state: PlotsState) => state.comparison 11 | ) 12 | 13 | return ( 14 | 0} 21 | noHeight 22 | > 23 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /webview/src/plots/components/comparisonTable/DropTarget.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | export const DropTarget: React.FC = () => ( 5 |
6 | ) 7 | -------------------------------------------------------------------------------- /webview/src/plots/components/comparisonTable/cell/ComparisonTableLoadingCell.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from '../styles.module.scss' 3 | 4 | export const ComparisonTableLoadingCell: React.FC = () => ( 5 |
6 |

Loading...

7 |
8 | ) 9 | -------------------------------------------------------------------------------- /webview/src/plots/components/customPlots/CustomPlotsGrid.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PlotsSection } from 'dvc/src/plots/webview/contract' 3 | import { Grid } from '../Grid' 4 | 5 | interface CustomPlotsGridProps { 6 | nbItemsPerRow: number 7 | order: string[] 8 | parentDraggedOver: boolean 9 | useVirtualizedGrid?: boolean 10 | setOrder: (order: string[]) => void 11 | } 12 | 13 | export const CustomPlotsGrid: React.FC = ({ 14 | nbItemsPerRow, 15 | parentDraggedOver, 16 | order, 17 | setOrder, 18 | useVirtualizedGrid 19 | }) => { 20 | return ( 21 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /webview/src/plots/components/customPlots/NoPlotsAdded.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { StartButton } from '../../../shared/components/button/StartButton' 3 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 4 | import { addPlot } from '../../util/messages' 5 | 6 | export const NoPlotsAdded: React.FC = () => { 7 | return ( 8 | 9 |

No Plots Added

10 | 11 |
12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /webview/src/plots/components/emptyState/EmptyState.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { CustomPlotsWrapper } from '../customPlots/CustomPlotsWrapper' 4 | import { EmptyState as SharedEmptyState } from '../../../shared/components/emptyState/EmptyState' 5 | 6 | export const EmptyState: React.FC<{ 7 | hasCustomPlots: boolean 8 | modal: React.ReactNode 9 | children: React.ReactNode 10 | }> = ({ children, hasCustomPlots, modal }) => ( 11 |
12 | 13 | {children} 14 | 15 | {hasCustomPlots && ( 16 | <> 17 | 18 | {modal} 19 | 20 | )} 21 |
22 | ) 23 | -------------------------------------------------------------------------------- /webview/src/plots/components/emptyState/GetStarted.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { AddPlots } from './AddPlots' 3 | import { Welcome } from './Welcome' 4 | import { EmptyState } from './EmptyState' 5 | 6 | type GetStartedProps = { 7 | hasCustomPlots: boolean 8 | hasPlots: boolean 9 | hasUnselectedPlots: boolean 10 | modal: React.ReactNode 11 | } 12 | 13 | export const GetStarted: React.FC = ({ 14 | hasCustomPlots, 15 | hasPlots, 16 | hasUnselectedPlots, 17 | modal 18 | }) => ( 19 | 20 | {hasPlots ? ( 21 | 22 | ) : ( 23 | 24 | )} 25 | 26 | ) 27 | -------------------------------------------------------------------------------- /webview/src/plots/components/emptyState/WaitForPlotsInfo.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { Icon } from '../../../shared/components/Icon' 4 | import { Info } from '../../../shared/components/icons' 5 | 6 | export const WaitForPlotsInfo: React.FC = () => ( 7 |

8 | 9 | If you have selected experiments that have just started, you might need to 10 | wait while plots are produced. The screen will be updated automatically if 11 | plots are setup correctly in this case. 12 |

13 | ) 14 | -------------------------------------------------------------------------------- /webview/src/plots/components/emptyState/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../shared/variables'; 2 | 3 | .emptyStateWrapper { 4 | display: flex; 5 | flex-direction: column; 6 | height: 100vh; 7 | width: 100%; 8 | 9 | > *:first-child { 10 | flex-grow: 1; 11 | } 12 | } 13 | 14 | .infoText { 15 | font-size: 0.8rem; 16 | 17 | svg { 18 | fill: variables.$accent-color; 19 | vertical-align: text-bottom; 20 | margin-right: 4px; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/plots/components/ribbon/RevisionIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import cx from 'classnames' 3 | import { VSCodeProgressRing } from '@vscode/webview-ui-toolkit/react' 4 | import styles from './styles.module.scss' 5 | 6 | export const RevisionIcon: React.FC<{ 7 | fetched: boolean 8 | errors?: string[] 9 | }> = ({ fetched, errors }) => ( 10 |
11 | {fetched && errors && '!'} 12 | {!fetched && ( 13 | 14 | )} 15 |
16 | ) 17 | -------------------------------------------------------------------------------- /webview/src/plots/components/ribbon/ribbonSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit' 2 | 3 | export interface RibbonState { 4 | height: number 5 | } 6 | 7 | export const ribbonInitialState: RibbonState = { 8 | height: 0 9 | } 10 | 11 | export const ribbonSlice = createSlice({ 12 | initialState: ribbonInitialState, 13 | name: 'ribbon', 14 | reducers: { 15 | update: (_, action: PayloadAction) => { 16 | return { 17 | height: action.payload 18 | } 19 | } 20 | } 21 | }) 22 | 23 | export const { update } = ribbonSlice.actions 24 | 25 | export default ribbonSlice.reducer 26 | -------------------------------------------------------------------------------- /webview/src/plots/components/templatePlots/TemplatePlotsWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { PlotsSection } from 'dvc/src/plots/webview/contract' 2 | import React from 'react' 3 | import { useSelector } from 'react-redux' 4 | import { TemplatePlots } from './TemplatePlots' 5 | import { PlotsContainer } from '../PlotsContainer' 6 | import { PlotsState } from '../../store' 7 | 8 | export const TemplatePlotsWrapper: React.FC = () => { 9 | const { nbItemsPerRow, isCollapsed, height, hasItems } = useSelector( 10 | (state: PlotsState) => state.template 11 | ) 12 | 13 | return ( 14 | 22 | 23 | 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /webview/src/plots/hooks/useModalOpenClass.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | 3 | export const useModalOpenClass = () => { 4 | useEffect(() => { 5 | const modalOpenClass = 'modalOpen' 6 | document.body.classList.add(modalOpenClass) 7 | 8 | return () => { 9 | document.body.classList.remove(modalOpenClass) 10 | } 11 | }, []) 12 | } 13 | -------------------------------------------------------------------------------- /webview/src/plots/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import { Provider } from 'react-redux' 4 | import '../shared/styles.scss' 5 | import { App } from './components/App' 6 | import '../util/wdyr' 7 | import { plotsStore } from './store' 8 | 9 | const root = ReactDOM.createRoot(document.querySelector('#root') as HTMLElement) 10 | root.render( 11 | 12 | 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /webview/src/py.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.py' 2 | -------------------------------------------------------------------------------- /webview/src/setup/components/dvc/DvcUnitialized.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 3 | import { Button } from '../../../shared/components/button/Button' 4 | import { initializeDvc } from '../../util/messages' 5 | 6 | export const DvcUninitialized: React.FC = ({ children }) => ( 7 | 8 |

DVC is not initialized

9 | {children} 10 |

11 | The current workspace does not contain a DVC project, which is needed to 12 | enable DVC-powered features. 13 |

14 | 15 |
16 | ) 17 | -------------------------------------------------------------------------------- /webview/src/setup/components/dvc/ProjectUninitialized.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import { useSelector } from 'react-redux' 3 | import { GitUninitialized } from './GitUnitialized' 4 | import { DvcUninitialized } from './DvcUnitialized' 5 | import { SetupState } from '../../store' 6 | 7 | export const ProjectUninitialized: React.FC = ({ 8 | children 9 | }) => { 10 | const needsGitInitialized = useSelector( 11 | (state: SetupState) => state.dvc.needsGitInitialized 12 | ) 13 | 14 | if (needsGitInitialized) { 15 | return {children} 16 | } 17 | 18 | return {children} 19 | } 20 | -------------------------------------------------------------------------------- /webview/src/setup/components/dvc/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../shared/variables'; 2 | @use '../../../shared/mixins'; 3 | 4 | .inlineWarningSvg { 5 | vertical-align: middle; 6 | fill: variables.$warn-color; 7 | width: 16px; 8 | height: 16px; 9 | } 10 | 11 | .sideBySideButtons *:not(:first-child) { 12 | margin-left: 15px; 13 | } 14 | 15 | .pythonExtInfo { 16 | max-width: 350px; 17 | margin: 8px auto; 18 | } 19 | -------------------------------------------------------------------------------- /webview/src/setup/components/experiments/NeedsGitCommit.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 3 | import { Button } from '../../../shared/components/button/Button' 4 | import { showScmPanel } from '../../util/messages' 5 | 6 | export const NeedsGitCommit: React.FC = () => ( 7 | 8 |
9 |

No Git commits detected

10 |

11 | At least one commit is required to enable DVC's experiments and 12 | plots functionality. 13 |

14 |
16 |
17 | ) 18 | -------------------------------------------------------------------------------- /webview/src/setup/components/experiments/PythonCodeBlock.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { CodeBlock } from '../shared/CodeBlock' 3 | 4 | export const PythonCodeBlock = ({ children }: { children: string }) => ( 5 | {children} 6 | ) 7 | -------------------------------------------------------------------------------- /webview/src/setup/components/experiments/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../shared/variables'; 2 | 3 | .dvcLiveExamples { 4 | margin-top: 20px; 5 | 6 | vscode-panels { 7 | width: max-content; 8 | margin: 0 auto; 9 | } 10 | } 11 | 12 | .otherFrameworks { 13 | white-space: pre-wrap; 14 | background-color: transparent !important; 15 | color: variables.$watermark-color; 16 | font-family: sans-serif; 17 | letter-spacing: 0.04em; 18 | line-height: 1.6; 19 | padding: 0; 20 | text-align: left; 21 | width: max-content; 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/setup/components/remotes/AzureBlobStorage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { ShowExtension } from './ShowExtension' 4 | 5 | export const AzureBlobStorage = () => ( 6 |
7 | 12 |

13 | See the{' '} 14 | 15 | docs 16 | {' '} 17 | for details on how to authenticate. 18 |

19 |
20 | ) 21 | -------------------------------------------------------------------------------- /webview/src/setup/components/remotes/DvcUninitialized.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SupportedStorage } from './SupportedStorage' 3 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 4 | import { FocusDvcSection } from '../shared/FocusDvcSection' 5 | 6 | export const DvcUninitialized: React.FC<{}> = () => ( 7 | 8 |

DVC is not initialized

9 | 10 | 11 |
12 | ) 13 | -------------------------------------------------------------------------------- /webview/src/setup/components/remotes/ProjectRemotes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | export const ProjectRemotes: React.FC<{ 5 | remotes: { [alias: string]: string } 6 | }> = ({ remotes }) => ( 7 | 8 | 9 | {Object.entries(remotes).map(([alias, remote]) => ( 10 | 11 | 12 | 13 | 14 | ))} 15 | 16 |
{alias}{remote}
17 | ) 18 | -------------------------------------------------------------------------------- /webview/src/setup/components/remotes/ShowExtension.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { Icon } from '../../../shared/components/Icon' 4 | import { Extensions } from '../../../shared/components/icons' 5 | import { ExtensionLink } from '../shared/ExtensionLink' 6 | 7 | export const ShowExtension: React.FC<{ 8 | capabilities: string 9 | id: string 10 | name: string 11 | className?: string 12 | }> = ({ capabilities, id, name, className }) => { 13 | return ( 14 |

15 | {' '} 21 | The {name} extension can 22 | be used to {capabilities}. 23 |

24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /webview/src/setup/components/shared/CliIncompatible.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import { FocusDvcSection } from './FocusDvcSection' 3 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 4 | 5 | export const CliIncompatible: React.FC = ({ children }) => ( 6 | 7 |

DVC is currently unavailable

8 | {children} 9 | 10 |
11 | ) 12 | -------------------------------------------------------------------------------- /webview/src/setup/components/shared/DetailsTable.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | 3 | import styles from './styles.module.scss' 4 | 5 | export const DetailsTable: React.FC> = ({ 6 | children, 7 | testId 8 | }) => { 9 | return ( 10 | 11 | {children} 12 |
13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /webview/src/setup/components/shared/DvcNotSetup.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import { FocusDvcSection } from './FocusDvcSection' 3 | import { EmptyState } from '../../../shared/components/emptyState/EmptyState' 4 | 5 | export const DvcNotSetup: React.FC = ({ children }) => ( 6 | 7 |

DVC is not setup

8 | {children} 9 | 10 |
11 | ) 12 | -------------------------------------------------------------------------------- /webview/src/setup/components/shared/ExtensionLink.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import type { HTMLAttributes } from 'react' 3 | 4 | interface ExtensionLinkProps extends HTMLAttributes { 5 | extensionId: string 6 | } 7 | 8 | export const ExtensionLink: React.FC> = ({ 9 | extensionId, 10 | children, 11 | ...props 12 | }) => { 13 | const idQuery = `"@id:${extensionId}"` 14 | return ( 15 | 21 | {children} 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /webview/src/setup/components/shared/FocusDvcSection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useDispatch } from 'react-redux' 3 | import { SetupSection } from 'dvc/src/setup/webview/contract' 4 | import { focusSection } from './util' 5 | import { Button } from '../../../shared/components/button/Button' 6 | 7 | export const FocusDvcSection = () => { 8 | const dispatch = useDispatch() 9 | return ( 10 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /webview/src/shared/components/button/RefreshButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ButtonProps } from './Button' 3 | import { IconButton } from './IconButton' 4 | import { Refresh } from '../icons' 5 | 6 | type RefreshButtonProps = Omit 7 | 8 | export const RefreshButton: React.FC = ({ 9 | appearance, 10 | onClick, 11 | isNested 12 | }: RefreshButtonProps) => { 13 | return ( 14 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/shared/components/button/StartButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ButtonProps } from './Button' 3 | import { IconButton } from './IconButton' 4 | import { Add } from '../icons' 5 | 6 | export const StartButton: React.FC = ({ 7 | appearance, 8 | onClick, 9 | isNested, 10 | text 11 | }: ButtonProps) => { 12 | return ( 13 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /webview/src/shared/components/button/styles.module.scss: -------------------------------------------------------------------------------- 1 | .secondaryButton { 2 | margin-left: 5px; 3 | } 4 | -------------------------------------------------------------------------------- /webview/src/shared/components/copyButton/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../variables'; 2 | 3 | .button { 4 | flex: 0 0 0.8em; 5 | font-size: 1.5em; 6 | display: none; 7 | border: none; 8 | background: none; 9 | color: variables.$fg-color; 10 | cursor: pointer; 11 | width: 0.8em; 12 | height: 0.8em; 13 | line-height: 0.8em; 14 | padding: 0; 15 | margin: 0 0.25em; 16 | } 17 | -------------------------------------------------------------------------------- /webview/src/shared/components/dragDrop/DragDropItemWithTarget.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | interface DragDropItemWithTargetProps { 5 | dropTarget: JSX.Element | null 6 | draggable: JSX.Element 7 | isAfter?: boolean 8 | shouldShowOnDrag?: boolean 9 | } 10 | 11 | export const DragDropItemWithTarget: React.FC< 12 | PropsWithChildren 13 | > = ({ dropTarget, isAfter, shouldShowOnDrag, draggable, children }) => { 14 | if (!dropTarget) { 15 | return <>{children} 16 | } 17 | 18 | const block = isAfter ? [children, dropTarget] : [dropTarget, children] 19 | 20 | return shouldShowOnDrag ? ( 21 | 22 | {block} 23 | 24 | ) : ( 25 | block 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /webview/src/shared/components/dragDrop/DropTarget.tsx: -------------------------------------------------------------------------------- 1 | import React, { HTMLAttributes } from 'react' 2 | 3 | interface DropTargetProps extends HTMLAttributes { 4 | children: JSX.Element 5 | id: string 6 | wrapper?: JSX.Element 7 | } 8 | 9 | export const DropTarget: React.FC = ({ 10 | children, 11 | id, 12 | wrapper, 13 | ...props 14 | }) => { 15 | const wrap = wrapper ||
16 | return ( 17 | 18 | {children} 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /webview/src/shared/components/dragDrop/GripIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Icon } from '../Icon' 3 | import { Gripper } from '../icons' 4 | 5 | interface GripIconProps { 6 | className?: string 7 | } 8 | 9 | export const GripIcon: React.FC = ({ className }) => ( 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /webview/src/shared/components/dragDrop/currentTarget.ts: -------------------------------------------------------------------------------- 1 | import { DragEvent } from 'react' 2 | 3 | export const getEventCurrentTargetDistances = (e: DragEvent) => { 4 | const itemClientRect = e.currentTarget.getBoundingClientRect() 5 | return { 6 | bottom: itemClientRect.bottom, 7 | left: itemClientRect.left, 8 | right: itemClientRect.right, 9 | top: itemClientRect.top 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /webview/src/shared/components/dragDrop/styles.module.scss: -------------------------------------------------------------------------------- 1 | .newBlockWhenShowingOnDrag { 2 | position: relative; 3 | } 4 | 5 | .dropTargetWhenShowingOnDrag { 6 | position: absolute; 7 | top: 0; 8 | height: 100%; 9 | z-index: 2; 10 | } 11 | 12 | .dropTargetWhenShowingOnDragLeft { 13 | left: -2px; 14 | } 15 | 16 | .dropTargetWhenShowingOnDragRight { 17 | right: -2px; 18 | } 19 | 20 | .ghost { 21 | display: none; 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/shared/components/emptyState/EmptyState.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import cx from 'classnames' 3 | import styles from './styles.module.scss' 4 | 5 | interface EmptyStateProps { 6 | isFullScreen?: boolean 7 | children: React.ReactNode 8 | } 9 | 10 | export const EmptyState: React.FC = ({ 11 | children, 12 | isFullScreen = true 13 | }) => { 14 | return ( 15 |
21 |
{children}
22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /webview/src/shared/components/emptyState/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../variables'; 2 | 3 | .emptyScreen { 4 | width: 100vw; 5 | height: 100vh; 6 | } 7 | 8 | .emptySection { 9 | width: 100%; 10 | min-height: 33vh; 11 | height: auto; 12 | } 13 | 14 | .emptyStateText { 15 | text-align: center; 16 | opacity: 0.8; 17 | letter-spacing: 0.04em; 18 | color: variables.$watermark-color; 19 | font-family: sans-serif; 20 | max-width: 600px; 21 | line-height: 1.6; 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/shared/components/errorIcon/ErrorIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { ErrorTooltip } from '../tooltip/ErrorTooltip' 4 | import { Error } from '../icons' 5 | 6 | export const ErrorIcon: React.FC<{ error: string; size: number }> = ({ 7 | error: msg, 8 | size 9 | }) => ( 10 | 11 |
12 | 18 |
19 |
20 | ) 21 | -------------------------------------------------------------------------------- /webview/src/shared/components/errorIcon/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../variables'; 2 | @use '../../styles'; 3 | 4 | .errorIcon { 5 | color: variables.$error-color; 6 | } 7 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Add.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Add = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default Add 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/ArrowDown.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const ArrowDown = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default ArrowDown 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/ArrowUp.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const ArrowUp = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default ArrowUp 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Beaker.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Beaker = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default Beaker 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Check.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Check = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Check 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/ChevronDown.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const ChevronDown = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default ChevronDown 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/ChevronRight.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const ChevronRight = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default ChevronRight 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Clock.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Clock = (props: SVGProps) => ( 4 | 12 | 18 | 19 | ) 20 | export default Clock 21 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Close.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Close = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Close 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Copy.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Copy = (props: SVGProps) => ( 4 | 12 | 17 | 22 | 23 | ) 24 | export default Copy 25 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Discard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Discard = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Discard 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Ellipsis.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Ellipsis = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default Ellipsis 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Extensions.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Extensions = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Extensions 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Filter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Filter = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Filter 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/GitCommit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const GitCommit = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default GitCommit 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/GraphLine.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const GraphLine = (props: SVGProps) => ( 4 | 12 | 13 | 14 | 15 | ) 16 | export default GraphLine 17 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/GraphScatter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const GraphScatter = (props: SVGProps) => ( 4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ) 20 | export default GraphScatter 21 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Gripper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Gripper = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default Gripper 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Info.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Info = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Info 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/ListFilter.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const ListFilter = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default ListFilter 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/PassFilled.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const PassFilled = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default PassFilled 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Refresh.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Refresh = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Refresh 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Remove.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Remove = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default Remove 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/SortPrecedence.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const SortPrecedence = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default SortPrecedence 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/StarEmpty.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const StarEmpty = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default StarEmpty 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/StarFull.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const StarFull = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default StarFull 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Table = (props: SVGProps) => ( 4 | 12 | 13 | 14 | ) 15 | export default Table 16 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Trash.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Trash = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Trash 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/icons/Warning.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import type { SVGProps } from 'react' 3 | const Warning = (props: SVGProps) => ( 4 | 12 | 17 | 18 | ) 19 | export default Warning 20 | -------------------------------------------------------------------------------- /webview/src/shared/components/messageBand/MessageBand.tsx: -------------------------------------------------------------------------------- 1 | import React, { PropsWithChildren } from 'react' 2 | import styles from './styles.module.scss' 3 | import { Icon, IconValue } from '../Icon' 4 | 5 | interface MessageBandProps { 6 | id: string 7 | icon?: IconValue 8 | } 9 | 10 | export const MessageBand: React.FC> = ({ 11 | children, 12 | icon, 13 | id 14 | }) => { 15 | return ( 16 |
17 | {icon && ( 18 | 24 | )} 25 |
{children}
26 |
27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /webview/src/shared/components/messageBand/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../variables'; 2 | 3 | .messageBand { 4 | width: 100%; 5 | background-color: variables.$bg-color; 6 | height: max-content; 7 | padding: 20px; 8 | display: flex; 9 | justify-content: space-between; 10 | align-items: center; 11 | line-height: 1.7; 12 | } 13 | 14 | .messageBandIcon { 15 | min-width: 50px; 16 | fill: variables.$accent-color; 17 | margin-right: 20px; 18 | } 19 | -------------------------------------------------------------------------------- /webview/src/shared/components/messagesMenu/MessagesMenu.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { 4 | MessagesMenuOption, 5 | MessagesMenuOptionProps 6 | } from './MessagesMenuOption' 7 | 8 | interface MessagesMenuProps { 9 | options: MessagesMenuOptionProps[] 10 | onOptionSelected?: () => void 11 | } 12 | 13 | export const MessagesMenu: React.FC = ({ 14 | options, 15 | onOptionSelected 16 | }) => ( 17 |
18 | {options.map(option => ( 19 | 24 | ))} 25 |
26 | ) 27 | -------------------------------------------------------------------------------- /webview/src/shared/components/messagesMenu/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../shared/variables'; 2 | 3 | .messagesMenu { 4 | padding: 4px 0; 5 | } 6 | 7 | .item { 8 | display: flex; 9 | cursor: default; 10 | height: 24px; 11 | padding: 2px 6px; 12 | 13 | &:hover { 14 | background-color: variables.$hover-background-color; 15 | } 16 | 17 | &:last-child { 18 | padding-bottom: 6px; 19 | } 20 | } 21 | 22 | .itemLabel { 23 | width: 165px; 24 | overflow: hidden; 25 | text-overflow: ellipsis; 26 | } 27 | 28 | .disabledItem { 29 | opacity: variables.$disabled-opacity; 30 | cursor: default; 31 | 32 | &:hover { 33 | background-color: variables.$bg-color; 34 | } 35 | } 36 | 37 | .itemIcon { 38 | width: 13px; 39 | } 40 | 41 | .dividerContainer { 42 | padding-right: 6px; 43 | padding-left: 6px; 44 | } 45 | -------------------------------------------------------------------------------- /webview/src/shared/components/modal/Modal.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, fireEvent } from '@testing-library/react' 3 | import { Modal } from './Modal' 4 | 5 | describe('Modal', () => { 6 | it('should call the onClose prop when pressing Escape', () => { 7 | const onClose = jest.fn() 8 | 9 | render() 10 | 11 | fireEvent.keyDown(window, { key: 'Escape' }) 12 | 13 | expect(onClose).toHaveBeenCalled() 14 | }) 15 | 16 | it('should not call the onClose prop when pressing other keys', () => { 17 | const onClose = jest.fn() 18 | 19 | render() 20 | 21 | fireEvent.keyDown(window, { key: 'Enter' }) 22 | fireEvent.keyDown(window, { key: 'e' }) 23 | fireEvent.keyDown(window, { key: 'Space' }) 24 | fireEvent.keyDown(window, { key: 'Alt' }) 25 | 26 | expect(onClose).not.toHaveBeenCalled() 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /webview/src/shared/components/modal/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../variables'; 2 | 3 | .backdrop { 4 | background-color: variables.$bg-transparency-5; 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | height: max(100%, 100vh); 9 | width: max(100%, 100vw); 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | z-index: 500; 14 | border: none; 15 | } 16 | 17 | .modal { 18 | background: variables.$bg-color; 19 | position: relative; 20 | } 21 | 22 | .modalContent { 23 | background: variables.$fg-transparency-1; 24 | padding: 2rem; 25 | } 26 | 27 | .closeButton { 28 | position: absolute; 29 | right: -12px; 30 | top: -40px; 31 | border: none; 32 | background: none; 33 | 34 | svg { 35 | fill: variables.$fg-color; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /webview/src/shared/components/selectMenu/SelectMenu.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SelectMenuOption, SelectMenuOptionProps } from './SelectMenuOption' 3 | 4 | interface SelectMenuProps { 5 | options: SelectMenuOptionProps[] 6 | onClick: (id: string) => void 7 | } 8 | 9 | export const SelectMenu: React.FC = ({ options, onClick }) => ( 10 |
11 | {options.map((option, i) => ( 12 | 18 | ))} 19 |
20 | ) 21 | -------------------------------------------------------------------------------- /webview/src/shared/components/selectMenu/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../../shared/variables'; 2 | 3 | .item { 4 | display: flex; 5 | cursor: pointer; 6 | height: 24px; 7 | padding: 2px 6px; 8 | 9 | &:hover { 10 | background-color: variables.$hover-background-color; 11 | } 12 | } 13 | 14 | .itemLabel { 15 | width: 165px; 16 | overflow: hidden; 17 | text-overflow: ellipsis; 18 | } 19 | 20 | .itemIcon { 21 | width: 13px; 22 | } 23 | -------------------------------------------------------------------------------- /webview/src/shared/components/slider/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../variables'; 2 | 3 | .wrapper { 4 | display: flex; 5 | align-items: center; 6 | } 7 | 8 | .label { 9 | font-size: 0.6rem; 10 | margin-right: 10px; 11 | color: variables.$fg-color; 12 | } 13 | 14 | .slider { 15 | accent-color: variables.$accent-color; 16 | outline: none !important; 17 | } 18 | -------------------------------------------------------------------------------- /webview/src/shared/components/tooltip/ErrorTooltip.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactElement } from 'react' 2 | import Tooltip from './Tooltip' 3 | import { ErrorTooltipContent } from './ErrorTooltipContent' 4 | 5 | export const ErrorTooltip: React.FC<{ 6 | error?: string 7 | children: ReactElement 8 | }> = ({ children, error }) => ( 9 | } 11 | placement="bottom" 12 | disabled={!error} 13 | > 14 | {children} 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /webview/src/shared/components/tooltip/ErrorTooltipContent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './styles.module.scss' 3 | import { Error } from '../icons' 4 | 5 | export const ErrorTooltipContent: React.FC<{ error?: string }> = ({ 6 | error 7 | }) => ( 8 |
9 | 10 | {error} 11 |
12 | ) 13 | -------------------------------------------------------------------------------- /webview/src/shared/components/virtualizedGrid/styles.module.scss: -------------------------------------------------------------------------------- 1 | .grid { 2 | width: 100%; 3 | min-height: 80vh; 4 | } 5 | -------------------------------------------------------------------------------- /webview/src/shared/components/webviewWrapper/WebviewWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react' 2 | import cx from 'classnames' 3 | import { useThemeVariables } from './useThemeVariables' 4 | import styles from './styles.module.scss' 5 | 6 | export const WebviewWrapper = ({ 7 | children, 8 | className 9 | }: { 10 | children: ReactNode 11 | className?: string 12 | }) => { 13 | const themeVariables = useThemeVariables() 14 | 15 | return ( 16 |
{ 21 | e.preventDefault() 22 | }} 23 | > 24 | {children} 25 |
26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /webview/src/shared/components/webviewWrapper/styles.module.scss: -------------------------------------------------------------------------------- 1 | .webviewWrapper { 2 | width: 100%; 3 | height: auto; 4 | min-height: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /webview/src/shared/mixins.scss: -------------------------------------------------------------------------------- 1 | @use 'variables'; 2 | 3 | %link { 4 | color: variables.$link-fg-color; 5 | 6 | &:hover, 7 | &:active { 8 | color: variables.$link-active-color; 9 | } 10 | } 11 | 12 | %buttonAsLink { 13 | @extend %link; 14 | 15 | background: none; 16 | border: none; 17 | padding: 0; 18 | cursor: pointer; 19 | } 20 | -------------------------------------------------------------------------------- /webview/src/shared/styles.scss: -------------------------------------------------------------------------------- 1 | @use 'variables'; 2 | @use 'mixins'; 3 | 4 | * { 5 | box-sizing: border-box; 6 | outline: none; 7 | } 8 | 9 | html, 10 | body, 11 | #root { 12 | margin: 0; 13 | padding: 0; 14 | width: 100%; 15 | height: 100%; 16 | } 17 | 18 | #root { 19 | display: block; 20 | } 21 | 22 | body { 23 | border: 0; 24 | margin: 0; 25 | padding: 0; 26 | background-color: variables.$bg-color; 27 | color: variables.$fg-color; 28 | font-family: variables.$editor-font; 29 | } 30 | 31 | .modalOpen { 32 | overflow: hidden; 33 | } 34 | 35 | .centered { 36 | display: flex; 37 | align-items: center; 38 | justify-content: center; 39 | margin: 0; 40 | flex-wrap: wrap; 41 | } 42 | 43 | button { 44 | font-family: inherit; 45 | } 46 | 47 | a { 48 | @extend %link; 49 | } 50 | -------------------------------------------------------------------------------- /webview/src/shared/vscode.ts: -------------------------------------------------------------------------------- 1 | import { vsCodeApi } from './api' 2 | 3 | // eslint-disable-next-line @typescript-eslint/unbound-method 4 | export const sendMessage = vsCodeApi.postMessage 5 | -------------------------------------------------------------------------------- /webview/src/stories/Slider.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { StoryFn, Meta } from '@storybook/react' 2 | import React from 'react' 3 | import { DISABLE_CHROMATIC_SNAPSHOTS } from './util' 4 | import { Slider } from '../shared/components/slider/Slider' 5 | 6 | export default { 7 | args: { 8 | defaultValue: 2, 9 | label: 'Slider with a max', 10 | maximum: 10 11 | }, 12 | component: Slider, 13 | parameters: DISABLE_CHROMATIC_SNAPSHOTS, 14 | title: 'Slider' 15 | } as Meta 16 | 17 | const Template: StoryFn<{ 18 | maximum: number 19 | defaultValue: number 20 | label: string 21 | }> = ({ maximum, defaultValue, label }) => ( 22 | {}} 27 | /> 28 | ) 29 | 30 | export const MinMaxSliderWithOnlyMax = Template.bind({}) 31 | -------------------------------------------------------------------------------- /webview/src/stories/components/IconWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | export const IconWrapper: React.FC<{ children: ReactNode; name: string }> = ({ 5 | children, 6 | name 7 | }) => ( 8 |
9 | {children} 10 |

{name}

11 |
12 | ) 13 | -------------------------------------------------------------------------------- /webview/src/stories/components/IconsWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react' 2 | import styles from './styles.module.scss' 3 | 4 | export const IconsWrapper: React.FC<{ children: ReactNode }> = ({ 5 | children 6 | }) =>
{children}
7 | -------------------------------------------------------------------------------- /webview/src/stories/components/styles.module.scss: -------------------------------------------------------------------------------- 1 | @use '../../shared/variables'; 2 | 3 | .iconsWrapper { 4 | display: flex; 5 | flex-wrap: wrap; 6 | gap: 10px; 7 | max-width: 100%; 8 | } 9 | 10 | .iconWrapper { 11 | padding: 5px; 12 | border: 1px solid variables.$fg-color; 13 | border-radius: 3px; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | width: 100px; 19 | height: 80px; 20 | 21 | svg { 22 | fill: variables.$fg-color; 23 | width: 20px; 24 | height: 20px; 25 | } 26 | } 27 | 28 | .iconTitle { 29 | font-size: 0.6rem; 30 | padding: 3px; 31 | } 32 | -------------------------------------------------------------------------------- /webview/src/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' 2 | -------------------------------------------------------------------------------- /webview/src/test/selection.ts: -------------------------------------------------------------------------------- 1 | export const createWindowTextSelection = ( 2 | nodeValue: string | null, 3 | offset = 0 4 | ) => { 5 | window.getSelection = () => { 6 | return { 7 | anchorOffset: 1, 8 | focusNode: nodeValue ? ({ nodeValue } as Node) : null, 9 | focusOffset: 1 + offset 10 | } as Selection 11 | } 12 | } 13 | 14 | export const clearSelection = () => createWindowTextSelection(null) 15 | -------------------------------------------------------------------------------- /webview/src/util/array.ts: -------------------------------------------------------------------------------- 1 | import isEqual from 'lodash.isequal' 2 | import { BaseType } from './objects' 3 | import { DraggedInfo } from '../shared/components/dragDrop/dragDropSlice' 4 | 5 | export const pushIf = (array: T[], condition: boolean, elements: T[]) => 6 | condition && array.push(...elements) 7 | 8 | export const keepEqualOldReferencesInArray = ( 9 | oldArray: T[], 10 | newArray: T[] 11 | ): T[] => 12 | newArray.map(item => oldArray.find(oldItem => isEqual(oldItem, item)) || item) 13 | 14 | export const changeOrderWithDraggedInfo = ( 15 | order: string[], 16 | dragged: DraggedInfo 17 | ) => { 18 | if (!dragged) { 19 | return order 20 | } 21 | const newOrder = [...order] 22 | const draggedIndex = Number.parseInt(dragged.itemIndex, 10) 23 | 24 | newOrder.splice(draggedIndex, 1) 25 | newOrder.push(dragged.itemId) 26 | return newOrder 27 | } 28 | -------------------------------------------------------------------------------- /webview/src/util/helpers.ts: -------------------------------------------------------------------------------- 1 | export const cond: ( 2 | condition: boolean, 3 | ifTrue: () => T, 4 | ifFalse: () => F 5 | ) => T | F = (condition, ifTrue, ifFalse) => (condition ? ifTrue() : ifFalse()) 6 | 7 | export const idToNode = (id: string) => 8 | // eslint-disable-next-line unicorn/prefer-query-selector 9 | (id && document.getElementById(id)) || null 10 | 11 | export const isTooltip = (el: Element, parentElements: string[]) => { 12 | let currentNode = el 13 | while (!parentElements.includes(currentNode.nodeName)) { 14 | if ( 15 | !!currentNode.className.toLocaleLowerCase && 16 | currentNode.className.toLocaleLowerCase().includes('tooltip') 17 | ) { 18 | return true 19 | } 20 | currentNode = currentNode.parentElement || currentNode 21 | } 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /webview/src/util/ids.test.ts: -------------------------------------------------------------------------------- 1 | import { createIDWithPrefixAndIndex } from './ids' 2 | 3 | describe('ids', () => { 4 | describe('createIDWithPrefixAndIndex', () => { 5 | it('should create an id with a prefix and an index correctly', () => { 6 | expect(createIDWithPrefixAndIndex('my-id', 42, 'this-isAPREFIX')).toBe( 7 | 'this-isAPREFIXmy-id_42' 8 | ) 9 | }) 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /webview/src/util/ids.ts: -------------------------------------------------------------------------------- 1 | const ID_SEPARATOR = '_' 2 | 3 | export const createIDWithIndex = (id: string, index: number) => 4 | `${id}${ID_SEPARATOR}${index}` 5 | 6 | export const createIDWithPrefixAndIndex = ( 7 | id: string, 8 | index: number, 9 | prefix: string 10 | ) => `${prefix}${id}${ID_SEPARATOR}${index}` 11 | 12 | export const getIDWithoutIndex = (id?: string) => id?.split(ID_SEPARATOR)[0] 13 | 14 | export const getIDIndex = (id: string) => 15 | Number.parseInt(id.split(ID_SEPARATOR).slice(-1)[0], 10) 16 | -------------------------------------------------------------------------------- /webview/src/util/objects.ts: -------------------------------------------------------------------------------- 1 | import isEqual from 'lodash.isequal' 2 | 3 | export type BaseType = 4 | | string 5 | | number 6 | | boolean 7 | | object 8 | | Obj 9 | | undefined 10 | | null 11 | 12 | type Any = BaseType | BaseType[] 13 | 14 | type Obj = { [key: string]: Any } 15 | 16 | export const keepReferenceIfEqual = ( 17 | old: T, 18 | recent: T 19 | ): T => (isEqual(old, recent) ? old : recent) 20 | -------------------------------------------------------------------------------- /webview/src/util/strings.ts: -------------------------------------------------------------------------------- 1 | export const isSelecting = (text: string[]) => { 2 | const selection = window.getSelection() 3 | 4 | return ( 5 | text.includes(selection?.focusNode?.nodeValue || '') && 6 | selection?.anchorOffset !== selection?.focusOffset 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /webview/src/util/wdyr.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 3 | import React from 'react' 4 | 5 | if (process.env.NODE_ENV === 'development') { 6 | const whyDidYouRender = require('@welldone-software/why-did-you-render') 7 | whyDidYouRender(React, { 8 | trackAllPureComponents: false 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /webview/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "target": "es6", 5 | "module": "commonjs", 6 | "strict": true, 7 | "outDir": "dist", 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true, 10 | "newLine": "LF", 11 | "sourceMap": true, 12 | "jsx": "react", 13 | "experimentalDecorators": true 14 | }, 15 | "include": ["src/**/*", "webpack.config.ts", "__mocks__"] 16 | } 17 | --------------------------------------------------------------------------------