Skip to content

feat: add customizable input layout with slot props and reusable sub-components#88

Closed
amcdnl wants to merge 4 commits intomasterfrom
claude/customizable-input-layout-W7aVh
Closed

feat: add customizable input layout with slot props and reusable sub-components#88
amcdnl wants to merge 4 commits intomasterfrom
claude/customizable-input-layout-W7aVh

Conversation

@amcdnl
Copy link
Copy Markdown
Member

@amcdnl amcdnl commented Mar 11, 2026

Add prepend, append, and actions slot props to ChatInput for flexible layout
customization. Each slot accepts a ReactNode or a render function that receives
ChatInputRenderContext (isLoading, disabled, message, sendMessage, stopMessage).

Extract SendButton and StopButton as standalone exported components so users can
compose custom action layouts while reusing the default themed buttons.

Add new Storybook demos: model selector, toolbar, custom actions, and full
custom layout combining all slots.

https://claude.ai/code/session_01ATC3T4KBTV5pXpzDTL5Zz7

claude added 2 commits March 11, 2026 00:37
…components

Add prepend, append, and actions slot props to ChatInput for flexible layout
customization. Each slot accepts a ReactNode or a render function that receives
ChatInputRenderContext (isLoading, disabled, message, sendMessage, stopMessage).

Extract SendButton and StopButton as standalone exported components so users can
compose custom action layouts while reusing the default themed buttons.

Add new Storybook demos: model selector, toolbar, custom actions, and full
custom layout combining all slots.

https://claude.ai/code/session_01ATC3T4KBTV5pXpzDTL5Zz7
Add 'before' and 'after' options to actionsPlacement prop for placing
action buttons to the left or right of the editor on the same row.
This complements the existing 'inline', 'top', and 'bottom' placements.

Add corresponding theme keys (actions.before, actions.after) and
Storybook demos (ActionsAfter, ActionsBefore).

https://claude.ai/code/session_01ATC3T4KBTV5pXpzDTL5Zz7
Copilot AI review requested due to automatic review settings March 11, 2026 00:48
@reaviz reaviz deleted a comment from claude Bot Mar 11, 2026
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

This PR enhances ChatInput customization by adding slot-style props (prepend, append, actions) and flexible actionsPlacement options, while extracting reusable themed SendButton/StopButton components and adding Storybook demos for various layouts.

Changes:

  • Add prepend/append/actions slot props (ReactNode or render function via ChatInputRenderContext) and actionsPlacement to ChatInput.
  • Extract and export reusable SendButton and StopButton components.
  • Extend the theme shape to support prepend/append wrappers and non-inline action placements; add Storybook demos for custom layouts.

Reviewed changes

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

File Description
stories/CustomInputLayout.stories.tsx Adds new Storybook demos showcasing custom input layouts and action placements.
src/theme.ts Extends ChatTheme.input for prepend/append and action placement variants.
src/ChatInput/ChatInput.tsx Implements slot rendering, action placement logic, and exports SendButton/StopButton + render context types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread stories/CustomInputLayout.stories.tsx Outdated
@@ -0,0 +1,611 @@
import { Meta, StoryFn } from '@storybook/react';
import { useState, useCallback } from 'react';
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

useCallback is imported but never used in this story file. Please remove the unused import to keep lint/TS clean.

Suggested change
import { useState, useCallback } from 'react';
import { useState } from 'react';

Copilot uses AI. Check for mistakes.
Comment thread src/ChatInput/ChatInput.tsx Outdated
Comment on lines +87 to +90
return (
<Button
title="Send"
className={cn(theme.input.actions.send, className)}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

SendButton reads theme.input.actions.send directly from ChatContext, but ChatContext's default value has no theme. Since SendButton is now exported for reuse, using it outside a <Chat> provider will throw at runtime. Consider falling back to chatTheme when theme is undefined (as other components do), or require a theme prop and/or throw a clearer error when missing context.

Suggested change
return (
<Button
title="Send"
className={cn(theme.input.actions.send, className)}
const sendClassName = theme?.input?.actions?.send;
return (
<Button
title="Send"
className={cn(sendClassName, className)}

Copilot uses AI. Check for mistakes.
Comment thread src/ChatInput/ChatInput.tsx Outdated
Comment on lines +445 to +447
{resolvedPrepend && (
<div className={cn(theme.input.prepend)}>{resolvedPrepend}</div>
)}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

{resolvedPrepend && ...} will skip rendering valid ReactNode values like 0 or an empty string. Prefer an explicit null/undefined check (e.g. resolvedPrepend != null) so all valid slot content renders correctly.

Copilot uses AI. Check for mistakes.
Comment thread src/ChatInput/ChatInput.tsx Outdated
Comment on lines +463 to +465
{resolvedAppend && (
<div className={cn(theme.input.append)}>{resolvedAppend}</div>
)}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

{resolvedAppend && ...} has the same truthiness issue as prepend and will not render valid ReactNode values like 0/empty string. Use an explicit null/undefined check instead of a truthy check.

Copilot uses AI. Check for mistakes.
Comment thread src/theme.ts
Comment on lines 318 to 321
input: [
'w-full border rounded-3xl px-3 py-2 pr-16 text-gray-500 border-gray-200 hover:bg-blue-100 hover:border-blue-500 after:hidden after:mx-10! bg-white [&>textarea]:w-full [&>textarea]:flex-none',
'dark:border-gray-700/50 dark:text-gray-200 dark:bg-gray-950 dark:hover:bg-blue-950/40'
].join(' '),
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

theme.input.input includes a hard-coded pr-16 to reserve space for inline actions, but this class is applied for all actionsPlacement values. When actions are placed top/bottom/before/after, this leaves unnecessary right padding on the editor. Consider making the reserved padding conditional (only for actionsPlacement === 'inline') or splitting the theme into separate classes for inline vs non-inline layouts.

Copilot uses AI. Check for mistakes.
The Cloudflare build uses pnpm with frozen-lockfile by default. The
lockfile was out of sync (stale zod version range and removed devDeps).

https://claude.ai/code/session_01ATC3T4KBTV5pXpzDTL5Zz7
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Mar 18, 2026

Deploying reachat-storybook with  Cloudflare Pages  Cloudflare Pages

Latest commit: ed00156
Status: ✅  Deploy successful!
Preview URL: https://6c6d67da.reachat-storybook.pages.dev
Branch Preview URL: https://claude-customizable-input-la.reachat-storybook.pages.dev

View logs

- Remove unused useCallback import from stories
- Add theme fallback for SendButton/StopButton so they work safely
  outside a <Chat> provider (falls back to default theme)
- Fix truthy slot checks: use != null instead of && to correctly
  render falsy ReactNode values like 0 or empty strings
- Remove hardcoded pr-16 from input theme; apply it conditionally
  only when actionsPlacement is 'inline' to avoid wasted padding
  for top/bottom/before/after placements

https://claude.ai/code/session_01ATC3T4KBTV5pXpzDTL5Zz7
@amcdnl amcdnl closed this May 5, 2026
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.

3 participants