You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As suggested by @gleamish and continuing on issue #24 maybe it would be interesting to consider a plugin-system to enrich what is contained in useThree and how the props of T are resolved.
One of the concerns I have for the plugin-system is how it would interact with third party solid-three components:
how can you create components if you are not sure what props they will have (or how those props will behave)?
if the prop resolution is handled in the root-component <Canvas/>, it could also extend external components, but how can you type them dynamically?
threlte opts for using context to inject the plugins and augmenting module namespaces to set the prop types. These 2 approaches are fundamentally at odds in my opinion, as context is about scoping and augmenting module namespaces are global. That being said, in 99.9999% of the user-projects there will only be a single threejs context, so it's not bad to have it as an option.
I am not sure how threlte handles typechecking external components if the props they use are actually valid in the project, but I was thinking of something like this:
from my-lib
Library authors would need to make their components generic
It's a bit of plumbing, but it would give you type-security.
A QOL idea would be that you can pass your external components in the createT-call, this way they could be typechecked at the declaration and <Box<Plugin> /> can become <T.Box />:
import{Box}from"my-lib"import{Canvas}from"./t.ts"declare global {namespace$3{interfaceProps{onMouseDown(event: MouseEvent): void}}}return<Canvas><BoxonMouseDown={console.log}/></Canvas>
Then you are also assured that the props your component relies upon are being handled as you would expect.
Or add a plugins-prop for complete granular control?
This is how I imagine it: cascading over multiple levels.
Base layer is the <Canvas/>-context 👉 components mount in <Canvas/> will fall back to resolving their props according to the plugins defined in createT. This includes external components. (?)
if in createT(..., [plugin1, plugin2]) plugin1 and plugin2 try to handle the same prop, the last wins (?)
All entities defined with createT and accessed via T. have their props resolved via the plugins defined in createT. This allows for libraries to define their own T for internal use.
Every <Entity /> and <T. /> component has a plugins-prop, in which they can pass plugins too. If a property is defined these will take preference.
The fact that it is also distributed over context is a bit awkward as
Activity
[-]consider plugin-system[/-][+]feat: consider plugin-system[/+]bigmistqke commentedon Aug 4, 2025
prior art: https://threlte.xyz/docs/learn/advanced/plugins
bigmistqke commentedon Aug 24, 2025
One of the concerns I have for the plugin-system is how it would interact with third party
solid-threecomponents:<Canvas/>, it could also extend external components, but how can you type them dynamically?threlte opts for using context to inject the plugins and augmenting module namespaces to set the prop types. These 2 approaches are fundamentally at odds in my opinion, as context is about scoping and augmenting module namespaces are global. That being said, in 99.9999% of the user-projects there will only be a single threejs context, so it's not bad to have it as an option.
I am not sure how threlte handles typechecking external components if the props they use are actually valid in the project, but I was thinking of something like this:
from my-lib
Library authors would need to make their components generic
The generic extends from
Plugin<{ ... }>defining all the non-standard props that the component makes use of.user project
It's a bit of plumbing, but it would give you type-security.
A QOL idea would be that you can pass your external components in the
createT-call, this way they could be typechecked at the declaration and<Box<Plugin> />can become<T.Box />:The same can be done for
<Entity<Plugin> />👉<T.Entity />bigmistqke commentedon Aug 24, 2025
Maybe it could fallback to the namespace if no generic is passed
my-lib
user code
bigmistqke commentedon Aug 24, 2025
Another option would be to follow in threlte's footsteps and also allow to scope plugins via context-scope:
Then you are also assured that the props your component relies upon are being handled as you would expect.
Or add a plugins-prop for complete granular control?
Question is then: how do these interact with plugins defined in
createT(..., [...plugins])?bigmistqke commentedon Aug 24, 2025
Some pseudo code
my-lib
user code
This is how I imagine it: cascading over multiple levels.
<Canvas/>-context 👉 components mount in<Canvas/>will fall back to resolving their props according to the plugins defined increateT. This includes external components. (?)createT(..., [plugin1, plugin2])plugin1 and plugin2 try to handle the same prop, the last wins (?)createTand accessed viaT.have their props resolved via the plugins defined increateT. This allows for libraries to define their ownTfor internal use.<Entity />and<T. />component has a plugins-prop, in which they can pass plugins too. If a property is defined these will take preference.The fact that it is also distributed over context is a bit awkward as
and
will have different ways of resolving their prop...
but context is a bit awkward in general, so ye.