Skip to content

feat(F0AiChatTextArea): inline clarifying questions#3611

Closed
daviz-fct wants to merge 2 commits intomainfrom
f0AiChatTextArea-question-status
Closed

feat(F0AiChatTextArea): inline clarifying questions#3611
daviz-fct wants to merge 2 commits intomainfrom
f0AiChatTextArea-question-status

Conversation

@daviz-fct
Copy link
Copy Markdown
Contributor

@daviz-fct daviz-fct commented Mar 9, 2026

Description

Adds support for inline clarifying questions in F0AiChatTextArea. When the AI returns a clarifying question, it is displayed inline inside the textarea area with confirm/cancel actions, without leaving the chat flow.

The idea is to replace the QuestionCard we made for the sales skill and have something that can be reused everywhere.

Screenshots (if applicable)

Grabacion.de.pantalla.2026-03-09.a.las.15.42.29.mov

Implementation details

What changed

  • F0AiChatTextArea: Added support for a questionStatus prop (or equivalent) that renders a clarifying question inline within the component, with confirm and dismiss actions.
  • fix: On confirming a clarifying question, the textarea input value is correctly cleared.

Why

The AI chat flow requires a way to present clarifying questions to the user without interrupting or navigating away from the current context. Rendering them inline inside F0AiChatTextArea keeps the interaction contained and consistent with the existing chat UX.

Adds a multi-step clarifying question panel that expands inside the
chat textarea. The AI can trigger it via the CopilotKit action
'AiWidgets.F0ClarifyingQuestion' to gather structured input from the
user before executing a task.

- ClarifyingQuestion/ClarifyingOption types in F0AiChatTextArea/types.ts
- Animated panel (Framer Motion) with skeleton → checkboxes crossfade
- Back button (step 2+), Next/Submit primary, enable logic (checkbox or text)
- Placeholder changes to 'Type your answer…' while question is active
- useClarifyingQuestionAction: CopilotKit action + ClarifyingQuestionController
  (ref-stabilised callbacks to avoid infinite render loops)
- State wired through AiChatStateProvider → ChatTextarea → F0AiChatTextArea
- WithClarifyingQuestions Storybook story for local validation
Copilot AI review requested due to automatic review settings March 9, 2026 15:52
@daviz-fct daviz-fct requested a review from a team as a code owner March 9, 2026 15:52
@github-actions github-actions bot added feat react Changes affect packages/react labels Mar 9, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

✅ No New Circular Dependencies

No new circular dependencies detected. Current count: 0

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

📦 Alpha Package Version Published

Use pnpm i github:factorialco/f0#npm/alpha-pr-3611 to install the package

Use pnpm i github:factorialco/f0#9ca10bd79d12ed4dc5fc0ded1f7b6336c925a8dd to install this specific commit

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

🔍 Visual review for your branch is published 🔍

Here are the links to:

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an inline “clarifying question” flow to the AI chat input (F0AiChatTextArea), allowing the backend to prompt multi-step checkbox questions directly inside the textarea UI.

Changes:

  • Introduced ClarifyingQuestion / ClarifyingOption types and a new clarifyingQuestion prop on F0AiChatTextArea.
  • Rendered an inline expanding panel (with skeleton/loading state) above the textarea, including checkbox options and navigation controls.
  • Added a CopilotKit action (AiWidgets.F0ClarifyingQuestion) plus provider plumbing to manage/display clarifying questions from the AI chat state.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/react/src/sds/ai/F0AiChatTextArea/types.ts Adds public types for clarifying question data/callbacks and wires the new prop into F0AiChatTextAreaProps.
packages/react/src/sds/ai/F0AiChatTextArea/stories/F0AiChatTextArea.stories.tsx Adds a Storybook scenario demonstrating a multi-step clarifying question flow.
packages/react/src/sds/ai/F0AiChatTextArea/F0AiChatTextArea.tsx Implements the inline clarifying question panel, confirm/back controls, and submit behavior updates (including input clearing).
packages/react/src/sds/ai/F0AiChat/providers/AiChatStateProvider.tsx Adds clarifyingQuestion state + setter to the AI chat context.
packages/react/src/sds/ai/F0AiChat/internal-types.ts Extends the provider return type to expose clarifyingQuestion and setClarifyingQuestion.
packages/react/src/sds/ai/F0AiChat/copilotActions/useDefaultCopilotActions.ts Registers the new clarifying question CopilotKit action by default.
packages/react/src/sds/ai/F0AiChat/copilotActions/useClarifyingQuestionAction.tsx New CopilotKit action + controller to drive multi-step clarifying question state and submit aggregated selections as a user message.
packages/react/src/sds/ai/F0AiChat/components/ChatTextarea.tsx Passes clarifyingQuestion from context into F0AiChatTextArea.

Comment on lines +185 to +194
{/* Clarifying question panel — expands inside the form above the textarea */}
<AnimatePresence>
{clarifyingQuestion && (
<motion.div
key="clarifying-question"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.25, ease: "easeOut" }}
className="overflow-hidden"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new clarifying-question panel introduces multiple animations (AnimatePresence + motion height/opacity), but the component doesn’t respect prefers-reduced-motion. packages/react/AGENTS.md recommends using useReducedMotion() for animated components and setting durations to 0 when true.

Copilot uses AI. Check for mistakes.
Comment on lines +389 to +396
<ButtonInternal
type="button"
variant="outline"
label="Back"
icon={ArrowLeft}
hideLabel
onClick={clarifyingQuestion.onBack}
/>
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Back button label is hardcoded ("Back"). Since this is user-facing UI copy, it should come from useI18n() (or be provided via the clarifyingQuestion prop) to keep the component fully localizable.

Copilot uses AI. Check for mistakes.
parts.push(labels.join(", "))
}
})
sendMessage(parts.join(" | "))
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleConfirm only serializes selected checkbox labels into sendMessage(...). If the user answers via free text (which the UI currently allows), that text is ignored and sendMessage may be called with an empty string when no options are selected. Align the behavior with the action description by including the textarea text in the confirm payload (and consider sending option IDs + question context rather than only labels to make backend parsing reliable).

Suggested change
sendMessage(parts.join(" | "))
const payload = parts.join(" | ").trim()
if (payload.length === 0) {
return
}
sendMessage(payload)

Copilot uses AI. Check for mistakes.
Comment on lines +206 to +210
setLoadingNext(true)
setTimeout(() => {
setStepIndex((prev) => prev + 1)
setLoadingNext(false)
}, 600)
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setTimeout used to advance to the next step isn’t cleaned up on unmount. If the action is removed/unmounted before the timeout fires, this can call setStepIndex/setLoadingNext on an unmounted component (React warning + potential leak). Store the timeout id in a ref and clear it in an effect cleanup/unmount handler.

Copilot uses AI. Check for mistakes.
Comment on lines +217 to +225
setClarifyingQuestion({
question: currentStep.question,
options: currentStep.options,
selectedOptionIds: selections[stepIndex] ?? [],
onToggleOption: handleToggle,
onConfirm: handleConfirm,
onBack: handleBack,
confirmLabel: isLastStep ? "Submit" : "Next",
loading: false,
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

confirmLabel is set to hardcoded strings ("Submit" / "Next"). Since these surface in the UI, they should be localized (via useI18n()) or passed in from the backend so the action works correctly across locales.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +29
onToggleOption: (id: string) => void
/** Called when the user confirms and moves forward */
onConfirm: () => void
/** Optional: called when the user wants to go back to a previous step */
onBack?: () => void
/** Step label, e.g. "1 / 3" */
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions confirm/cancel (dismiss) actions for the inline clarifying question, but the proposed ClarifyingQuestion API only supports onConfirm and optional onBack. If users need to dismiss the clarifying flow, consider adding an explicit onDismiss/onCancel callback (and a corresponding UI control) so the interaction can be exited deterministically.

Copilot uses AI. Check for mistakes.
mentions.close()
if (inProgress) {
onStop?.()
} else if (clarifyingQuestion && !clarifyingQuestion.loading) {
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When clarifyingQuestion is active, form submit always calls clarifyingQuestion.onConfirm() and clears inputValue, so any free-text the user typed is never sent/used. Since the confirm button is enabled when hasDataToSend is true, this can drop user input and even confirm with an empty payload. Consider extending ClarifyingQuestion.onConfirm to accept the current text (and/or exposing a controlled text value in clarifyingQuestion), or route free-text submits through onSend when no options are selected.

Suggested change
} else if (clarifyingQuestion && !clarifyingQuestion.loading) {
} else if (clarifyingQuestion && !clarifyingQuestion.loading && !hasDataToSend) {

Copilot uses AI. Check for mistakes.
}, [clarifyingQuestion?.question])

const resolvedDefaultPlaceholder = clarifyingQuestion
? "Type your answer…"
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolvedDefaultPlaceholder uses a hardcoded user-facing string ("Type your answer…") when a clarifying question is present. Per packages/react/AGENTS.md i18n guidelines, user-facing strings should come from useI18n() so they can be translated consistently.

Suggested change
? "Type your answer…"
? translation.ai.inputPlaceholder

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 9, 2026

Coverage Report for packages/react

Status Category Percentage Covered / Total
🔵 Lines 42.92% 8902 / 20740
🔵 Statements 42.24% 9144 / 21646
🔵 Functions 34.4% 1976 / 5744
🔵 Branches 33.25% 5405 / 16253
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/sds/ai/F0AiChat/internal-types.ts 100% 100% 100% 100%
packages/react/src/sds/ai/F0AiChat/components/ChatTextarea.tsx 10% 100% 0% 10% 34-65
packages/react/src/sds/ai/F0AiChat/copilotActions/useClarifyingQuestionAction.tsx 1.44% 0% 0% 1.63% 50-232
packages/react/src/sds/ai/F0AiChat/copilotActions/useDefaultCopilotActions.ts 10% 100% 0% 10% 25-33
packages/react/src/sds/ai/F0AiChat/providers/AiChatStateProvider.tsx 7.04% 0% 0% 7.35% 33-45, 66-232, 239-283
packages/react/src/sds/ai/F0AiChatTextArea/F0AiChatTextArea.tsx 1.81% 0% 0% 1.85% 33-337
packages/react/src/sds/ai/F0AiChatTextArea/types.ts 100% 100% 100% 100%
Generated in workflow #11545 for commit decd775 by the Vitest Coverage Report Action

* { "question": "Which time period?", "options": [...] }
* ]
* }}
*/
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a big fan of commenting code, TBH is something extra to maintain and also it can consecuent be outdated with every change.

Plus, the code is enough clear at first look, could we remove these?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes ! No problem :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat react Changes affect packages/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants