@@ -86,9 +86,11 @@ export function useChat(options?: UseChatOptions) {
8686
8787 // Stable refs for options that shouldn't trigger re-renders
8888 const stableStorageKey = useRef ( options ?. storageKey ?? 'chat' ) . current ;
89- const stableSystemPrompt = useRef (
89+ const defaultSystemPrompt = useRef (
9090 buildSystemPrompt ( { customPrompt : options ?. systemPrompt } ) ,
9191 ) . current ;
92+ // Active system prompt — can be overridden by a flow's customPrompt
93+ const systemPromptRef = useRef ( defaultSystemPrompt ) ;
9294 const stableUserSuffix = useRef (
9395 options ?. userSuffix !== undefined ? options . userSuffix : DEFAULT_USER_SUFFIX ,
9496 ) . current ;
@@ -229,7 +231,7 @@ export function useChat(options?: UseChatOptions) {
229231
230232 // Build conversation history for the LLM
231233 const history : LlmMessage [ ] = [
232- { role : 'system' , content : stableSystemPrompt } ,
234+ { role : 'system' , content : systemPromptRef . current } ,
233235 ] ;
234236
235237 for ( const m of [ ...messages , userMsg ] ) {
@@ -295,8 +297,10 @@ export function useChat(options?: UseChatOptions) {
295297 setInput ( '' ) ;
296298 clearSavedHistory ( stableStorageKey ) ;
297299 msgIdRef . current = 0 ;
300+ flowRef . current = null ;
301+ systemPromptRef . current = defaultSystemPrompt ;
298302 inputRef . current ?. focus ( ) ;
299- } , [ ] ) ;
303+ } , [ defaultSystemPrompt ] ) ;
300304
301305 /** Update an assistant message's content and re-parse it. */
302306 const updateMessage = useCallback (
@@ -309,6 +313,74 @@ export function useChat(options?: UseChatOptions) {
309313 [ reparseLastAssistant ] ,
310314 ) ;
311315
316+ // Active flow state for multi-step example flows
317+ const flowRef = useRef < { steps : { userMessage : string ; markdown : string } [ ] ; currentStep : number } | null > ( null ) ;
318+
319+ /** Inject a single user+assistant message pair and parse the markdown. */
320+ const injectStep = useCallback (
321+ async ( userMessage : string , markdown : string ) => {
322+ const userMsg : ChatMsg = {
323+ id : ++ msgIdRef . current ,
324+ role : 'user' ,
325+ content : userMessage ,
326+ ast : null ,
327+ store : null ,
328+ } ;
329+ const assistantMsg : ChatMsg = {
330+ id : ++ msgIdRef . current ,
331+ role : 'assistant' ,
332+ content : markdown ,
333+ ast : null ,
334+ store : null ,
335+ } ;
336+ setMessages ( ( prev ) => [ ...prev , userMsg , assistantMsg ] ) ;
337+
338+ const asstId = assistantMsg . id ;
339+ try {
340+ const { ast, store } = await parseMarkdownRef . current ( markdown ) ;
341+ setMessages ( ( prev ) =>
342+ prev . map ( ( m ) => ( m . id === asstId ? { ...m , ast, store } : m ) ) ,
343+ ) ;
344+ } catch {
345+ // parse error — content is still shown as raw text
346+ }
347+ } ,
348+ [ ] ,
349+ ) ;
350+
351+ /** Start a multi-step example flow. Loads the first step immediately. */
352+ const startFlow = useCallback (
353+ async ( steps : { userMessage : string ; markdown : string } [ ] , customPrompt ?: string ) => {
354+ if ( steps . length === 0 ) return ;
355+ // Override system prompt if a flow-specific custom prompt is provided
356+ systemPromptRef . current = customPrompt
357+ ? buildSystemPrompt ( { customPrompt } )
358+ : defaultSystemPrompt ;
359+ flowRef . current = { steps, currentStep : 0 } ;
360+ await injectStep ( steps [ 0 ] . userMessage , steps [ 0 ] . markdown ) ;
361+ flowRef . current ! . currentStep = 1 ;
362+ } ,
363+ [ injectStep , defaultSystemPrompt ] ,
364+ ) ;
365+
366+ /** Advance the active flow to the next step (if any). */
367+ const advanceFlow = useCallback ( async ( ) => {
368+ const flow = flowRef . current ;
369+ if ( ! flow || flow . currentStep >= flow . steps . length ) return ;
370+ const step = flow . steps [ flow . currentStep ] ;
371+ flow . currentStep ++ ;
372+ await injectStep ( step . userMessage , step . markdown ) ;
373+ } , [ injectStep ] ) ;
374+
375+ /** Inject a pre-built markdown document as a user+assistant message pair. */
376+ const injectDocument = useCallback (
377+ async ( label : string , markdown : string ) => {
378+ flowRef . current = null ; // clear any active flow
379+ await injectStep ( `Show me: ${ label } ` , markdown ) ;
380+ } ,
381+ [ injectStep ] ,
382+ ) ;
383+
312384 return {
313385 config,
314386 messages,
@@ -323,5 +395,8 @@ export function useChat(options?: UseChatOptions) {
323395 stop,
324396 clear,
325397 updateMessage,
398+ injectDocument,
399+ startFlow,
400+ advanceFlow,
326401 } ;
327402}
0 commit comments