Merge pull request #7 from amay077/feature/issue_2

送信を Shift+Enter または Enter から選択できるようにしました
This commit is contained in:
きむそん
2025-09-15 21:45:06 +09:00
committed by GitHub
5 changed files with 88 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ import { AlertCircleIcon, LoaderIcon, SendIcon } from "lucide-react";
import { type FC, useCallback, useId, useRef, useState } from "react";
import { Button } from "../../../../../components/ui/button";
import { Textarea } from "../../../../../components/ui/textarea";
import { useConfig } from "../../../../hooks/useConfig";
import type { CommandCompletionRef } from "./CommandCompletion";
import type { FileCompletionRef } from "./FileCompletion";
import { InlineCompletion } from "./InlineCompletion";
@@ -42,6 +43,7 @@ export const ChatInput: FC<ChatInputProps> = ({
const commandCompletionRef = useRef<CommandCompletionRef>(null);
const fileCompletionRef = useRef<FileCompletionRef>(null);
const helpId = useId();
const { config } = useConfig();
const handleSubmit = async () => {
if (!message.trim()) return;
@@ -58,9 +60,19 @@ export const ChatInput: FC<ChatInputProps> = ({
return;
}
if (e.key === "Enter" && e.shiftKey) {
e.preventDefault();
handleSubmit();
// IMEで変換中の場合は送信しない
if (e.key === "Enter" && !e.nativeEvent.isComposing) {
const isEnterSend = config?.enterKeyBehavior === "enter-send";
if (isEnterSend && !e.shiftKey) {
// Enter: Send mode
e.preventDefault();
handleSubmit();
} else if (!isEnterSend && e.shiftKey) {
// Shift+Enter: Send mode (default)
e.preventDefault();
handleSubmit();
}
}
};

View File

@@ -1,4 +1,5 @@
import type { FC } from "react";
import { useConfig } from "../../../../hooks/useConfig";
import { ChatInput, useNewChatMutation } from "../chatForm";
export const NewChat: FC<{
@@ -6,18 +7,27 @@ export const NewChat: FC<{
onSuccess?: () => void;
}> = ({ projectId, onSuccess }) => {
const startNewChat = useNewChatMutation(projectId, onSuccess);
const { config } = useConfig();
const handleSubmit = async (message: string) => {
await startNewChat.mutateAsync({ message });
};
const getPlaceholder = () => {
const isEnterSend = config?.enterKeyBehavior === "enter-send";
if (isEnterSend) {
return "Type your message here... (Start with / for commands, @ for files, Enter to send)";
}
return "Type your message here... (Start with / for commands, @ for files, Shift+Enter to send)";
};
return (
<ChatInput
projectId={projectId}
onSubmit={handleSubmit}
isPending={startNewChat.isPending}
error={startNewChat.error}
placeholder="Type your message here... (Start with / for commands, @ for files, Shift+Enter to send)"
placeholder={getPlaceholder()}
buttonText="Start Chat"
minHeight="min-h-[200px]"
containerClassName="space-y-4"

View File

@@ -1,4 +1,5 @@
import type { FC } from "react";
import { useConfig } from "../../../../../../hooks/useConfig";
import {
ChatInput,
useResumeChatMutation,
@@ -11,6 +12,7 @@ export const ResumeChat: FC<{
isRunningTask: boolean;
}> = ({ projectId, sessionId, isPausedTask, isRunningTask }) => {
const resumeChat = useResumeChatMutation(projectId, sessionId);
const { config } = useConfig();
const handleSubmit = async (message: string) => {
await resumeChat.mutateAsync({ message });
@@ -23,6 +25,14 @@ export const ResumeChat: FC<{
return "Resume";
};
const getPlaceholder = () => {
const isEnterSend = config?.enterKeyBehavior === "enter-send";
if (isEnterSend) {
return "Type your message... (Start with / for commands, Enter to send)";
}
return "Type your message... (Start with / for commands, Shift+Enter to send)";
};
return (
<div className="border-t border-border/50 bg-muted/20 p-4 mt-6">
<ChatInput
@@ -30,7 +40,7 @@ export const ResumeChat: FC<{
onSubmit={handleSubmit}
isPending={resumeChat.isPending}
error={resumeChat.error}
placeholder="Type your message... (Start with / for commands, Shift+Enter to send)"
placeholder={getPlaceholder()}
buttonText={getButtonText()}
minHeight="min-h-[100px]"
containerClassName="space-y-2"

View File

@@ -4,6 +4,13 @@ import { useQueryClient } from "@tanstack/react-query";
import { type FC, useCallback, useId } from "react";
import { configQueryConfig, useConfig } from "@/app/hooks/useConfig";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { projectQueryConfig } from "../app/projects/[projectId]/hooks/useProject";
interface SettingsControlsProps {
@@ -20,6 +27,7 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
className = "",
}: SettingsControlsProps) => {
const checkboxId = useId();
const enterKeyBehaviorId = useId();
const { config, updateConfig } = useConfig();
const queryClient = useQueryClient();
@@ -53,6 +61,15 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
await onConfigChanged();
};
const handleEnterKeyBehaviorChange = async (value: string) => {
const newConfig = {
...config,
enterKeyBehavior: value as "shift-enter-send" | "enter-send",
};
updateConfig(newConfig);
await onConfigChanged();
};
return (
<div className={`space-y-4 ${className}`}>
<div className="flex items-center space-x-2">
@@ -97,6 +114,36 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
title
</p>
)}
<div className="space-y-2">
{showLabels && (
<label
htmlFor={enterKeyBehaviorId}
className="text-sm font-medium leading-none"
>
Enter Key Behavior
</label>
)}
<Select
value={config?.enterKeyBehavior || "shift-enter-send"}
onValueChange={handleEnterKeyBehaviorChange}
>
<SelectTrigger id={enterKeyBehaviorId} className="w-full">
<SelectValue placeholder="Select enter key behavior" />
</SelectTrigger>
<SelectContent>
<SelectItem value="shift-enter-send">
Shift+Enter to send (default)
</SelectItem>
<SelectItem value="enter-send">Enter to send</SelectItem>
</SelectContent>
</Select>
{showDescriptions && (
<p className="text-xs text-muted-foreground mt-1">
Choose how the Enter key behaves in message input
</p>
)}
</div>
</div>
);
};

View File

@@ -3,6 +3,10 @@ import z from "zod";
export const configSchema = z.object({
hideNoUserMessageSession: z.boolean().optional().default(true),
unifySameTitleSession: z.boolean().optional().default(true),
enterKeyBehavior: z
.enum(["shift-enter-send", "enter-send"])
.optional()
.default("shift-enter-send"),
});
export type Config = z.infer<typeof configSchema>;