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

[slottable-request] Initial draft #45

Merged
merged 10 commits into from
Apr 4, 2024
Merged

[slottable-request] Initial draft #45

merged 10 commits into from
Apr 4, 2024

Conversation

kevinpschaaf
Copy link
Collaborator

There are often use cases that require a component to render UI based on data, but where the specific rendering of the data should be controllable by the user.

This proposal describes an interoperable protocol for components to request that their owning context render slotted content into themselves, using data provided via a protocol-defined event.

@sorvell
Copy link
Collaborator

sorvell commented Aug 30, 2023

For discussion:

  1. slotAssignment: "manual" is now pretty widely supported. Should this API change to require or support this? It would mean the user would not be required to use/render a slot name.
  2. The protocol should support the requester making decisions about what to render based on the response to the slottable-request event. Both these points could possibly be addressed if slottable-request listener is required to (or can optionally) return a response to the event with the nodes to assign. To accommodate asynchronous rendering systems, this could be (or optionally be) a Promise. Examples:
    • A button that can display an icon can decide not to render specific spacing around the icon if none is provided.
    • A list could ask for items to be slotted and let the provider could provide N items and the list renderer would then only render N items.
  3. Support for re-distribution: I think this will be hard but it should probably be considered. Slots can only be re-distributed in their entirety so I think at the least each element in the distribution tree would have to participate in using the protocol.

@sorvell sorvell mentioned this pull request Sep 18, 2023
1 task
There are often use cases that require a component to render UI based on data, but where the specific rendering of the data should be controllable by the user. Use cases of this include:

* A data table or tree component, which manages the rendering of cells or tree nodes, but where the given look and feel of the items is customizable by the user.
* A virtual list component, which optimizes rendering a list of data by only rendering a subset of user-templated items based on which array instances are visible on the screen.
Copy link
Contributor

Choose a reason for hiding this comment

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

This virtual list does not use this pattern: https://github.com/WICG/virtual-scroller

Worth reviewing why they came to the decision not to.

Copy link
Member

Choose a reason for hiding this comment

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

The virtual-scroller effort is defunct, but even so it did seem to follow a very similar pattern where the container fires an event to tell the host to render new content: https://github.com/WICG/virtual-scroller#rangechange-event

Can you say what you think the important differences are?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's unclear to me how the event-listener is intended to provide the content. Inline examples would help clarify this for me.

Copy link
Collaborator

@Westbrook Westbrook left a comment

Choose a reason for hiding this comment

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

I'm of a mind to approve this into draft state as part of our monthly gathering on Tuesday. Any opposed, feel free to chime in!

@matthewp
Copy link
Contributor

matthewp commented Nov 7, 2023

@Westbrook I have unanswered questions about this proposal that would be nice to have answered before it is merged.

@Westbrook
Copy link
Collaborator

@matthewp I'd say that working through questions like yours is 100% what is meant by moving something to "draft" status:

Draft A protocol generally agreed to be an interesting area of investigation is given "Draft" status. At this point, the conversation will pivot away from proving the need for a protocol and towards proving a specific pattern by which the protocol can be achieved across the web components community. Developmental work in this area can be addressed in PRs or via group meetings of the w3c's Web Components Community Group as needed.

If you are in disagreement with this being an "interesting area of investigation", we'd love to hear more about why you felt that way.

Learn more about our other protocol status levels on our README.

Be sure to also join our monthly WCCG meetings, managed on this calendar, or join us on Discord to further the conversation there.

@matthewp
Copy link
Contributor

matthewp commented Nov 8, 2023

@Westbrook If that's the barrier to having something merged in this repo, then the current proposal format which focuses on how the API works is not the best way to go. If the question is whether something is "interesting area of investigation" then the format should focus mostly on goals and non-goals. I've supposed a new proposal template for this end: #47

export const remove = Symbol('slottable-request-remove');
```

When a component wants to render customizable content, it (1) fires a `slottable-request` event for a given slot name, and (2) renders a `<slot>` element into its shadow root corresponding to the requested slot name.
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if the component is defined before the parent component has been defined? It will miss these events.

One solution here is to have an imperative API, such as a function that you can call, to cause a render to occur.

Copy link
Collaborator Author

@kevinpschaaf kevinpschaaf Nov 8, 2023

Choose a reason for hiding this comment

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

Could you explain who would call the imperative API, and how they would know call it?

Slotting is really a form of dependency injection, and it does get tricky when the owning scope isn't available to provide dependencies. We've dealt with this in the context protocol at an implementation level by having an "unfulfilled context request" manager at the root re-dispatch context requests from consumers to providers see discussion. We haven't updated the protocol to include that yet, but we implement it in Lit's version to pretty good effect, and I could see something like that forming a solution.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's unclear to me how the event-listener is intended to provide the content. Inline examples would help clarify this for me.

The `remove` symbol is provided as a sentinel value for `data` to indicate that the given content assigned to `slotName` should be removed, and should be fired when the associated `<slot>` is no longer rendered to avoid leaking light-DOM children.


## Examples
Copy link
Contributor

Choose a reason for hiding this comment

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

Code examples here would be good.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The playground links are code examples -- were you looking for inline code snippets? They got a bit long and distracting so moved them to playground.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, outside links are fine too, but linkrot exists so there should be inline examples to view, and having a condensed version is a good exercise to see if the API scales down to simpler cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, all of these examples are using a framework, so it's harder to tell what is going on unless you understand the internals of what the framework is doing. So one or two plain JS examples would be good.

@justinfagnani
Copy link
Member

@sorvell

slotAssignment: "manual" is now pretty widely supported. Should this API change to require or support this? It would mean the user would not be required to use/render a slot name.

I wonder what this would look like? The container would presumably need some way for the host to signify which slots to target. At the limit there isn't a way to do this because the container could assign to slots by any criteria. It could specify something like "the host should render an <input> and <label> per item" and then slot those manually. I don't know how we'd describe that in the protocol, other than just making the slot name in the event nullable for those cases.

@Westbrook
Copy link
Collaborator

Westbrook commented Nov 8, 2023

slotAssignment: "manual" is now pretty widely supported. Should this API change to require or support this? It would mean the user would not be required to use/render a slot name.

Important side note for this if you use slot assignment manual, then you could be reducing the depth flexibility of your slottables. All items must be manually slotted, and you can't manually slot through multiple slots without needing updated content in each DOM tree, which may be prohibitive from a performance and bookkeeping standpoint.

I also was drawn to the idea, especially how it does some interesting things for complex accessibility conversations to bring the "options" into the shadow root as multiple <slot> elements. I need to play with it more before sharing code, but the idea of post request slots roughly looking as follows is quite interesting:

#shadow-root
  <button aria-activedescendant="x-1">Select trigger</button>
  <div role="listbox">
     ...
     <slot role="option" id="x-1"></slot>
     <slot role="option" id="x-2"></slot>
     ....
  </div>

@mattlucock
Copy link

mattlucock commented Nov 21, 2023

Something I really feel is being overlooked here is that web components are already the standard mechanism for constructing a DOM tree on-demand and passing data to it, which seemingly is what this proposal effectively seeks to reinvent, which is a little puzzling.

#8 and lit/lit#1478 seem to be helpful context for understanding this proposal. I'm very confused by the talk of different templating and rendering implementations, because I don't see how they are relevant; web components already abstract their implementation details from their users, and there's no reason why we'd want to leak them; even better, web components encapsulate their internals more broadly. We can very easily pass a web component as a prop to another web component, either as a reference to a class or as a string name, and easily construct the passed component since all web components have a standard API contract for construction. We can also trivially render a web component inside another web component without concern about styles, since web components encapsulate their styles. And, of course, we can pass data to web components using attributes/properties/slots, with the added benefit that web components can respond to updates without the user needing any knowledge of this.

Here is a bare-bones, vanilla list rendering example. <x-list> renders list items using the specified <x-list-item> component as a template. It just works.

Advantages of this approach over the proposed approach include:

  • It's way simpler.
  • No potential performance overhead of events or complexity in catching them.
  • The component can, if it wants to, elegantly handle the case where no template is provided.
  • It can be used declaratively as shown (and so trivially supports SSR), which I feel is a must for this given that we're dealing with specifying the content of the page.
  • The component does not leak responsibility to the user of appending elements to the component and removing them when requested, which is responsibility the user shouldn't have. Likewise, the component does not leak knowledge of its internal structure to the user.

Reading the proposal and the provided proof-of-concepts, I'm struggling to see any meaningful benefit this proposal would achieve that my above example does not show, in principle, can be achieved in a much simpler way just by using web components. I look at the code for handling slottable requests in the provided list proof-of-concept, and I see the code for imperatively constructing a list item element and appending it to the DOM, and I think "this thing is just a web component, except it comes with extra steps, and it's not as good".

If I'm missing something obvious, I'm very happy to have it explained to me.

@Haprog
Copy link

Haprog commented Feb 5, 2024

Reading the proposal and the provided proof-of-concepts, I'm struggling to see any meaningful benefit this proposal would achieve that my above example does not show, in principle, can be achieved in a much simpler way just by using web components.

I think one clear benefit here with the proposal and the PoC is that within a React context this proposal makes it possible to easily create and use React components that wrap a Web Component, but also provides an easy way to provide content via callback in a React-like way (JSX template).

With this (as in the PoC):

  • As a React component library author: I can create React component wrappers for Web Components and allow the user of the React component to intuitively provide content via "render props" (callback that returns JSX/ReactNode), if the web component would support this protocol instead of library specific rendering like lit-html.
  • As a React application developer/maintainer (consumer of React components): I can use React components with good DX, even if the React component internally uses a Web Component without me needing to know how to create Web Components or how to convert my existing React components/templates into Web Components just to be able to control a small piece of rendering in a component I want to use.

Without this proposed solution in the React context the application developer would need to be ok with managing both React components and Web Components code with two different kinds of rendering/templating instead of just being able to do everything in one way (as plain React components). It will add much unnecessary complexity to a React app if the React app needs to directly mix implementation of Web Components and React components.

I'm not sure if this proposal has some non-obvious limitations or performance issues, but at least I see the value it provides for some use cases and it does seem to work with good DX for the component consumer.

}
}

export const remove = Symbol('slottable-request-remove');
Copy link
Contributor

Choose a reason for hiding this comment

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

Given that remove is a short, common identifier, we may want to consider exposing this as a static symbol like SlottableRequestEvent.remove.

Copy link
Collaborator

@Westbrook Westbrook left a comment

Choose a reason for hiding this comment

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

If we move this PR to target the "Proposal" status, the lowest possible status, we agreed as per the March monthly WCCG meeting that merging this PR is appropriate. We'll do our best to capture any conversations into issues for follow-up as the protocol moves up the various status levels (or not), so feel free to get your last comments posted over the next week and a half. On the 25th of March this PR will be merged as a "Proposal" protocol.

README.md Outdated Show resolved Hide resolved
@Westbrook Westbrook mentioned this pull request Mar 16, 2024
@mattlucock
Copy link

mattlucock commented Apr 10, 2024

@Westbrook I've been absent for a little while so I'm just catching up with this now, and I must say I'm a little perplexed. Back in November, I was trying to raise the alarm that this whole idea is misguided. I was really hoping that if you folks disagreed with me (which I expected you did), you folks would explain why and we could discuss it. You folks making no real acknowledgment of my contribution and then merging this anyway feels bizarre to me. You say

We'll do our best to capture any conversations into issues for follow-up as the protocol moves up the various status levels (or not)

and I can tell that you're deliberately trying to reassure me in saying this, but this doesn't reassure me of anything because:

  • If you folks are moving forward with this idea, how can you incorporate my contribution that the idea is fundamentally misguided into that process? You folks not acknowledging my contribution simply means that you folks have dismissed it and ignored it.
  • I expect that this inevitably will move up the status levels, because you folks have already decided that you want to do this, and that's seemingly the basis upon which ideas get adopted here. It doesn't feel like the prospect of this being fundamentally misguided was something you folks were ever open to considering.

I also feel the same way regarding #15.

I want to be disagreed with and I want to have technically rigourous conversations that will make the web better. But I don't see how I can make constructive/productive contributions to this group when the constructive/productive contributions I try to make are dismissed and ignored.

@benjamind
Copy link
Collaborator

benjamind commented Apr 10, 2024

@mattlucock I think this comment did begin to address the need for this protocol that your proposed solution does nothing to support: #45 (comment)

You should follow up if you have a counter proposal, but I suspect the lack of response from you was taken as a sign that the proposal as stated with that reasoning was valid enough to continue with. Those of us working in mixed technology environments are keenly aware that interop between React, Angular and other rendering frameworks with web components is a highly desirable capability.

@hunterloftis
Copy link
Contributor

@mattlucock at work we’ve been testing an early implementation of this proposal on a non-trivial app. During that test, I’ve observed some properties of slottable requests that are desirable and, I think, not achievable via the element-name-as-string approach. If there are ways to achieve them with simple strings and custom element definitions, I would be interested in exploring that further:

Styling from the host

When I provide a <span slot=“title”>Title</span> to an <x-container>, I am able to style that element at the host level, since it’s a child of the host rather than the <x-container> element that it's projected into. Nodes that are appended into the shadow root of <x-container> rather than projected via a slot are not able to be styled as children of the host.

Notification of use

Using stringly-typed element names and relying on <x-container> to create and append elements precludes the host knowing when the conditional content is needed (or if it ever is). For large applications, this means that you must import every stringly-typed element that possibly could be used on the page, rather than dynamically importing content when you know it will be used. By providing list-item=“x-list-item” as input, I’ve made a promise that such an element is available for use.

Event listeners at the host level

I commonly need to attach event listeners to slotted children. For example, listening to events and state changes on slotted, nested menu items within a menu. Delegating element creation to the menu container means - as a consumer of a menu/menu-item system - I can no longer configure those children. Event listeners are a common example of this, but other configurations like inline styles, conditional attributes, etc are also not possible. You might work around this by passing in a more complex configuration object that mirrors the structure of the DOM, but at that point, you’ve abandoned simplicity.

Multiple or heterogeneous children

Following along the theme of customization, another common need is to provide something like <x-menu-item><x-icon name=“foo” active></x-icon>Bar</x-menu-item> rather than just <x-list-item>Bar</x-list-item>. When each item slotted (or, in your example’s case, appended to the internal shadow root) may wrap several nodes, in arbitrary orders, with arbitrary configuration, styling, event listeners, etc, the burden of configuration and bookkeeping in a stringly-typed system skyrockets. In a non-trivial application, wrapping all such configurations as named custom elements themselves would similarly lead to a combinatorial explosion of components. There’s nothing technically barring this, but I would argue it again moves away from the main benefit of the string-based approach (simplicity).

Optimization, not architecture

Finally, one of the nicest properties we’ve seen from our test implementation is that it’s an optimization pattern that leverages existing primitives (like slots). If I adopt a stringly-typed approach that puts the burden of element creation & updating on the internal container, that’s an architectural decision that I have to make early and carry through. It impacts the scope of my styles, the mechanism by which I create and configure and render elements, etc.

However, I can naively implement a system that leverages standard slotting behavior and build out a complex application. I can profile the application to find large DOM subtrees of unrendered, slotted elements, and I can recursively augment them with slottable requests to optimize them. I might start at a top-level menu with a thousand nodes that mostly don’t need to be shown, for the biggest immediate performance win. Then, I could address each sub-menu. Then, perhaps surgically optimize particularly large sub-sub-menus. The ability to start with built-in, standard behaviors, then progressively measure-and-optimize, has proved to be a large benefit for us.

@mattlucock
Copy link

@benjamind

I suspect the lack of response from you was taken as a sign that the proposal as stated with that reasoning was valid enough to continue with

I struggle to believe this because:

  • My criticism of the fundamental technical principles of the proposal was unequivocal.
  • Nobody pinged me or otherwise asked what I thought.
  • This is more tenuous, but given how slowly many issues and PRs move, including in this repository, I don't think that much time had really passed relatively speaking.

Every engineering decision involves both benefits and tradeoffs; if you folks had given consideration to the concerns and tradeoffs I touched on but felt as though they were justified given the benefits, I really think someone should have said that. Nobody did. I think it is reasonable to feel troubled that nobody made direct acknowledgment of the points I had made, as if it was an unserious contribution not even worthy of being acknowledged.

I'll post a follow-up comment in response to both you and @hunterloftis, grappling with the technical aspect more holistically.

@mattlucock
Copy link

mattlucock commented Apr 10, 2024

Firstly, I just want to make clear that that I do not actually think that globally defined custom elements with globally unique string names are a good idea; in fact, there may actually be no bigger critic of them than me, and if you could see what I'm working on, you'd know what I mean when I say that. Indeed, I'm comforted by the concern being raised about them here, because I think it's more evidence that I'm right about this. The vanilla bare-bones example I showed was using the standard APIs in the simplest way possible, and so necessarily it was a 'purist' web components example adhering to the traditional web components dogma. Again, I don't really think that this is actually the right way to go about things. So @hunterloftis,

If there are ways to achieve them with simple strings and custom element definitions, I would be interested in exploring that further

I'm afraid I'm going to disappoint you on that one, but I think there are other ways we can use web components to achieve what we want to.

My understanding of these proposals was that they were primarily concerned with interoperability between varying web component implementations, so you'll have to forgive me for considering this proposal in that context. But my general response to the framework interop point is this: allowing standards-unfriendly frameworks to interop with web components necessitates web components implementing a complex and unnatural abstraction, specifically for the purpose of this interop, because... why, exactly? Web Components should have responsibility and knowledge of this because why exactly? The proof-of-concept linked in the proposal shows that this proposal requires abstraction on the React side anyway—which of course is almost certainly unavoidable, but it actually means that this proposal is effectively the worst of both worlds, since both sides need to be abstracted.

It would be much, much better if only the React side had to be abstracted. Thinking about this in my head and referring to the React abstraction in the proposal's proof-of-concept as inspiration, I think we could likely develop a React abstraction that would be end up being functionally equivalent to what is proposed here in terms of behavior, and that would both equivalent to the consuming React component in terms of API, and equivalent to the web component in terms of API. I would suggest that the reason what I'm thinking of hasn't already been thought of is because it getting it to work would require a significant amount of creative problem solving. Would this React abstraction be elegant? No, but React is inherently inelegant, so I think React users would be fine with that.

I guess my overall thesis here is that, if a standards-unfriendly framework wants to interoperate with a standard component, that's got nothing to do with the web component itself; that's the framework's problem, and the onus is on the framework to solve it. And while it's true that web components seem like they have really problematic limitations, they're standard APIs, and I think it's actually very possible to use other standard APIs to overcome many of those limitations if we're clever about it and employ some creative problem solving. If we step outside of a particular set of preconceived notions, we can use standard APIs to do basically whatever we want.

Funnily enough, even though what I'm working on only relates to interoperating web components with other web components, I've actually been toying with an idea that was directly inspired by this proposal, and achieves a functionally very similar result—that is, defining a template that is part of your component, but actually rendering it inside of another. Like, imagine render props, but for web components! I had cooled on my particular idea because I wasn't sure if it would really be useful or necessary, but this has me thinking again that it probably would be. This mechanism would actually be elegant, as opposed to an equivalent React abstraction which wouldn't be, but refer to my previous remarks about that.

I realise this all sounds quite hypothetical, and that I'm saying this can be done but I haven't proved that it can. I guess probably the best thing I can do to action this would be to ship what I have to prove that the mechanism works between web components, and then show how the mechanism could be extended to interoperate with a standards-unfriendly framework. Life is messy at the moment (hence my absence) but I'm hopeful I can ship something relatively soon.

But to be clear: I have no counter-proposal for a protocol, because I don't think this needs to be or a should be a protocol.

I've largely focused on limitations of web components since that seems to be the main source of your concern @hunterloftis, but if you have deeper concerns I'll try to speak to them.

@hunterloftis
Copy link
Contributor

@mattlucock, unless I misread, it sounds like you both initially and presently hold that this proposal / standard is unnecessary. I read your original comment as a proposal of an alternative mechanism to solve the problems addressed by the slottable requests approach, based on:

Here is a bare-bones, vanilla list rendering example. renders list items using the specified component as a template. It just works ... Advantages of this approach over the proposed approach include ...

But based on your most recent comments, would it be accurate to say that you are not proposing an alternative approach that solves these problems?

I do not actually think that globally defined custom elements with globally unique string names are a good idea; in fact, there may actually be no bigger critic of them than me

The vanilla bare-bones example I showed was using the standard APIs in the simplest way possible, and so necessarily it was a 'purist' web components example adhering to the traditional web components dogma. Again, I don't really think that this is actually the right way to go about things.

Application developers building with web components today need some mechanism of addressing these issues:

  1. DOM node count impacts performance and is unacceptably high in many large web apps.
  2. Conditionally rendering DOM nodes across components, rather than within components, requires some interface between the components.
  3. Large web apps rely on inter-operating component libraries from a variety of vendors, both internal and external.

Slottable requests address these via:

  1. Conditionally rendering slotted content to reduce the total number of nodes required in the DOM
  2. Leveraging the platform's event system to coordinate across components
  3. Decoupling the rendering mechanism from the coordination mechanism, such that an app composed of "vanilla," Lit, FAST, and other web components can operate cohesively.

@mattlucock
Copy link

@hunterloftis I must admit that I'm confused by your confusion. I am indeed proposing an alternative approach that solves these problems and I spent multiple paragraphs trying to motivate such a thing. I was deliberately vague about how such a thing would work because this is something I've actively working on behind the scenes, so I'd rather just get it implemented, ship it, and then show it to you (as I tried to explain in my previous comment). In my original comment, the central claim I was making was

web components are already the standard mechanism for constructing a DOM tree on-demand and passing data to it

and the bare-bones, vanilla example I showed was in support of that claim; I was trying to show that the concept worked. You're right to point out that there are some practical issues with doing it the way I showed, but this doesn't really surprise me since it was just a bare-bones, vanilla example trying to show that the concept was correct. To address those limitations we would unfortunately need to make things a little bit more complicated, but I'm confident we can develop something that addresses those limitations and is based on the same concept I showed in the example—no protocol required. And even though it would be a bit more complicated of an abstraction, I think it would still be significantly less complex than what this proposal entails. Again, if you think there are deeper concerns with the concept beyond just practical limitations of web components, I would be keen to hear your thoughts on them.

@benjamind
Copy link
Collaborator

@mattlucock when you're ready to share an actual proposal for how to address the goals outlined in the draft proposal in this repo feel free to file an issue and detail them.

Thats how this process is supposed to work, an initial draft of a protocol that solves a number of issues has been made, its a concrete proposal, but is still only in 'proposal' status. It is now time for counter proposals, and revisions to be made as separate issues in this repo for folks to be able to concretely discuss the relative merits of them.

When you've got details to share, please file the ticket and I'm sure folks will be happy to discuss your approach. Concrete examples will help the discussion I'm sure.

@matthewp
Copy link
Contributor

matthewp commented Apr 10, 2024

A proposal is essentially meaningless. You could propose that all web components have the wc- prefix and according to the process here, that would be merged.

The problem is that almost no other proposal process works that way and essentially everyone in the general community thinks a merged proposal is an acceptance and a pseudo standard.

@mattlucock
Copy link

mattlucock commented Apr 10, 2024

Three things:

Firstly, @benjamind I hear you loud and clear, and I appreciate you engaging with me in good faith. Will do.

Secondly, In my original comment I said

I'm struggling to see any meaningful benefit this proposal would achieve that my above example does not show, in principle, can be achieved in a much simpler way just by using web components.

When I said this I was referring to web components being a basis upon which we can do this, and not necessarily standard conventional web components actually being the way to do this. I can see now that I didn't convey that clearly, and so I consider that I misspoke, and I apologise for that.

Thirdly, I feel like I've already dug myself into a bit of a hole here, and so I don't want to dig deeper right now, but I believe that I definitely share some of @matthewp's concerns about all of this.

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

Successfully merging this pull request may close these issues.