diff --git a/docs/patterns.md b/docs/patterns.md index 4232554..dc29b8c 100644 --- a/docs/patterns.md +++ b/docs/patterns.md @@ -60,13 +60,14 @@ Listen for display mode changes via {@link app!App.onhostcontextchanged onhostco {@includeCode ./patterns.tsx#persistData} -## [TODO] Lower perceived latency / manage loading time +## Lowering perceived latency -Leverage partial inputs to show widgets as possible. +Use {@link app!App.ontoolinputpartial ontoolinputpartial} to receive streaming tool arguments as they arrive, allowing you to show a loading preview before the complete input is available. -Beware of partial JSON being partial (but healed), so some of your objects may not be complete (e.g. in a list you may need to skip the last item if your code validates input schemas strictly). +{@includeCode ../src/app.examples.ts#App_ontoolinputpartial_progressiveRendering} -{@includeCode ./patterns.tsx#lowerPerceivedLatency} +> [!IMPORTANT] +> Partial arguments are "healed" JSON — the host closes unclosed brackets/braces to produce valid JSON. This means objects may be incomplete (e.g., the last item in an array may be truncated). Don't rely on partial data for critical operations; use it only for preview UI. ## [TODO] Supporting both iframe & MCP Apps in same binary diff --git a/docs/patterns.tsx b/docs/patterns.tsx index 9a2f3dd..c481ad3 100644 --- a/docs/patterns.tsx +++ b/docs/patterns.tsx @@ -111,17 +111,6 @@ function persistData(app: App) { //#endregion persistData } -/** - * Example: Lower perceived latency / manage loading time - */ -function lowerPerceivedLatency(app: App) { - //#region lowerPerceivedLatency - // TODO: Leverage partial inputs to show widgets as possible. - // Beware of partial JSON being partial (but healed), - // so some objects may not be complete. - //#endregion lowerPerceivedLatency -} - /** * Example: Supporting both iframe & MCP Apps in same binary */ @@ -147,6 +136,5 @@ void errorsToModel; void hostStylingVanillaJs; void hostStylingReact; void persistData; -void lowerPerceivedLatency; void iframeAndMcpApps; void migrateFromOpenai; diff --git a/src/app.examples.ts b/src/app.examples.ts index 4c09796..bf6f9dc 100644 --- a/src/app.examples.ts +++ b/src/app.examples.ts @@ -157,13 +157,34 @@ async function App_ontoolinput_setter(app: App) { */ function App_ontoolinputpartial_progressiveRendering(app: App) { //#region App_ontoolinputpartial_progressiveRendering + let toolInputs: Record | null = null; + let toolInputsPartial: Record | null = null; + app.ontoolinputpartial = (params) => { - console.log("Partial args:", params.arguments); - // Update your UI progressively as arguments stream in + toolInputsPartial = params.arguments as Record; + render(); }; + + app.ontoolinput = (params) => { + toolInputs = params.arguments as Record; + toolInputsPartial = null; + render(); + }; + + function render() { + if (toolInputs) { + renderFinalUI(toolInputs); + } else { + renderLoadingUI(toolInputsPartial); // e.g., shimmer with partial preview + } + } //#endregion App_ontoolinputpartial_progressiveRendering } +// Stubs for App_ontoolinputpartial_progressiveRendering example +declare function renderLoadingUI(data: Record | null): void; +declare function renderFinalUI(data: Record): void; + /** * Example: Display tool execution results using ontoolresult. */ diff --git a/src/app.ts b/src/app.ts index ab5fabd..0628029 100644 --- a/src/app.ts +++ b/src/app.ts @@ -287,6 +287,11 @@ export class App extends Protocol { * streams partial tool arguments during tool call initialization. This enables * progressive rendering of tool arguments before they're complete. * + * **Important:** Partial arguments are "healed" JSON — the host closes unclosed + * brackets/braces to produce valid JSON. This means objects may be incomplete + * (e.g., the last item in an array may be truncated). Use partial data only + * for preview UI, not for critical operations. + * * This setter is a convenience wrapper around `setNotificationHandler()` that * automatically handles the notification schema and extracts the params for you. *