flame icon indicating copy to clipboard operation
flame copied to clipboard

docs: Community Snippets

Open munsterlander opened this issue 3 years ago • 19 comments

Description

Based on our conversation in discord, I have created the initial concept of recipes for the docs. What they end up looking like could be completely different than this. I know @spydon was thinking of embedding functioning games in this and maybe we can, but looking at Klondike, it seems that we would be committing a lot of files when people really just want the code snippet. So maybe this isn't a recipe per se, but more of a snippet library. None the less, as this is OSS, I felt it important to give credit back to the folks I took the initial tidbits from, so please feel free to adjust as necessary.

Checklist

  • [x] The title of my PR starts with a Conventional Commit prefix (fix:, feat:, docs: etc).
  • [x] I have read the Contributor Guide and followed the process outlined for submitting PRs.
  • [ ] I have updated/added tests for ALL new/updated/fixed functionality.
  • [x] I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • [ ] I have updated/added relevant examples in examples.

Breaking Change

  • [ ] Yes, this is a breaking change.
  • [x] No, this is not a breaking change.

Related Issues

munsterlander avatar Aug 27 '22 19:08 munsterlander

Good job! What I meant with embedded running code was more like how the router docs are, not full games like Klondike.

So I was looking at this but I didn't see it in router - I was reviewing the code in Decorators. Router is what I wanted to do as well. Let me see if I can figure out how these can all be one project - like Decorators and that they would be easy to add. What I didn't know, was for the WASD keyboard example, would the event passthrough or be captured by the docs, etc.

Edit: Additionally, I didn't know (not that I compare router vs decorators) if you would want to just keep adding on and on to that example, i.e. if there are lots of recipes, does it make that one main.dart file too big to manage with all the registrations. If not, then I can totally add this (I think).

Edit2: How do you go about testing that integration locally? I figured that the .md pages had to be rendered through Myst first, but is it just a regular flutter project that you run main or such??

munsterlander avatar Aug 28 '22 00:08 munsterlander

@munsterlander check here: https://docs.flame-engine.org/main/development/documentation.html#building-documentation-locally

spydon avatar Aug 28 '22 15:08 spydon

@munsterlander check here: https://docs.flame-engine.org/main/development/documentation.html#building-documentation-locally

Well how did I not see that, lol! I will get them built today and see if I can integrate the embedded apps.

munsterlander avatar Aug 28 '22 17:08 munsterlander

I don't know if its my environment or what (I have both Python 2&3 loaded due to work stuff), but I can't get it to make:

Running Sphinx v5.1.1
myst v0.16.1: MdParserConfig(renderer='sphinx', commonmark_only=False, enable_extensions=['dollarmath', 'html_admonition', 'html_image', 'linkify', 'replacements', 'smartquotes'], linkify_fuzzy_links=True, dmath_allow_labels=True, dmath_allow_space=True, dmath_allow_digits=True, dmath_double_inline=False, update_mathjax=True, mathjax_classes='tex2jax_process|mathjax_process|math|output_area', disable_syntax=[], url_schemes=['http', 'https', 'mailto', 'ftp'], heading_anchors=4, heading_slug_func=None, html_meta=[], footnote_transition=True, substitutions=[], sub_delimiters=['{', '}'], words_per_minute=200)
building [mo]: targets for 0 po files that are out of date
building [html]: targets for 55 source files that are out of date
updating environment: [new config] 55 added, 0 changed, 0 removed
Compiling Flutter app [flame-examples]documentation

Exception occurred:
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\subprocess.py", line 997, in _execute_child
    startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified
The full traceback has been saved in C:\Users\munsterlander\AppData\Local\Temp\sphinx-err-3nye4z0r.log, if you want to report the issue to the developers.
Please also report this if it was a user error, so that a better error message can be provided next time.
A bug report can be filed in the tracker at <https://github.com/sphinx-doc/sphinx/issues>. Thanks!

Sphinx unfortunately has 990 open issues and searching is not exactly turning anything up. Have any of you experienced this before or have an idea of what be the cause? The debug log is basically devoid of useful information imo:

# Sphinx version: 5.1.1
# Python version: 3.6.5 (CPython)
# Docutils version: 0.17.1 release
# Jinja2 version: 3.0.3
# Last messages:
#   55 added, 0 changed, 0 removed
#   
#   reading sources... [  1%] README
#   
#   reading sources... [  3%] development/development
#   
#   reading sources... [  5%] development/documentation
#   
#   Compiling Flutter app [flame-examples]
#   
# Loaded extensions:
#   sphinx.ext.mathjax (5.1.1) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\ext\mathjax.py
#   sphinxcontrib.applehelp (1.0.2) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinxcontrib\applehelp\__init__.py
#   sphinxcontrib.devhelp (1.0.2) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinxcontrib\devhelp\__init__.py
#   sphinxcontrib.htmlhelp (2.0.0) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinxcontrib\htmlhelp\__init__.py
#   sphinxcontrib.serializinghtml (1.1.5) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinxcontrib\serializinghtml\__init__.py
#   sphinxcontrib.qthelp (1.0.3) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinxcontrib\qthelp\__init__.py
#   alabaster (0.7.12) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\alabaster\__init__.py
#   myst_parser (0.16.1) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\__init__.py
#   sphinxcontrib.mermaid (5.1.1) from c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinxcontrib\mermaid.py
#   extensions.flutter_app (unknown version) from C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_sphinx\extensions\flutter_app.py
Traceback (most recent call last):
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\cmd\build.py", line 277, in build_main
    app.build(args.force_all, filenames)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\application.py", line 349, in build
    self.builder.build_update()
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\builders\__init__.py", line 303, in build_update
    len(to_build))
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\builders\__init__.py", line 317, in build
    updated_docnames = set(self.read())
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\builders\__init__.py", line 424, in read
    self._read_serial(docnames)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\builders\__init__.py", line 445, in _read_serial
    self.read_doc(docname)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\builders\__init__.py", line 498, in read_doc
    publisher.publish()
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\docutils\core.py", line 218, in publish
    self.settings)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\sphinx\io.py", line 104, in read
    self.parse()
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\docutils\readers\__init__.py", line 78, in parse
    self.parser.parse(self.input, document)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\sphinx_parser.py", line 60, in parse
    parser.renderer.render(tokens, parser.options, env)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\docutils_renderer.py", line 188, in render
    self._render_tokens(list(tokens))
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\docutils_renderer.py", line 167, in _render_tokens
    self.rules[f"render_{child.type}"](child)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\docutils_renderer.py", line 463, in render_fence
    return self.render_directive(token)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\docutils_renderer.py", line 991, in render_directive
    nodes_list = self.run_directive(name, arguments, content, position)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\site-packages\myst_parser\docutils_renderer.py", line 1075, in run_directive
    result = directive_instance.run()
  File "C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_sphinx\extensions\flutter_app.py", line 93, in run
    self._ensure_compiled()
  File "C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_sphinx\extensions\flutter_app.py", line 171, in _ensure_compiled
    self._compile_source()
  File "C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_sphinx\extensions\flutter_app.py", line 186, in _compile_source
    check=True,
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\subprocess.py", line 403, in run
    with Popen(*popenargs, **kwargs) as process:
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\subprocess.py", line 709, in __init__
    restore_signals, start_new_session)
  File "c:\users\munsterlander\appdata\local\programs\python\python36-32\lib\subprocess.py", line 997, in _execute_child
    startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified

munsterlander avatar Aug 28 '22 21:08 munsterlander

Ah, I guess we haven't tried building on a windows machine before :)

Would the command flutter build web work for you? Or does it have to be flutter.exe build web? Sorry, I never worked with Flutter on windows.

If it's the latter, then open the file doc/_sphinx/extensions/flutter_app.py, and in line 182 change 'flutter' into 'flutter.exe'. If after this the error disappears, then we'll figure out how to modify it to work properly on both windows and linux.

st-pasha avatar Aug 28 '22 21:08 st-pasha

flutter build web works fine. The following gets generated in a folder that doesn't have a pubspec of course:

flutter build web
Error: No pubspec.yaml file found.
This command should be run from the root of your Flutter project.

For giggles, I went ahead and changed it to flutter.exe but I get the exact same error and the same log output.

Edit: I went into Examples folder and decided to run flutter build web to see what happens, this is what I get:

PS C:\Users\munsterlander\AndroidStudioProjects\flame\examples> flutter build web
Running "flutter pub get" in examples...                            3.7s

 Building with sound null safety 

Target dart2js failed: Exception: Warning: The 'dart2js' entrypoint script is deprecated, please use 'dart compile js' instead.
lib/stories/bridge_libraries/forge2d/utils/balls.dart:66:8:
Error: The method 'Ball.beginContact' has more required arguments than those of overridden method 'BodyComponent with ContactCallbacks.beginContact'.
  void beginContact(Object other, Contact contact) {
       ^
lib/stories/bridge_libraries/forge2d/utils/balls.dart:6:7:
Info: This is the overridden method ('beginContact').
class Ball extends BodyComponent with ContactCallbacks {
      ^
lib/stories/bridge_libraries/forge2d/utils/balls.dart:85:7:
Error: Applying the mixin 'ContactCallbacks' to 'Ball' introduces an erroneous override of 'beginContact'.
class WhiteBall extends Ball with ContactCallbacks {
      ^^^^^^^^^
/C:/flutter/flutter/.pub-cache/hosted/pub.dartlang.org/flame_forge2d-0.11.0/lib/contact_callbacks.dart:65:8:
Info: The method 'ContactCallbacks.beginContact' has fewer positional arguments than those of overridden method 'Ball.beginContact'.
  void beginContact(Contact contact) =>
       ^
lib/stories/bridge_libraries/forge2d/utils/balls.dart:66:8:
Info: This is the overridden method ('beginContact').
  void beginContact(Object other, Contact contact) {
       ^
lib/stories/bridge_libraries/forge2d/utils/balls.dart:92:8:
Error: The method 'WhiteBall.beginContact' has more required arguments than those of overridden method 'Ball with ContactCallbacks.beginContact'.
  void beginContact(Object other, Contact contact) {
       ^
lib/stories/bridge_libraries/forge2d/utils/balls.dart:85:7:
Info: This is the overridden method ('beginContact').
class WhiteBall extends Ball with ContactCallbacks {
      ^
games/padracing/lib/ball.dart:67:8:
Error: The method 'Ball.beginContact' has more required arguments than those of overridden method 'BodyComponent with ContactCallbacks.beginContact'.
  void beginContact(Object other, Contact contact) {
       ^
games/padracing/lib/ball.dart:12:7:
Info: This is the overridden method ('beginContact').
class Ball extends BodyComponent<PadRacingGame> with ContactCallbacks {
      ^
games/padracing/lib/lap_line.dart:89:8:
Error: The method 'LapLine.beginContact' has more required arguments than those of overridden method 'BodyComponent with ContactCallbacks.beginContact'.
  void beginContact(Object other, Contact contact) {
       ^
games/padracing/lib/lap_line.dart:12:7:
Info: This is the overridden method ('beginContact').
class LapLine extends BodyComponent with ContactCallbacks {
      ^
Error: Compilation failed.


Compiling lib\main.dart for the Web...                             36.4s
Exception: Failed to compile application for the Web.

munsterlander avatar Aug 28 '22 22:08 munsterlander

So, what happens if you:

  1. Go to the directory with doc examples: cd doc/flame/examples
  2. Run python: python
  3. In python console run command import subprocess; subprocess.run(['flutter', 'build', 'web']) ?

st-pasha avatar Aug 28 '22 23:08 st-pasha

3. import subprocess; subprocess.run(['flutter', 'build', 'web'])

So I ran it as one line and then broke it down into individual commands to see if something else was afoot. Same error. flutter web build in the same directory runs, just throws the errors from above. I am working on a current branch as well.

Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess; subprocess.run(['flutter', 'build', 'web'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\munsterlander\AppData\Local\Programs\Python\Python36-32\lib\subprocess.py", line 403, in run
    with Popen(*popenargs, **kwargs) as process:
  File "C:\Users\munsterlander\AppData\Local\Programs\Python\Python36-32\lib\subprocess.py", line 709, in __init__
    restore_signals, start_new_session)
  File "C:\Users\munsterlander\AppData\Local\Programs\Python\Python36-32\lib\subprocess.py", line 997, in _execute_child
    startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified
>>> import subprocess;
>>> subprocess.run(['flutter', 'build', 'web']);
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\munsterlander\AppData\Local\Programs\Python\Python36-32\lib\subprocess.py", line 403, in run
    with Popen(*popenargs, **kwargs) as process:
  File "C:\Users\munsterlander\AppData\Local\Programs\Python\Python36-32\lib\subprocess.py", line 709, in __init__
    restore_signals, start_new_session)
  File "C:\Users\munsterlander\AppData\Local\Programs\Python\Python36-32\lib\subprocess.py", line 997, in _execute_child
    startupinfo)
FileNotFoundError: [WinError 2] The system cannot find the file specified
>>>

munsterlander avatar Aug 28 '22 23:08 munsterlander

What if you say subprocess.run("flutter build web"), or subprocess.run("flutter build web", shell=True)?

st-pasha avatar Aug 29 '22 01:08 st-pasha

subprocess.run("flutter build web", shell=True)

That runs! Its currently compiling.

Edit: The first one failed with the same errors as before.

Edit2: Success!

>>> subprocess.run("flutter build web", shell=True);
Running "flutter pub get" in examples...                         1,545ms

 Building with sound null safety

Compiling lib\main.dart for the Web...                             53.2s
CompletedProcess(args='flutter build web', returncode=0)
>>>

Edit3: And....failure. So I updated the python file with that command, ran it and now we get:

Compiling Flutter app [flame-examples]documentation
Error: No pubspec.yaml file found.
This command should be run from the root of your Flutter project.

Exception occurred:
  File "C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_sphinx\extensions\flutter_app.py", line 206, in _copy_compiled
    dirs_exist_ok=True,
TypeError: copytree() got an unexpected keyword argument 'dirs_exist_ok'
The full traceback has been saved in C:\Users\munsterlander\AppData\Local\Temp\sphinx-err-k3y0ah4d.log, if you want to report the issue to the developers.
Please also report this if it was a user error, so that a better error message can be provided next time.
A bug report can be filed in the tracker at <https://github.com/sphinx-doc/sphinx/issues>. Thanks!

So my guess is not the command if you will, but the directory in which its being called.

munsterlander avatar Aug 29 '22 01:08 munsterlander

New problem, but I should be able to solve it shortly. The docs for the python http server apparently are incorrect:

C:\Users\munsterlander\AndroidStudioProjects\flame>python -m http.server 8000 --directory doc/_build/html
usage: server.py [-h] [--cgi] [--bind ADDRESS] [port]
server.py: error: unrecognized arguments: --directory doc/_build/html

Although the python docs say you can do directory, it was throwing an error. Simply going to that directory and running the command got it running.

C:\Users\munsterlander\AndroidStudioProjects\flame\doc\_build\html>python -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Ok, this may all have been because we need python 3.8+ and I am running 3.6. To be clear - lol, I have like 4 versions on this machine for different environments. I should have had them all in containers, but - well, I didn't. I am removing all the older ones.

munsterlander avatar Aug 29 '22 01:08 munsterlander

@st-pasha So the whole issue was not the python code, but the python version. What clued me in, was this comment about python version for the error regarding dirs_exist_ok: https://stackoverflow.com/questions/63661976/problems-using-shutil-copytree-in-python

Anyway, once I upgraded to Python 3.8+ (I am running 3.9.x), I reverted the code and it all ran perfectly. I will update the docs to state that Python 3.8+ is required and I would suspect there needs to be a check in one of the scripts.

munsterlander avatar Aug 29 '22 02:08 munsterlander

I reverted the code and it all ran perfectly.

So subprocess.run("flutter build web", shell=True) is not actually needed, it runs fine as-is? In that case bumping the min required version of Python would definitely be the easiest solution, especially since Python 3.6 has reached its end of life already: https://endoflife.date/python

st-pasha avatar Aug 29 '22 19:08 st-pasha

I reverted the code and it all ran perfectly.

So subprocess.run("flutter build web", shell=True) is not actually needed, it runs fine as-is? In that case bumping the min required version of Python would definitely be the easiest solution, especially since Python 3.6 has reached its end of life already: https://endoflife.date/python

Yes, the original subprocess code runs fine. It was only the min version of python. I have updated the docs accordingly.

munsterlander avatar Aug 29 '22 20:08 munsterlander

Ok, now no clue what is going on. The errors have come back. I had to revert to the new subprocess command to get it building but then tons of errors regarding missing yaml files. I will keep working on this.

Edit: It seems like a path issue. I added this code and it runs, but throws a lot of errors for klondike where it didn't in the past.

cwd = os.getcwd()
            # Print the current working directory
            print("Current working directory: {0}".format(cwd))
            abspath = os.path.abspath("../flame/examples")
            os.chdir(abspath)
            subprocess.run("flutter build web", shell=True)

And the good news is, it all built and runs fine. I was able to get the physics example to work inside the little component.

munsterlander avatar Aug 29 '22 23:08 munsterlander

Well thats odd....not certain why the build is failing. Error is unable to install the flame package. Nothing was changed to pubspecs after the last successful build.

munsterlander avatar Aug 30 '22 23:08 munsterlander

Note: there's file doc/_sphinx/extensions/__pycache__/flutter_app.cpython-39.pyc in the PR currently -- it's a temporary file that shouldn't be checked in. Perhaps add pyc extension to the gitignore?

st-pasha avatar Aug 31 '22 00:08 st-pasha

Note: there's file doc/_sphinx/extensions/__pycache__/flutter_app.cpython-39.pyc in the PR currently -- it's a temporary file that shouldn't be checked in. Perhaps add pyc extension to the gitignore?

Will do and revert and remove.

Edit: So I reverted and removed the cached files. Added it to gitignore. The build keeps failing due to:

Running "flutter pub get" in flame...                           
Warning: pubspec.yaml has overrides from pubspec_overrides.yaml
Because test >=1.17.11 <1.18.0 depends on test_api 0.4.3 and test >=1.18.0 <1.18.1 depends on test_api 0.4.4, test >=1.17.11 <1.18.1 requires test_api 0.4.3 or 0.4.4.
And because test >=1.18.1 <1.19.0 depends on test_api 0.4.5 and test >=1.19.0 <1.19.3 depends on test_api 0.4.6, test >=1.17.11 <1.19.3 requires test_api 0.4.3 or 0.4.4 or 0.4.5 or 0.4.6.
And because test >=1.19.3 <1.19.4 depends on test_api 0.4.7 and test >=1.19.4 <1.20.0 depends on test_api 0.4.8, test >=1.17.11 <1.20.0 requires test_api 0.4.3 or 0.4.4 or 0.4.5 or 0.4.6 or 0.4.7 or 0.4.8.
And because test >=1.20.0 <1.21.2 depends on test_api 0.4.9 and test >=1.21.2 <1.21.3 depends on test_core 0.4.14, test >=1.17.11 <1.21.3 requires test_api 0.4.3 or 0.4.4 or 0.4.5 or 0.4.6 or 0.4.7 or 0.4.8 or 0.4.9 or test_core 0.4.14.
And because test >=1.21.3 <1.21.4 depends on test_core 0.4.15 and test >=1.21.5 depends on test_core 0.4.17, test >=1.17.11 <1.21.4-∞ or >=1.21.5 requires test_core 0.4.14 or 0.4.15 or 0.4.17 or test_api 0.4.3 or 0.4.4 or 0.4.5 or 0.4.6 or 0.4.7 or 0.4.8 or 0.4.9.
And because test >=1.21.4 <1.21.5 depends on test_core 0.4.16 and every version of flutter_test from sdk depends on test_api 0.4.12, if flutter_test from sdk and test >=1.17.11 then test_core 0.4.14 or 0.4.15 or 0.4.16 or 0.4.17.
And because every version of flame_test from path depends on test ^1.17.12 and test_core >=0.4.13 depends on analyzer >=3.3.0 <5.0.0, if flutter_test from sdk and flame_test from path then analyzer >=3.3.0 <5.0.0.
And because flame depends on dartdoc ^4.1.0 which depends on analyzer ^2.7.0, flutter_test from sdk is incompatible with flame_test from path.
So, because flame depends on both flutter_test from sdk and flame_test from path, version solving failed.

I didn't change anything in the pubspec and the last person to change it was Spydon 11 days ago but this was building after that. The only change is in the python, but that code runs fine locally and shouldn't have anything to do with the flutter pub get in examples\examples that is causing the failure. I do not see these packages in the examples pubspec, nor where they ever there, so I assume they get injected from upstream.

munsterlander avatar Aug 31 '22 00:08 munsterlander

I didn't change anything in the pubspec and the last person to change it was Spydon 11 days ago but this was building after that. The only change is in the python, but that code runs fine locally and shouldn't have anything to do with the flutter pub get in examples\examples that is causing the failure. I do not see these packages in the examples pubspec, nor where they ever there, so I assume they get injected from upstream.

Haven't had a look yet but I would guess it is due to the new Flutter version (or possibly some updates package).

spydon avatar Sep 01 '22 08:09 spydon

Ok don't know why it closed. I accepted all incoming changes and just updated my branch. Regardless, this PR was so far behind, I will open a new one and just reference this one. I fear something may have got lost in the merge.

munsterlander avatar Oct 31 '22 00:10 munsterlander