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
Opening this as a design conversation rather than a change request — I'd like to hear how you see the renderer evolving before proposing any code.
Context
Today HTMLRenderer is the only renderer, and the "renderer contract" exists implicitly in its implementation: it walks the Component tree, pulls js_functions / state_initializers from the render context and appends them to head, escapes text nodes, treats RAW_TEXT_TAGS as verbatim, and emits {{...}} placeholders for JSCode children. None of that is declared anywhere as guaranteed behavior — it can only be learned by reading elements/renderer/html.py.
Two observations from working with the code recently:
fix(server): propagate render context to sync handlers in executor #38 showed how easy it is to break implicit behavior invisibly: the threading.local → ContextVar change silently disconnected sync handlers from the render context, and nothing defined (or tested) what was supposed to hold.
The Component tree is a clean, target-agnostic UI AST, and py2js is reusable on its own. That combination makes HTeaLeaf renderable to targets other than an HTML string — but today an external renderer has to depend on undeclared internals (get_render_ctx(), ctx.js_functions, ctx.state_initializers).
What I'd like to discuss
Would you accept extracting a minimal formal interface — e.g. an abstract Renderer base (or Protocol) with render(cmpt: Component) -> str — that HTMLRenderer implements, with the render-context interaction (what a renderer may read, and when injection happens) documented as part of the contract?
Related surface question: which of the currently un-exported names do you consider public? (elements.py's __all__ omits tl_if and the table elements, and elements/__init__.py doesn't re-export them, so consumers must import from htealeaf.elements.elements.)
Is Server.__process_response__ the intended seam for choosing a renderer, or would you prefer renderer selection to live elsewhere (per-app config, per-route, a hook)?
Motivation (full disclosure)
I'm exploring an external, independent package that consumes HTeaLeaf to emit Astro components (server-first HTML + islands from @js/LocalState maps naturally onto Astro's islands model). It would live outside this repo as a normal dependency, not a fork — but it only works cleanly if the renderer boundary is something HTeaLeaf actually promises. Rather than copying internals into that package, I'd prefer to send small PRs here (like #38) so the public API gets formalized where it benefits every consumer.
No urgency — happy to draft the base-class PR if you like the direction, or drop it if you'd rather keep the renderer private for now.
Opening this as a design conversation rather than a change request — I'd like to hear how you see the renderer evolving before proposing any code.
Context
Today
HTMLRendereris the only renderer, and the "renderer contract" exists implicitly in its implementation: it walks theComponenttree, pullsjs_functions/state_initializersfrom the render context and appends them tohead, escapes text nodes, treatsRAW_TEXT_TAGSas verbatim, and emits{{...}}placeholders forJSCodechildren. None of that is declared anywhere as guaranteed behavior — it can only be learned by readingelements/renderer/html.py.Two observations from working with the code recently:
threading.local→ContextVarchange silently disconnected sync handlers from the render context, and nothing defined (or tested) what was supposed to hold.Componenttree is a clean, target-agnostic UI AST, andpy2jsis reusable on its own. That combination makes HTeaLeaf renderable to targets other than an HTML string — but today an external renderer has to depend on undeclared internals (get_render_ctx(),ctx.js_functions,ctx.state_initializers).What I'd like to discuss
Rendererbase (or Protocol) withrender(cmpt: Component) -> str— thatHTMLRendererimplements, with the render-context interaction (what a renderer may read, and when injection happens) documented as part of the contract?elements.py's__all__omitstl_ifand the table elements, andelements/__init__.pydoesn't re-export them, so consumers must import fromhtealeaf.elements.elements.)Server.__process_response__the intended seam for choosing a renderer, or would you prefer renderer selection to live elsewhere (per-app config, per-route, a hook)?Motivation (full disclosure)
I'm exploring an external, independent package that consumes HTeaLeaf to emit Astro components (server-first HTML + islands from
@js/LocalStatemaps naturally onto Astro's islands model). It would live outside this repo as a normal dependency, not a fork — but it only works cleanly if the renderer boundary is something HTeaLeaf actually promises. Rather than copying internals into that package, I'd prefer to send small PRs here (like #38) so the public API gets formalized where it benefits every consumer.No urgency — happy to draft the base-class PR if you like the direction, or drop it if you'd rather keep the renderer private for now.