Skip to content

Commit 2295414

Browse files
committed
🤖 Use shared CompactOptions and ForkOptions types
- Rename internal execution types to avoid confusion - ForkOptions/CompactOptions are now user-facing types - ForkExecutionOptions/CompactExecutionOptions for internal use - Modals use options type directly as state - Eliminates intermediate object construction Generated with `cmux`
1 parent 6e30f1b commit 2295414

File tree

4 files changed

+137
-57
lines changed

4 files changed

+137
-57
lines changed

src/components/CompactModal.tsx

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,36 @@ import {
1010
CommandDisplay,
1111
CommandLabel,
1212
} from "./Modal";
13-
import { formatCompactCommand } from "@/utils/chatCommands";
13+
import { formatCompactCommand, type CompactOptions } from "@/utils/chatCommands";
1414

1515
interface CompactModalProps {
1616
isOpen: boolean;
1717
onClose: () => void;
18-
onCompact: (maxOutputTokens?: number, model?: string) => Promise<void>;
18+
onCompact: (options: CompactOptions) => Promise<void>;
1919
}
2020

2121
const CompactModal: React.FC<CompactModalProps> = ({ isOpen, onClose, onCompact }) => {
22-
const [maxOutputTokens, setMaxOutputTokens] = useState<string>("");
23-
const [model, setModel] = useState<string>("");
22+
const [options, setOptions] = useState<CompactOptions>({});
23+
const [maxOutputTokensInput, setMaxOutputTokensInput] = useState<string>("");
2424
const [isLoading, setIsLoading] = useState<boolean>(false);
2525
const infoId = useId();
2626

27+
// Sync options with input fields
28+
useEffect(() => {
29+
setOptions({
30+
maxOutputTokens: maxOutputTokensInput.trim()
31+
? parseInt(maxOutputTokensInput.trim(), 10)
32+
: undefined,
33+
model: options.model?.trim() || undefined,
34+
continueMessage: options.continueMessage?.trim() || undefined,
35+
});
36+
}, [maxOutputTokensInput]);
37+
2738
// Reset form when modal opens
2839
useEffect(() => {
2940
if (isOpen) {
30-
setMaxOutputTokens("");
31-
setModel("");
41+
setOptions({});
42+
setMaxOutputTokensInput("");
3243
setIsLoading(false);
3344
}
3445
}, [isOpen]);
@@ -45,12 +56,9 @@ const CompactModal: React.FC<CompactModalProps> = ({ isOpen, onClose, onCompact
4556
setIsLoading(true);
4657

4758
try {
48-
const tokens = maxOutputTokens.trim() ? parseInt(maxOutputTokens.trim(), 10) : undefined;
49-
const modelParam = model.trim() || undefined;
50-
51-
await onCompact(tokens, modelParam);
52-
setMaxOutputTokens("");
53-
setModel("");
59+
await onCompact(options);
60+
setOptions({});
61+
setMaxOutputTokensInput("");
5462
onClose();
5563
} catch (err) {
5664
console.error("Compact failed:", err);
@@ -60,9 +68,6 @@ const CompactModal: React.FC<CompactModalProps> = ({ isOpen, onClose, onCompact
6068
}
6169
};
6270

63-
const tokensValue = maxOutputTokens.trim() ? parseInt(maxOutputTokens.trim(), 10) : undefined;
64-
const modelValue = model.trim() || undefined;
65-
6671
return (
6772
<Modal
6873
isOpen={isOpen}
@@ -78,8 +83,8 @@ const CompactModal: React.FC<CompactModalProps> = ({ isOpen, onClose, onCompact
7883
<input
7984
id="maxOutputTokens"
8085
type="number"
81-
value={maxOutputTokens}
82-
onChange={(event) => setMaxOutputTokens(event.target.value)}
86+
value={maxOutputTokensInput}
87+
onChange={(event) => setMaxOutputTokensInput(event.target.value)}
8388
disabled={isLoading}
8489
placeholder="e.g., 3000"
8590
min="100"
@@ -94,14 +99,33 @@ const CompactModal: React.FC<CompactModalProps> = ({ isOpen, onClose, onCompact
9499
<input
95100
id="model"
96101
type="text"
97-
value={model}
98-
onChange={(event) => setModel(event.target.value)}
102+
value={options.model ?? ""}
103+
onChange={(event) =>
104+
setOptions({ ...options, model: event.target.value || undefined })
105+
}
99106
disabled={isLoading}
100107
placeholder="e.g., claude-3-5-sonnet-20241022"
101108
/>
102109
<HelpText>Specify a model for compaction. Leave empty to use current model.</HelpText>
103110
</FormGroup>
104111

112+
<FormGroup>
113+
<label htmlFor="continueMessage">Continue Message (optional):</label>
114+
<input
115+
id="continueMessage"
116+
type="text"
117+
value={options.continueMessage ?? ""}
118+
onChange={(event) =>
119+
setOptions({ ...options, continueMessage: event.target.value || undefined })
120+
}
121+
disabled={isLoading}
122+
placeholder="Message to send after compaction completes"
123+
/>
124+
<HelpText>
125+
If provided, this message will be sent automatically after compaction finishes.
126+
</HelpText>
127+
</FormGroup>
128+
105129
<ModalInfo id={infoId}>
106130
<p>
107131
Compaction will summarize your conversation history, allowing you to continue with a
@@ -112,7 +136,7 @@ const CompactModal: React.FC<CompactModalProps> = ({ isOpen, onClose, onCompact
112136

113137
<div>
114138
<CommandLabel>Equivalent command:</CommandLabel>
115-
<CommandDisplay>{formatCompactCommand(tokensValue, modelValue)}</CommandDisplay>
139+
<CommandDisplay>{formatCompactCommand(options)}</CommandDisplay>
116140
</div>
117141

118142
<ModalActions>

src/components/ForkWorkspaceModal.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import {
1010
CommandDisplay,
1111
CommandLabel,
1212
} from "./Modal";
13-
import { formatForkCommand } from "@/utils/chatCommands";
13+
import { formatForkCommand, type ForkOptions } from "@/utils/chatCommands";
1414

1515
interface ForkWorkspaceModalProps {
1616
isOpen: boolean;
1717
sourceWorkspaceName: string;
1818
onClose: () => void;
19-
onFork: (newName: string) => Promise<void>;
19+
onFork: (options: ForkOptions) => Promise<void>;
2020
}
2121

2222
const ForkWorkspaceModal: React.FC<ForkWorkspaceModalProps> = ({
@@ -25,15 +25,15 @@ const ForkWorkspaceModal: React.FC<ForkWorkspaceModalProps> = ({
2525
onClose,
2626
onFork,
2727
}) => {
28-
const [newName, setNewName] = useState<string>("");
28+
const [options, setOptions] = useState<ForkOptions>({ newName: "" });
2929
const [error, setError] = useState<string | null>(null);
3030
const [isLoading, setIsLoading] = useState<boolean>(false);
3131
const infoId = useId();
3232

3333
// Reset form when modal opens
3434
useEffect(() => {
3535
if (isOpen) {
36-
setNewName("");
36+
setOptions({ newName: "" });
3737
setError(null);
3838
setIsLoading(false);
3939
}
@@ -48,7 +48,7 @@ const ForkWorkspaceModal: React.FC<ForkWorkspaceModalProps> = ({
4848
const handleSubmit = async (event: React.FormEvent) => {
4949
event.preventDefault();
5050

51-
const trimmedName = newName.trim();
51+
const trimmedName = options.newName.trim();
5252
if (!trimmedName) {
5353
setError("Workspace name cannot be empty");
5454
return;
@@ -58,8 +58,8 @@ const ForkWorkspaceModal: React.FC<ForkWorkspaceModalProps> = ({
5858
setError(null);
5959

6060
try {
61-
await onFork(trimmedName);
62-
setNewName("");
61+
await onFork({ ...options, newName: trimmedName });
62+
setOptions({ newName: "" });
6363
onClose();
6464
} catch (err) {
6565
const message = err instanceof Error ? err.message : "Failed to fork workspace";
@@ -84,8 +84,8 @@ const ForkWorkspaceModal: React.FC<ForkWorkspaceModalProps> = ({
8484
<input
8585
id="newName"
8686
type="text"
87-
value={newName}
88-
onChange={(event) => setNewName(event.target.value)}
87+
value={options.newName}
88+
onChange={(event) => setOptions({ ...options, newName: event.target.value })}
8989
disabled={isLoading}
9090
placeholder="Enter new workspace name"
9191
required
@@ -102,18 +102,20 @@ const ForkWorkspaceModal: React.FC<ForkWorkspaceModalProps> = ({
102102
</p>
103103
</ModalInfo>
104104

105-
{newName.trim() && (
105+
{options.newName.trim() && (
106106
<div>
107107
<CommandLabel>Equivalent command:</CommandLabel>
108-
<CommandDisplay>{formatForkCommand(newName.trim())}</CommandDisplay>
108+
<CommandDisplay>
109+
{formatForkCommand({ ...options, newName: options.newName.trim() })}
110+
</CommandDisplay>
109111
</div>
110112
)}
111113

112114
<ModalActions>
113115
<CancelButton type="button" onClick={handleCancel} disabled={isLoading}>
114116
Cancel
115117
</CancelButton>
116-
<PrimaryButton type="submit" disabled={isLoading || newName.trim().length === 0}>
118+
<PrimaryButton type="submit" disabled={isLoading || options.newName.trim().length === 0}>
117119
{isLoading ? "Forking..." : "Fork Workspace"}
118120
</PrimaryButton>
119121
</ModalActions>

src/components/ProjectSidebar.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ import { WorkspaceListItem, type WorkspaceSelection } from "./WorkspaceListItem"
1919
import { RenameProvider } from "@/contexts/WorkspaceRenameContext";
2020
import ForkWorkspaceModal from "./ForkWorkspaceModal";
2121
import CompactModal from "./CompactModal";
22-
import { forkWorkspace, executeCompaction } from "@/utils/chatCommands";
22+
import {
23+
forkWorkspace,
24+
executeCompaction,
25+
type ForkOptions,
26+
type CompactOptions,
27+
} from "@/utils/chatCommands";
2328
import { useSendMessageOptions } from "@/hooks/useSendMessageOptions";
2429

2530
// Re-export WorkspaceSelection for backwards compatibility
@@ -679,14 +684,15 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
679684
setCompactModalState({ workspaceId });
680685
};
681686

682-
const handleForkSubmit = async (newName: string) => {
687+
const handleForkSubmit = async (options: ForkOptions) => {
683688
if (!forkModalState) {
684689
return;
685690
}
686691

687692
const result = await forkWorkspace({
688693
sourceWorkspaceId: forkModalState.workspaceId,
689-
newName,
694+
newName: options.newName,
695+
startMessage: options.startMessage,
690696
sendMessageOptions,
691697
});
692698

@@ -695,15 +701,16 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
695701
}
696702
};
697703

698-
const handleCompactSubmit = async (maxOutputTokens?: number, model?: string) => {
704+
const handleCompactSubmit = async (options: CompactOptions) => {
699705
if (!compactModalState) {
700706
return;
701707
}
702708

703709
const result = await executeCompaction({
704710
workspaceId: compactModalState.workspaceId,
705-
maxOutputTokens,
706-
model,
711+
maxOutputTokens: options.maxOutputTokens,
712+
model: options.model,
713+
continueMessage: options.continueMessage,
707714
sendMessageOptions,
708715
});
709716

0 commit comments

Comments
 (0)