RFC: Unified Renderer - #633
Conversation
80b6f35 to
18b6a6a
Compare
pgherveou
left a comment
There was a problem hiding this comment.
please rewrite that as a 1 pager RFC issue, the details of all the types can live in the PR, the RFC is just here to express the high level concepts
|
Issue: #666 |
| /// Streams renderer trees for one product-rendered body. Each item | ||
| /// replaces the previous tree. The stream stays open while the body is | ||
| /// displayed so the product can redraw in place. | ||
| fn render( |
There was a problem hiding this comment.
we can also have different render api for each modalities instead of a single with an enum carrying all the variants.
I suppose some app will only support some modalities, and can just define subscription handlers for the one they support
There was a problem hiding this comment.
The intent is precisely to collapse all future rendering APIs into this single one. A small enum describing every possible context seems preferable to duplicating the same API each time, which would require substantial supporting code and a lengthy RFC section to describe it.
My proposal is a single method in TruAPI. For convenience, we can expose multiple rendering calls in product-sdk, truapi-host, etc., and route them all to this one trait.
|
@eugypalu you should review this as it impacts the Chat modalities you own |
Lead the summary and motivation with the intent, tighten prose to the RFC style rules, drop the chat-specific peer field from the action item, and make the action payload a required Vec<u8> that is empty for a button press.
b9ec013 to
ece35a5
Compare
RFC:
docs/rfcs/unified-renderer.md.Third of a four-PR stack: Subscription Typed Interrupt Payload (#631) → Worker Lifecycle (#632) → Unified Renderer (#633) → Input Modality (#634). Based on the worker-lifecycle branch.
Why this RFC exists
A product has one way to show its own information inside a host surface: a
ChatMessageContent::Custommessage, rendered throughChat::custom_message_renderand answered throughChat::action_subscribe. The request is keyed by message, and a press on a tree button is indistinguishable from a press on a button the host drew for anActionsmessage. The Input Modality needs a product-drawn candidate and the Pocket modality a card face. One service with aRenderContextper surface serves all of them instead of a render callback and an action stream per surface.What it defines
Rendererworker service besideChat: a host-initiatedrenderstream ofRendererNodeand a product-sideaction_subscribe. Both carry aRenderContextnaming where the body lives:ChatMessage { room_id, message_id, message_type },InputWidget { candidate_id },PocketCard { card_id }. A new surface is a new variant.{ context, payload }; a surface that needs a content discriminator carries it in its context variant. The action item is{ context, action_id, payload: Vec<u8> }; the payload is empty for a button press and the UTF-8 bytes of the new value for a text-field change.CallError<GenericError>as their interrupt type. Arenderstream opens when the body comes on screen, closes when it leaves, and each item replaces the whole tree. An open stream is one worker reference under the Worker Lifecycle RFC.Chathas nocustom_message_render; aCustommessage renders throughRenderer::renderwith aChatMessagecontext.ChatActionPayload::ActionTriggeredcarries only presses on host-drawnActionsbuttons.Render tree
RendererNodeis the chatCustomRendererNodevocabulary under a surface-neutral name, with these additions and shape changes. Product authors that build trees by hand or through a serializer need to know all of them.New nodes
Image { modifiers, props: ImageProps }.ImageProps.sourceis anImageSource:Bulletin(cid)for a Bulletin chain blob orArchive(path)for a file inside the product's executable archive, so the tree never carries a URL and the host does the fetch.ImageProps.fitis an optionalImageFit(None,Fill,Cover,Contain,ScaleDown) defaulting toFill. An image that cannot be fetched draws as empty space.Effect { props: EffectProps, children }applies one visual effect to its children.Effectis an enum whose variants each name an effect and carry its parameters;Rainbowis the first. The remaining variants are an open question.New modifiers
Opacity(u8), 0 transparent to 255 opaque.BlendingMode(BlendingMode)with the sixteen values common to CSSmix-blend-mode, SwiftUIBlendModeand ComposeBlendMode: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity.New shape
Shape::Square, besideRounded(radius)andCircle.Shape changes
children:Spacer,TextFieldandImagehavemodifiersand props only.Effecthaspropsandchildrenbut nomodifiers.Shape::Rounded(Size),Modifier::Height(Size),Width,MinWidth,MinHeight,FillWidth(bool),FillHeight(bool). The SCALE encoding is the same as the one-field struct form.OptionBoolis used directly forenabledandloading; there is noOptionalBoolalias. An absent value leaves the default to the host.Scope
The chat path is implemented here. The
PocketCardcontext is the seam the Pocket modality (#609) adopts when it lands; card rendering is not implemented in this PR.Implementation
Part of this PR.
truapi:api/renderer.rs,v01/renderer.rs(moved fromv01/chat/custom_renderer.rs), versioned envelopes,latestre-exports;Chat::custom_message_renderremovedtruapi-codegengoldens and generated dispatcher, wire table, wasm bridgetruapi-server:render_custom_messagebecomesrenderwith aChatMessagecontext onProductRuntimeControl, native and wasm surfaces,native_renderer.rs@parity/truapiand@parity/truapi-host:renderer.onRender,renderer.actionSubscribe, host runtime plumbing