Skip to content

Commit 14c09bc

Browse files
committed
🤖 refactor: Convert ChatInput to Tailwind CSS
- Remove @emotion/styled dependency from ChatInput.tsx - Replace 8 styled components with Tailwind utility classes - InputSection → relative pt-[5px] px-[15px] pb-[15px] with containerType inline-size - InputControls → flex gap-2.5 items-end - ModeToggles/ModeTogglesRow → flex flex-col gap-1, flex items-center - ModeToggleWrapper → flex items-center gap-1.5 ml-auto with @[700px]:hidden container query - StyledToggleContainer → Dynamic bg/text classes with cn() for exec/plan modes - EditingIndicator → text-[11px] text-edit-mode font-medium - ModelDisplayWrapper → flex items-center gap-1 mr-3 h-[11px] - Use @[700px]: arbitrary container queries for responsive behavior - Apply conditional styling for mode-specific button states (first/last-of-type) - Maintain all interactive features (command suggestions, vim mode, image attachments) - Code reduction: 907 → 813 lines (10% reduction) All type checks passing, maintains feature parity
1 parent 7948162 commit 14c09bc

File tree

1 file changed

+22
-116
lines changed

1 file changed

+22
-116
lines changed

‎src/components/ChatInput.tsx‎

Lines changed: 22 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState, useRef, useCallback, useEffect, useId } from "react";
2-
import styled from "@emotion/styled";
2+
import { cn } from "@/lib/utils";
33
import { CommandSuggestions, COMMAND_SUGGESTION_KEYS } from "./CommandSuggestions";
44
import type { Toast } from "./ChatInputToast";
55
import { ChatInputToast } from "./ChatInputToast";
@@ -41,105 +41,6 @@ import type { CmuxFrontendMetadata } from "@/types/message";
4141
import { useTelemetry } from "@/hooks/useTelemetry";
4242
import { setTelemetryEnabled } from "@/telemetry";
4343

44-
const InputSection = styled.div`
45-
position: relative;
46-
padding: 5px 15px 15px 15px; /* Reduced top padding from 15px to 5px */
47-
background: #252526;
48-
border-top: 1px solid #3e3e42;
49-
display: flex;
50-
flex-direction: column;
51-
gap: 8px;
52-
container-type: inline-size; /* Enable container queries for responsive behavior */
53-
`;
54-
55-
const InputControls = styled.div`
56-
display: flex;
57-
gap: 10px;
58-
align-items: flex-end;
59-
`;
60-
61-
// Input now rendered by VimTextArea; styles moved there
62-
63-
const ModeToggles = styled.div`
64-
display: flex;
65-
flex-direction: column;
66-
gap: 4px;
67-
`;
68-
69-
const ModeTogglesRow = styled.div`
70-
display: flex;
71-
align-items: center;
72-
`;
73-
74-
const ModeToggleWrapper = styled.div`
75-
display: flex;
76-
align-items: center;
77-
gap: 6px;
78-
margin-left: auto;
79-
80-
/* Hide mode toggle on narrow containers */
81-
/* Note: Text area border changes color with mode, so this omission is acceptable */
82-
@container (max-width: 700px) {
83-
display: none;
84-
}
85-
`;
86-
87-
const StyledToggleContainer = styled.div<{ mode: UIMode }>`
88-
display: flex;
89-
gap: 0;
90-
background: var(--color-toggle-bg);
91-
border-radius: 4px;
92-
93-
button {
94-
&:first-of-type {
95-
${(props) =>
96-
props.mode === "exec" &&
97-
`
98-
background: var(--color-exec-mode);
99-
color: white;
100-
101-
&:hover {
102-
background: var(--color-exec-mode-hover);
103-
}
104-
`}
105-
}
106-
107-
&:last-of-type {
108-
${(props) =>
109-
props.mode === "plan" &&
110-
`
111-
background: var(--color-plan-mode);
112-
color: white;
113-
114-
&:hover {
115-
background: var(--color-plan-mode-hover);
116-
}
117-
`}
118-
}
119-
}
120-
`;
121-
122-
const EditingIndicator = styled.div`
123-
font-size: 11px;
124-
color: var(--color-editing-mode);
125-
font-weight: 500;
126-
`;
127-
128-
const ModelDisplayWrapper = styled.div`
129-
display: flex;
130-
align-items: center;
131-
gap: 4px;
132-
margin-right: 12px;
133-
height: 11px;
134-
135-
/* Hide help indicators on narrow containers */
136-
@container (max-width: 700px) {
137-
.help-indicator-wrapper {
138-
display: none;
139-
}
140-
}
141-
`;
142-
14344
export interface ChatInputAPI {
14445
focus: () => void;
14546
restoreText: (text: string) => void;
@@ -802,7 +703,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
802703
})();
803704

804705
return (
805-
<InputSection data-component="ChatInputSection">
706+
<div className="relative pt-[5px] px-[15px] pb-[15px] bg-[#252526] border-t border-[#3e3e42] flex flex-col gap-2" style={{ containerType: "inline-size" }} data-component="ChatInputSection">
806707
<ChatInputToast toast={toast} onDismiss={handleToastDismiss} />
807708
<CommandSuggestions
808709
suggestions={commandSuggestions}
@@ -812,7 +713,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
812713
ariaLabel="Slash command suggestions"
813714
listId={commandListId}
814715
/>
815-
<InputControls data-component="ChatInputControls">
716+
<div className="flex gap-2.5 items-end" data-component="ChatInputControls">
816717
<VimTextArea
817718
ref={inputRef}
818719
value={input}
@@ -833,17 +734,17 @@ export const ChatInput: React.FC<ChatInputProps> = ({
833734
}
834735
aria-expanded={showCommandSuggestions && commandSuggestions.length > 0}
835736
/>
836-
</InputControls>
737+
</div>
837738
<ImageAttachments images={imageAttachments} onRemove={handleRemoveImage} />
838-
<ModeToggles data-component="ChatModeToggles">
739+
<div className="flex flex-col gap-1" data-component="ChatModeToggles">
839740
{editingMessage && (
840-
<EditingIndicator>
741+
<div className="text-[11px] text-edit-mode font-medium">
841742
Editing message ({formatKeybind(KEYBINDS.CANCEL_EDIT)} to cancel)
842-
</EditingIndicator>
743+
</div>
843744
)}
844-
<ModeTogglesRow>
745+
<div className="flex items-center">
845746
<ChatToggles modelString={preferredModel}>
846-
<ModelDisplayWrapper>
747+
<div className="flex items-center gap-1 mr-3 h-[11px] @[700px]:[&_.help-indicator-wrapper]:hidden">
847748
<ModelSelector
848749
ref={modelSelectorRef}
849750
value={preferredModel}
@@ -872,10 +773,15 @@ export const ChatInput: React.FC<ChatInputProps> = ({
872773
</Tooltip>
873774
</TooltipWrapper>
874775
</span>
875-
</ModelDisplayWrapper>
776+
</div>
876777
</ChatToggles>
877-
<ModeToggleWrapper>
878-
<StyledToggleContainer mode={mode}>
778+
<div className="flex items-center gap-1.5 ml-auto @[700px]:hidden">
779+
<div className={cn(
780+
"flex gap-0 bg-toggle-bg rounded",
781+
"[&>button:first-of-type]:rounded-l [&>button:last-of-type]:rounded-r",
782+
mode === "exec" && "[&>button:first-of-type]:bg-exec-mode [&>button:first-of-type]:text-white [&>button:first-of-type]:hover:bg-exec-mode-hover",
783+
mode === "plan" && "[&>button:last-of-type]:bg-plan-mode [&>button:last-of-type]:text-white [&>button:last-of-type]:hover:bg-plan-mode-hover"
784+
)}>
879785
<ToggleGroup<UIMode>
880786
options={[
881787
{ value: "exec", label: "Exec" },
@@ -884,7 +790,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
884790
value={mode}
885791
onChange={setMode}
886792
/>
887-
</StyledToggleContainer>
793+
</div>
888794
<span className="help-indicator-wrapper">
889795
<TooltipWrapper inline>
890796
<HelpIndicator>?</HelpIndicator>
@@ -899,9 +805,9 @@ export const ChatInput: React.FC<ChatInputProps> = ({
899805
</Tooltip>
900806
</TooltipWrapper>
901807
</span>
902-
</ModeToggleWrapper>
903-
</ModeTogglesRow>
904-
</ModeToggles>
905-
</InputSection>
808+
</div>
809+
</div>
810+
</div>
811+
</div>
906812
);
907813
};

0 commit comments

Comments
 (0)