Render the assistant's replies as Markdown (#118)
Build image / build-and-push (push) Successful in 7s
Build image / build-and-push (push) Successful in 7s
Render assistant chat output as GFM Markdown (tables, lists, code, headings), lazy-loaded so the ~150 KB renderer only ships when an assistant message shows. Hardened per review: no <img> (exfiltration-beacon guard), no raw HTML, error-boundary + stale-chunk recovery around the lazy chunk, GFM column alignment, and memoized parsing. Co-authored-by: Steve Dudenhoeffer <[email protected]>
This commit was merged in pull request #118.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { lazy, type ComponentType } from 'react'
|
||||
|
||||
/**
|
||||
* Lazily load a component by its named export, with recovery for the stale-chunk
|
||||
* problem. A push to main redeploys, so a still-open app references chunk hashes
|
||||
* the server has just replaced; that import 404s and React.lazy MEMOIZES the
|
||||
* rejection, so a "Try again" can never recover — the user is stuck until a manual
|
||||
* hard reload. On the first such failure we reload once (fetching the fresh index
|
||||
* + hashes); a session flag stops a reload loop, and a success clears it so a
|
||||
* later genuine failure can reload again.
|
||||
*
|
||||
* Shared by the route splits (router.tsx) and any feature-level lazy load (the
|
||||
* assistant's Markdown renderer), so they all get the same recovery.
|
||||
*/
|
||||
export function lazyPage<M, K extends keyof M>(load: () => Promise<M>, name: K) {
|
||||
// Preserve the component's own prop type so callers keep type-checked props
|
||||
// (e.g. MarkdownMessage's `children: string`), rather than erasing to `{}`.
|
||||
type C = M[K] extends ComponentType<infer P> ? ComponentType<P> : never
|
||||
return lazy<C>(async () => {
|
||||
try {
|
||||
const mod = await load()
|
||||
try {
|
||||
sessionStorage.removeItem('pansy:chunk-reload')
|
||||
} catch {
|
||||
/* storage unavailable — fine */
|
||||
}
|
||||
return { default: mod[name] as C }
|
||||
} catch (err) {
|
||||
try {
|
||||
if (!sessionStorage.getItem('pansy:chunk-reload')) {
|
||||
sessionStorage.setItem('pansy:chunk-reload', '1')
|
||||
window.location.reload()
|
||||
return await new Promise<{ default: C }>(() => {}) // hold for the reload
|
||||
}
|
||||
} catch {
|
||||
/* storage unavailable — fall through to surface the error */
|
||||
}
|
||||
throw err // already reloaded once (or can't); let the error boundary show it
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user