Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🪐 Support for JupyterLab 4.x #142

Closed
wants to merge 43 commits into from
Closed

Conversation

agoose77
Copy link
Collaborator

@agoose77 agoose77 commented May 16, 2023

This PR will probably take a while. The new windowed notebook breaks things that will require some care to recover.

Simultaneously, I think we should think about how #102 fits into this PR at the same time.

Once this gets merged, it might be more straightforward for us to only support 4.x. I haven't had much experience in maintaining compatibility across multiple versions. The obvious way is to make a branch from HEAD~1 after merging this PR, and bump the version in main to 2.0.0. Then we can continue to make backports if needs be.

@github-actions
Copy link
Contributor

Binder 👈 Launch a Binder on branch executablebooks/jupyterlab-myst/agoose77/feat-jupyterlab-v4

@agoose77 agoose77 force-pushed the agoose77/feat-jupyterlab-v4 branch from 696e3fd to 402eba1 Compare May 30, 2023 11:50
@rowanc1 rowanc1 mentioned this pull request May 31, 2023
@fperez
Copy link
Contributor

fperez commented Jun 1, 2023

@agoose77 - I'm very happy to see this progress, feel free to ping me if at some point you want some testing/feedback I can help with. Happy to do it, as having this entire toolchain happily spinning on JL4 is important to me. And huge thanks for the amazing work, once more :)

@agoose77
Copy link
Collaborator Author

agoose77 commented Jun 1, 2023

@rowanc1 @fperez could you take this for a spin? I was seeing some local performance regressions in Firefox, but I can't reproduce them today.

I'm aware of a double-render issue with executing inline expression cells; I plan to fix that in the coming week.

@rowanc1
Copy link
Member

rowanc1 commented Jun 1, 2023

Ok, I have taken for a spin -- a few things to note is that the new jlpm requires it's own version of yarn, and so we should do jlpm install in various places, probably also in the CI if it isn't already.

Things I noticed:

  • Widgets are text-only, this is likely to do with trust Pass trust to the mimerenderer #150
  • New features for exercise, unknown renderers, cite:p, etc are not included yet
  • the matplotlib sparkline example crashes the cell renderer. Not sure why.
  • local images are not resolved (probably also need to know if you are in a cell as well for attachments?) Was fixed in 🌉 Update images to work locally #145

Things that worked:

  • inline evaluations (for text)
  • task lists

I have also noticed the double render, if you have a handle on that it would be awesome to fix! :)

@fperez
Copy link
Contributor

fperez commented Jun 1, 2023

Thanks @rowanc1! I was actually trying to set up a dev environment to test it, but I got mired in problems with conda/mamba... Argh... In any case, it seems like your round of input may give enough for now - I'll finish cleaning up my local setup so I can help with a round of testing after more updates.

If the yarn/jlpm fixes go in that would be great - I know so little about js/ts development that I'm likely to stumble hard on that if it doesn't work out of the box as per the README (which is what I was planning on blindly following).

Copy link
Member

@rowanc1 rowanc1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few of the cells in the example notebook have been removed. Is this intentional?

One of the matplotlib examples does actually fail now, this could be related to the double render, so I think we should fix that first!

I updated trust in #150.

README.md Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
model?: ITextModel;
};

const TextModelContext = createContext<TextModelState | undefined>(undefined);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that task lists work in the markdown preview as well?

Copy link
Collaborator Author

@agoose77 agoose77 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes & no. RenderMime isn't supposed to write back to the model IIRC, but we could probably do it if the model we're given is directly associated with the file content, i.e. not a copy. If this were the case, we would need to do a bit more here, though.

In the current version, fragmentContext is set for notebooks only. Perhaps there's a more general refactor to be done, though.

@parmentelat
Copy link

I am also in the process of putting together a jlab4-based env for next year courses starting in September

in order to try out this wip I was expecting to be able to do

 pip install git+https://github.com/executablebooks/jupyterlab-myst.git@agoose77/feat-jupyterlab-v4

which is a recipe that I've used successfully with the jupytext wip at least
it is a little slow but that's fine as long as it keeps the rather gory ts/jlpm/yarn details out of my way ;)

however in the case of jupyterlab_myst this fails with a long-ish error log, where the important part seems to be (full log in the details section below)

          message: 'Module build failed (from ./node_modules/css-loader/dist/cjs.js):\n' +
            "Error: Can't resolve 'app.css' in '/tmp/pip-req-build-hb92s9ma/style'\n" +

is this easily fixable ?
it would be nice because it could help mere mortals like @fperez and myself more easily deploy and try it out

in any case, thanks for taking care of the upgrade 👍

the end of the error log

<snip>
      ➤ YN0013: │ yargs-parser@npm:21.1.1 can't be found in the cache and will be fetched from the remote registry
      ➤ YN0013: │ yargs@npm:17.7.2 can't be found in the cache and will be fetched from the remote registry
      ➤ YN0013: │ yjs@npm:13.6.1 can't be found in the cache and will be fetched from the remote registry
      ➤ YN0013: │ yocto-queue@npm:0.1.0 can't be found in the cache and will be fetched from the remote registry
      ➤ YN0013: │ zwitch@npm:2.0.4 can't be found in the cache and will be fetched from the remote registry
      ➤ YN0000: └ Completed in 27s 544ms
      ➤ YN0000: ┌ Link step
      ➤ YN0007: │ @fortawesome/fontawesome-free@npm:5.15.4 must be built because it never has been before or the last one failed
      ➤ YN0000: └ Completed in 10s 724ms
      ➤ YN0000: Done with warnings in 38s 857ms
      INFO:hatch_jupyter_builder.utils:> /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/bin/jlpm run build:prod
      Building extension in .

      Compilation starting…


      Compilation finished

      [
        {
          moduleIdentifier: '/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/css-loader/dist/cjs.js!/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/style/base.css',
          moduleName: './node_modules/css-loader/dist/cjs.js!./style/base.css',
          message: 'Module build failed (from ./node_modules/css-loader/dist/cjs.js):\n' +
            "Error: Can't resolve 'app.css' in '/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/style'\n" +
            '    at finishWithoutResolve (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:321:18)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:398:15\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:447:5\n' +
            '    at eval (eval at create (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/tapable/lib/HookCodeFactory.js:33:10), :15:1)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:447:5\n' +
            '    at eval (eval at create (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/tapable/lib/HookCodeFactory.js:33:10), :27:1)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:87:43\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:447:5\n' +
            '    at eval (eval at create (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/tapable/lib/HookCodeFactory.js:33:10), :16:1)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/forEachBail.js:18:13',
          moduleId: 41150,
          moduleTrace: [ [Object], [Object], [Object] ],
          details: undefined,
          stack: 'ModuleBuildError: Module build failed (from ./node_modules/css-loader/dist/cjs.js):\n' +
            "Error: Can't resolve 'app.css' in '/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/style'\n" +
            '    at finishWithoutResolve (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:321:18)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:398:15\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:447:5\n' +
            '    at eval (eval at create (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/tapable/lib/HookCodeFactory.js:33:10), :15:1)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:447:5\n' +
            '    at eval (eval at create (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/tapable/lib/HookCodeFactory.js:33:10), :27:1)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:87:43\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/Resolver.js:447:5\n' +
            '    at eval (eval at create (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/tapable/lib/HookCodeFactory.js:33:10), :16:1)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/enhanced-resolve/lib/forEachBail.js:18:13\n' +
            '    at processResult (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/webpack/lib/NormalModule.js:764:19)\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/webpack/lib/NormalModule.js:866:5\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/loader-runner/lib/LoaderRunner.js:400:11\n' +
            '    at /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/loader-runner/lib/LoaderRunner.js:252:18\n' +
            '    at context.callback (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/loader-runner/lib/LoaderRunner.js:124:13)\n' +
            '    at Object.loader (/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/css-loader/dist/index.js:123:5)\n' +
            '    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)',
          compilerPath: undefined
        }
      ]
      /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/lib/python3.11/site-packages/jupyterlab/debuglog.py:56: UserWarning: An error occurred.
        warnings.warn("An error occurred.")
      /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/lib/python3.11/site-packages/jupyterlab/debuglog.py:57: UserWarning: subprocess.CalledProcessError: Command '['node', '/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji/node_modules/@jupyterlab/builder/lib/build-labextension.js', '--core-path', '/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/lib/python3.11/site-packages/jupyterlab/staging', '/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-req-build-bnue2gji']' returned non-zero exit status 2.
        warnings.warn(msg[-1].strip())
      /private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/lib/python3.11/site-packages/jupyterlab/debuglog.py:58: UserWarning: See the log file for details: /var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/jupyterlab-debug-5hec7dl0.log
        warnings.warn(f"See the log file for details: {log_path!s}")
      Traceback (most recent call last):
        File "/Users/tparment/miniconda3/envs/ue12-p23-intro/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 353, in 
          main()
        File "/Users/tparment/miniconda3/envs/ue12-p23-intro/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 335, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/tparment/miniconda3/envs/ue12-p23-intro/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 152, in prepare_metadata_for_build_wheel
          whl_basename = backend.build_wheel(metadata_directory, config_settings)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/lib/python3.11/site-packages/hatchling/build.py", line 56, in build_wheel
          return os.path.basename(next(builder.build(wheel_directory, ['standard'])))
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/lib/python3.11/site-packages/hatchling/builders/plugin/interface.py", line 150, in build
          build_hook.initialize(version, build_data)
        File "/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/normal/lib/python3.11/site-packages/hatch_jupyter_builder/plugin.py", line 87, in initialize
          raise e
        File "/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/normal/lib/python3.11/site-packages/hatch_jupyter_builder/plugin.py", line 82, in initialize
          build_func(self.target_name, version, **build_kwargs)
        File "/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/normal/lib/python3.11/site-packages/hatch_jupyter_builder/utils.py", line 115, in npm_builder
          run([*npm_cmd, "run", build_cmd], cwd=str(abs_path))
        File "/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/normal/lib/python3.11/site-packages/hatch_jupyter_builder/utils.py", line 229, in run
          return subprocess.check_call(cmd, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/tparment/miniconda3/envs/ue12-p23-intro/lib/python3.11/subprocess.py", line 413, in check_call
          raise CalledProcessError(retcode, cmd)
      subprocess.CalledProcessError: Command '['/private/var/folders/9n/sxs31qhj1gnd6gk2v0ns8848000fn2/T/pip-build-env-tts1f8tl/overlay/bin/jlpm', 'run', 'build:prod']' returned non-zero exit status 1.
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.

agoose77 and others added 18 commits June 11, 2023 15:27
* chore: simplify dependencies

* fix: update source-map-loader

Addresses error with nth-check source-map parsing: webpack-contrib/source-map-loader#186

* chore: upgrade to PyPI version
This is needed for a different PR! oops
…140)

* 🎨 Update for showing proofs

* Update Playwright Snapshots

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
SHA256 hashes:

jupyterlab-myst-1.2.0.tgz: e730003c864ec43b85b1d2f0f8fe2cc9ad318a9d9caaa48348b055d2ee85f9d0

jupyterlab_myst-1.2.0-py3-none-any.whl: 29b8055003e713058f9702fcf8d28fbbf74002c9e66cf4625d8746d7ee8f22ae

jupyterlab_myst-1.2.0.tar.gz: fbc686a79543cdd439d256c71d26410d281bcdff6620ff92b7fea247a71b6f12
src/myst.ts Show resolved Hide resolved
@agoose77 agoose77 force-pushed the agoose77/feat-jupyterlab-v4 branch from af05b3f to f0c30a6 Compare June 11, 2023 14:49
@rowanc1 rowanc1 changed the title wip: support JupyterLab 4.x 🪐 Support for JupyterLab 4.x Jun 11, 2023
@rowanc1 rowanc1 marked this pull request as ready for review June 11, 2023 21:14
@rowanc1
Copy link
Member

rowanc1 commented Jun 11, 2023

Turns out that the screenshot action is based off of main, and that is why there was a failure and the updates to the YAML were not working. Pretty unintuitive, but should be fine for anything except a jupyterlab release.

Copy link
Member

@rowanc1 rowanc1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final look through the code, I think all this is great and super happy to get this in and have a major release to support jlab 4. I think some of the things that happen right after that are the double render improvements and then try to get to some of the other major features that we need!

Nice work @agoose77 🚀

@@ -1,2841 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we want to bring this yarn-lock back, or we could add it to the gitignore, it is probably fine for it to be generated every time.

@parmentelat
Copy link

@rowanc1

I do confirm the pip command now can carry out proper install

So I could easily try out commit b405903, and happily report that it works as expected, as far as I can tell for my relatively modest needs - primarily admonitions

thank you !

src/inlineExpression.tsx Outdated Show resolved Hide resolved
Copy link
Member

@rowanc1 rowanc1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@agoose77 -- tested this out, and some very strange behaviour on a subsequent rerender of a text-cell. Changing a first cell renders with the contents of the last cell.

@rowanc1
Copy link
Member

rowanc1 commented Jun 12, 2023

Ok, fixed the wrong cell model being shown -- in my JupyterLab (or notebook?) the cell.id is an empty string. The object comparison works well here.

Right now there are a lot of errors being thrown, I think that these should be at most debug messages as we are actually expecting them to fail. Having this be the default state of the console is a bit rough - also not sure if we should leave the console logs in, but if we do, we should make sure that they are debug calls.

image

@@ -83,7 +83,7 @@ export class RenderedExpression extends Widget {

// Create renderer
const renderer = this.rendermime.createRenderer(mimeType);
layout.widget = renderer;
if (layout) layout.widget = renderer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why, but this was consistently null.

@agoose77
Copy link
Collaborator Author

Hmm, I've not been seeing that. In any case, I'm not happy about the PR as it stands - after our meeting I took another look and I have a hunch that we can revert back to something more like our original code (main). I know that we discussed releasing as is, but I've made good progress, I'll see tomorrow if it works and then we can feel more confident moving forward. I'll check out this bug as a part of that.

Copy link
Member

@rowanc1 rowanc1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, extension is working again with my minor changes! Without the double render is MUCH nicer from a UX perspective. Would prefer not to have the console errors when we release this, but isn't a big deal and can always be fixed later.

@@ -84,7 +83,7 @@ export class RenderedExpression extends Widget {

// Create renderer
const renderer = this.rendermime.createRenderer(mimeType);
layout.widget = renderer;
if (layout) layout.widget = renderer;
console.assert(renderer.isAttached, 'renderer was not attached!', renderer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of these calls consistently fail, and aren't actually a problem. I think that we should not show console errors unless there is a problem. We could also put them behind a dev flag if we want.

@rowanc1
Copy link
Member

rowanc1 commented Jun 12, 2023

Not sure, maybe this will just go away .. says it is a github internal error:
image

src/index.ts Show resolved Hide resolved
@fperez
Copy link
Contributor

fperez commented Jun 13, 2023

Thanks for the pip line @parmentelat! Indeed, for someone like me with such limited JS/TS expertise, being able to test like this is pretty important :)

Here just to report that things are looking pretty good so far on a 4.0.2 installation, thanks so much @agoose77 @rowanc1!!

I am not sure if the math double rendering is entirely gone or just less noticeable due to performance improvements, but in any case the experience with inline math is much, much smoother and more reliable than very recently. It was very hit or miss in my prior tests (I always had to demo it with a caveat to the audience :) and now it works pretty well (in light testing though).

I noticed that inline markdown links are not properly formatted, though they do work as URLs. See e.g. the example for the youtube link in the included example:

image

That was already probably on your radar, but just in case.

As usual thanks so much for the great work, LMK if I can be of any help with further testing/input. Very much looking forward to this being fully released!

@rowanc1
Copy link
Member

rowanc1 commented Jun 22, 2023

Taken over by #155

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants