Skip to content

feat: add FlowTool, the tool-call card, and its message part - #44

Open
divyanshub024 wants to merge 2 commits into
mainfrom
dv/flutter-tool-widget-plan-5bad8e
Open

feat: add FlowTool, the tool-call card, and its message part#44
divyanshub024 wants to merge 2 commits into
mainfrom
dv/flutter-tool-widget-plan-5bad8e

Conversation

@divyanshub024

@divyanshub024 divyanshub024 commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

Adds FlowTool, roadmap item 17: the tool-call card.

  • One 16px mark carries the status, never words: a still muted asterisk while pending, the thinking indicator's turning asterisk beside a shimmering title while running, a check in success once complete, error_outline in error on failure. Nothing new to localize for status.
  • The header row holds the host's title (or the tool name in the code face), the primary argument as a chip, and a chevron at the trailing edge when there is a body. Input and output render as nested FlowCodeBlocks (json and plain by default, wrapping) under host-localized section labels, behind a widget-owned disclosure (initiallyExpanded + onExpandedChanged, the ExpansionTile contract). errorMessage renders under the header, outside the disclosure, as a live region.
  • FlowToolPart joins the sealed part model with FlowToolStatus { pending, running, complete, error }. FlowMessage renders it and keys the card by the provider's call id; FlowThread forwards toolInputLabel and toolOutputLabel. Copy reuses the existing onCodeCopy / copiedCodePart contract through synthesized FlowCodeParts, the markdown-fence precedent.
  • FlowToolStyle joins the component styles with a FlowTheme.toolStyle default. The four accents recolor the mark, never the words.
  • The chrome is the code block's flat card (surfaceContainerLowest, outline firming to outlineVariant on hover), not the confirmation's raised one: a tool call is a record of the runtime's work, the confirmation is the host's gate.
  • Docs page, playground stage (Live run / Complete / Running / Error / In a thread), README row, _redirects line, roadmap rows and changelog entry included.

Reviewer notes:

  • No Figma frame for this card yet. Metrics derive from the code block and confirmation specs (12 radius, 36 header, 16 mark, nested blocks at radius 8) and are flagged as provisional in the changelog and docs.
  • The disclosure state lives in the route's PageStorage, keyed through a PageStorageKey on the part's wrapper, because FlowThread remounts its subtree when a conversation first outgrows the viewport, which opening a card in a short thread triggers. It is stored under a private identifier built from the key chain rather than the framework's computed one: the blocks' SelectableText restores its scroll offset from the same chain and casts it to double?, which a bool there crashes. Found in the playground and fixed before this PR.
  • The body animates with ClipRect + Align(heightFactor:) on one controller shared with the chevron, since AnimatedSize drops the content on the first frame of a collapse.
  • Approval is not a tool state: a call that needs one is a FlowConfirmationPart beside the tool part.
  • Title and chip are both Flexible, so under pressure each gets at most half the row; a priority layout is a possible follow-up. A host-rendered output slot and grouping consecutive calls are out of scope.

Screenshots

New component, so no before column.

Complete, light Complete, dark Running Error In a thread

How this was verified

  • Playground tool stage in Chrome (web-server build): all five variants, light and dark. Expanding a card inside the 420px thread stays open across the viewport flip, collapses without a bounce, and the two cards keep independent state. Copy shows the check on the right block, the running mark turns, and the phone stage ellipsizes the title and chip.
  • Headless screenshots of the embeds (?embed=tool&variant=...&theme=...).
  • flutter analyze clean at the root and in playground/; example/ reports only its pre-existing missing env.g.dart. dart format . applied.
  • Not exercised: reduced motion and keyboard toggling. Both use the same guards as the thinking indicator and the code block.

Checklist

  • flutter analyze lib and flutter analyze in example/ and playground/ are clean
  • dart format . applied
  • Exercised in the playground — with a stage demo added or updated if this is a new component or variant
  • No new entries under dependencies: in pubspec.yaml (Flutter SDK and flutter.dev packages only)
  • Nothing model-facing — no prompts, schemas, or provider/network calls
  • New public API is exported from lib/flow_ui.dart and documented in docs/ and the README table
  • CHANGELOG.md updated for user-facing changes, with breaking changes called out
  • PR title follows conventional commits (feat:, fix:, refactor:, docs:, chore:)

Note

Medium Risk
Adds a new sealed FlowMessagePart subtype, so host exhaustive switches must handle FlowToolPart; thread/message rendering and PageStorage disclosure logic are non-trivial but presentation-only.

Overview
Ships FlowTool, the tool-call card for assistant turns: a morphing status mark (pending → running → complete/error), host title and detail chip, collapsible input/output as nested FlowCodeBlocks, and errors visible outside the disclosure.

Adds FlowToolPart / FlowToolStatus to the sealed message-part model. FlowMessage and FlowThread render tool parts (thread-level toolInputLabel / toolOutputLabel, shared onCodeCopy), and cards keyed by call id keep disclosure state in PageStorage across thread remounts. FlowToolStyle and FlowTheme.toolStyle round out theming; public exports, docs, playground variants, roadmap/changelog/README updates included.

Reviewed by Cursor Bugbot for commit 94c026c. Bugbot is set up for automated code reviews on this repo. Configure here.

…t-plan-5bad8e

# Conflicts:
#	CHANGELOG.md
#	README.md
#	docs/public/_redirects
#	docs/src/content/docs/theming.mdx
#	lib/flow_ui.dart
#	lib/src/theme/flow_theme.dart
#	playground/lib/src/demo_registry.dart
#	playground/lib/src/playground_item.dart

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 94c026c. Configure here.

.merge(style?.errorMessageStyle),
),
),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Error text skips selection block

Medium Severity

errorMessage renders as a bare Text with no FlowSelectionBlock. Sibling cards wrap that same host-written paragraph so a thread copy keeps a newline; without the block, Flutter concatenates the failure with the prose or code on either side.

Fix in Cursor Fix in Web

Triggered by learned rule: FlowThread selection and copy contracts

Reviewed by Cursor Bugbot for commit 94c026c. Configure here.

color: colors.onSurfaceMuted,
),
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Header chrome joins thread selection

Medium Severity

The header is documented as chrome, and the title and chip opt out of selection, but the settled status Icons and the chevron do not. Icon paints with RichText, so a drag through the card copies those glyphs into the clipboard.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: FlowThread selection and copy contracts

Reviewed by Cursor Bugbot for commit 94c026c. Configure here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant