feat: send reserved feature for current session

This commit is contained in:
d-kimsuon
2025-10-27 08:48:24 +09:00
parent 586dbe7833
commit 9fbe4d78fe
10 changed files with 1238 additions and 83 deletions

View File

@@ -0,0 +1,97 @@
import { describe, expect, it } from "vitest";
import type { ChatInputProps } from "./ChatInput";
describe("ChatInput Props", () => {
it("should have correct type definition for enableScheduledSend", () => {
const props: ChatInputProps = {
projectId: "test-project",
onSubmit: async () => {},
isPending: false,
placeholder: "Type your message...",
buttonText: "Send",
enableScheduledSend: true,
baseSessionId: null,
};
expect(props.enableScheduledSend).toBe(true);
expect(props.baseSessionId).toBe(null);
});
it("should allow enableScheduledSend to be undefined", () => {
const props: ChatInputProps = {
projectId: "test-project",
onSubmit: async () => {},
isPending: false,
placeholder: "Type your message...",
buttonText: "Send",
};
expect(props.enableScheduledSend).toBeUndefined();
});
it("should allow baseSessionId to be a string", () => {
const props: ChatInputProps = {
projectId: "test-project",
onSubmit: async () => {},
isPending: false,
placeholder: "Type your message...",
buttonText: "Send",
baseSessionId: "session-123",
};
expect(props.baseSessionId).toBe("session-123");
});
it("should validate datetime format parsing logic", () => {
const scheduledTime = "2025-10-26T15:30";
const match = scheduledTime.match(
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/,
);
expect(match).not.toBeNull();
if (match) {
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
const hours = Number(match[4]);
const minutes = Number(match[5]);
expect(year).toBe(2025);
expect(month).toBe(10);
expect(day).toBe(26);
expect(hours).toBe(15);
expect(minutes).toBe(30);
const localDate = new Date(year, month - 1, day, hours, minutes);
expect(localDate.getFullYear()).toBe(2025);
expect(localDate.getMonth()).toBe(9); // 0-indexed
expect(localDate.getDate()).toBe(26);
expect(localDate.getHours()).toBe(15);
expect(localDate.getMinutes()).toBe(30);
}
});
it("should handle invalid datetime format", () => {
const invalidTime = "invalid-datetime";
const match = invalidTime.match(
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/,
);
expect(match).toBeNull();
});
it("should generate default scheduled time with correct format", () => {
const now = new Date();
now.setHours(now.getHours() + 1);
const formatted = now.toISOString().slice(0, 16);
// Verify format is correct (YYYY-MM-DDTHH:mm)
expect(formatted).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/);
// Verify the format can be parsed back
const parsed = new Date(formatted);
expect(parsed).toBeInstanceOf(Date);
expect(Number.isNaN(parsed.getTime())).toBe(false);
});
});

View File

@@ -8,8 +8,19 @@ import {
XIcon,
} from "lucide-react";
import { type FC, useCallback, useId, useRef, useState } from "react";
import { toast } from "sonner";
import { Button } from "../../../../../components/ui/button";
import { Input } from "../../../../../components/ui/input";
import { Label } from "../../../../../components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "../../../../../components/ui/select";
import { Textarea } from "../../../../../components/ui/textarea";
import { useCreateSchedulerJob } from "../../../../../hooks/useScheduler";
import { useConfig } from "../../../../hooks/useConfig";
import type { CommandCompletionRef } from "./CommandCompletion";
import type { FileCompletionRef } from "./FileCompletion";
@@ -33,6 +44,8 @@ export interface ChatInputProps {
containerClassName?: string;
disabled?: boolean;
buttonSize?: "sm" | "default" | "lg";
enableScheduledSend?: boolean;
baseSessionId?: string | null;
}
export const ChatInput: FC<ChatInputProps> = ({
@@ -46,6 +59,8 @@ export const ChatInput: FC<ChatInputProps> = ({
containerClassName = "",
disabled = false,
buttonSize = "lg",
enableScheduledSend = false,
baseSessionId = null,
}) => {
const { i18n } = useLingui();
const [message, setMessage] = useState("");
@@ -56,6 +71,19 @@ export const ChatInput: FC<ChatInputProps> = ({
relative: { top: number; left: number };
absolute: { top: number; left: number };
}>({ relative: { top: 0, left: 0 }, absolute: { top: 0, left: 0 } });
const [sendMode, setSendMode] = useState<"immediate" | "scheduled">(
"immediate",
);
const [scheduledTime, setScheduledTime] = useState(() => {
const now = new Date();
now.setHours(now.getHours() + 1);
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day}T${hours}:${minutes}`;
});
const containerRef = useRef<HTMLDivElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
@@ -64,6 +92,7 @@ export const ChatInput: FC<ChatInputProps> = ({
const fileCompletionRef = useRef<FileCompletionRef>(null);
const helpId = useId();
const { config } = useConfig();
const createSchedulerJob = useCreateSchedulerJob();
const handleSubmit = async () => {
if (!message.trim() && attachedFiles.length === 0) return;
@@ -88,14 +117,73 @@ export const ChatInput: FC<ChatInputProps> = ({
const finalText = message.trim() + additionalText;
await onSubmit({
text: finalText,
images: images.length > 0 ? images : undefined,
documents: documents.length > 0 ? documents : undefined,
});
if (enableScheduledSend && sendMode === "scheduled") {
// Create a scheduler job for scheduled send
const match = scheduledTime.match(
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/,
);
if (!match) {
throw new Error("Invalid datetime format");
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
const hours = Number(match[4]);
const minutes = Number(match[5]);
const localDate = new Date(year, month - 1, day, hours, minutes);
setMessage("");
setAttachedFiles([]);
try {
await createSchedulerJob.mutateAsync({
name: `Scheduled message at ${scheduledTime}`,
schedule: {
type: "reserved",
reservedExecutionTime: localDate.toISOString(),
},
message: {
content: finalText,
projectId,
baseSessionId,
},
enabled: true,
});
toast.success(
i18n._({
id: "chat.scheduled_send.success",
message: "Message scheduled successfully",
}),
{
description: i18n._({
id: "chat.scheduled_send.success_description",
message: "You can view and manage it in the Scheduler tab",
}),
},
);
setMessage("");
setAttachedFiles([]);
} catch (error) {
toast.error(
i18n._({
id: "chat.scheduled_send.failed",
message: "Failed to schedule message",
}),
{
description: error instanceof Error ? error.message : undefined,
},
);
}
} else {
// Immediate send
await onSubmit({
text: finalText,
images: images.length > 0 ? images : undefined,
documents: documents.length > 0 ? documents : undefined,
});
setMessage("");
setAttachedFiles([]);
}
};
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -338,33 +426,93 @@ export const ChatInput: FC<ChatInputProps> = ({
)}
</div>
<Button
onClick={handleSubmit}
disabled={
(!message.trim() && attachedFiles.length === 0) ||
isPending ||
disabled
}
size={buttonSize}
className="gap-2 transition-all duration-200 hover:shadow-md hover:scale-105 active:scale-95 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-500 hover:to-purple-500 disabled:from-muted disabled:to-muted"
>
{isPending ? (
<>
<LoaderIcon className="w-4 h-4 animate-spin" />
<span>
<Trans
id="chat.status.processing"
message="Processing..."
/>
</span>
</>
) : (
<>
<SendIcon className="w-4 h-4" />
{buttonText}
</>
<div className="flex items-center gap-2">
{enableScheduledSend && (
<div className="flex items-center gap-2">
<Label htmlFor="send-mode" className="text-xs sr-only">
<Trans id="chat.send_mode.label" message="Send mode" />
</Label>
<Select
value={sendMode}
onValueChange={(value: "immediate" | "scheduled") =>
setSendMode(value)
}
disabled={isPending || disabled}
>
<SelectTrigger
id="send-mode"
className="h-8 w-[140px] text-xs"
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="immediate">
<Trans
id="chat.send_mode.immediate"
message="Send now"
/>
</SelectItem>
<SelectItem value="scheduled">
<Trans
id="chat.send_mode.scheduled"
message="Schedule send"
/>
</SelectItem>
</SelectContent>
</Select>
{sendMode === "scheduled" && (
<div className="flex items-center gap-1.5">
<Label
htmlFor="scheduled-time"
className="text-xs sr-only"
>
<Trans
id="chat.send_mode.scheduled_time"
message="Scheduled time"
/>
</Label>
<Input
id="scheduled-time"
type="datetime-local"
value={scheduledTime}
onChange={(e) => setScheduledTime(e.target.value)}
disabled={isPending || disabled}
className="h-8 w-[180px] text-xs"
/>
</div>
)}
</div>
)}
</Button>
<Button
onClick={handleSubmit}
disabled={
(!message.trim() && attachedFiles.length === 0) ||
isPending ||
disabled
}
size={buttonSize}
className="gap-2 transition-all duration-200 hover:shadow-md hover:scale-105 active:scale-95 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-500 hover:to-purple-500 disabled:from-muted disabled:to-muted"
>
{isPending ? (
<>
<LoaderIcon className="w-4 h-4 animate-spin" />
<span>
<Trans
id="chat.status.processing"
message="Processing..."
/>
</span>
</>
) : (
<>
<SendIcon className="w-4 h-4" />
{buttonText}
</>
)}
</Button>
</div>
</div>
</div>
<InlineCompletion

View File

@@ -26,18 +26,24 @@ export const ContinueChat: FC<{
const getPlaceholder = () => {
const behavior = config?.enterKeyBehavior;
if (behavior === "enter-send") {
return i18n._(
"Type your message... (Start with / for commands, @ for files, Enter to send)",
);
return i18n._({
id: "chat.placeholder.continue.enter",
message:
"Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)",
});
}
if (behavior === "command-enter-send") {
return i18n._(
"Type your message... (Start with / for commands, @ for files, Command+Enter to send)",
);
return i18n._({
id: "chat.placeholder.continue.command_enter",
message:
"Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)",
});
}
return i18n._(
"Type your message... (Start with / for commands, @ for files, Shift+Enter to send)",
);
return i18n._({
id: "chat.placeholder.continue.shift_enter",
message:
"Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)",
});
};
const buttonText = <Trans id="chat.send" message="Send" />;
@@ -56,6 +62,8 @@ export const ContinueChat: FC<{
minHeight="min-h-[120px]"
containerClassName=""
buttonSize="lg"
enableScheduledSend={true}
baseSessionId={sessionId}
/>
</div>
</div>

View File

@@ -25,18 +25,24 @@ export const ResumeChat: FC<{
const getPlaceholder = () => {
const behavior = config?.enterKeyBehavior;
if (behavior === "enter-send") {
return i18n._(
"Type your message... (Start with / for commands, @ for files, Enter to send)",
);
return i18n._({
id: "chat.placeholder.resume.enter",
message:
"Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)",
});
}
if (behavior === "command-enter-send") {
return i18n._(
"Type your message... (Start with / for commands, @ for files, Command+Enter to send)",
);
return i18n._({
id: "chat.placeholder.resume.command_enter",
message:
"Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)",
});
}
return i18n._(
"Type your message... (Start with / for commands, @ for files, Shift+Enter to send)",
);
return i18n._({
id: "chat.placeholder.resume.shift_enter",
message:
"Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)",
});
};
const buttonText = <Trans id="chat.resume" message="Resume" />;
@@ -55,6 +61,8 @@ export const ResumeChat: FC<{
minHeight="min-h-[120px]"
containerClassName=""
buttonSize="lg"
enableScheduledSend={true}
baseSessionId={sessionId}
/>
</div>
</div>

View File

@@ -163,19 +163,19 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
{job ? (
<Trans
id="scheduler.dialog.title.edit"
message="スケジュールジョブを編集"
message="Edit Scheduled Job"
/>
) : (
<Trans
id="scheduler.dialog.title.create"
message="スケジュールジョブを作成"
message="Create Scheduled Job"
/>
)}
</DialogTitle>
<DialogDescription>
<Trans
id="scheduler.dialog.description"
message="Claude Code にメッセージを送信するスケジュールジョブを設定します"
message="Set up a scheduled job to send messages to Claude Code"
/>
</DialogDescription>
</DialogHeader>
@@ -185,12 +185,12 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<div className="flex items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<Label htmlFor="enabled" className="text-base font-semibold">
<Trans id="scheduler.form.enabled" message="有効化" />
<Trans id="scheduler.form.enabled" message="Enabled" />
</Label>
<p className="text-sm text-muted-foreground">
<Trans
id="scheduler.form.enabled.description"
message="このスケジュールジョブを有効または無効にします"
message="Enable or disable this scheduled job"
/>
</p>
</div>
@@ -205,7 +205,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
{/* Job Name */}
<div className="space-y-2">
<Label htmlFor="job-name">
<Trans id="scheduler.form.name" message="ジョブ名" />
<Trans id="scheduler.form.name" message="Job Name" />
</Label>
<Input
id="job-name"
@@ -213,7 +213,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
onChange={(e) => setName(e.target.value)}
placeholder={_({
id: "scheduler.form.name.placeholder",
message: "例: 日次レポート",
message: "e.g., Daily Report",
})}
disabled={isSubmitting}
/>
@@ -224,7 +224,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<Label>
<Trans
id="scheduler.form.schedule_type"
message="スケジュールタイプ"
message="Schedule Type"
/>
</Label>
<Select
@@ -241,13 +241,13 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<SelectItem value="cron">
<Trans
id="scheduler.form.schedule_type.cron"
message="定期実行 (Cron)"
message="Recurring (Cron)"
/>
</SelectItem>
<SelectItem value="reserved">
<Trans
id="scheduler.form.schedule_type.reserved"
message="予約実行"
message="One-time"
/>
</SelectItem>
</SelectContent>
@@ -265,7 +265,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<Label htmlFor="reserved-datetime">
<Trans
id="scheduler.form.reserved_time"
message="実行予定日時"
message="Scheduled Date and Time"
/>
</Label>
<Input
@@ -278,7 +278,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<p className="text-xs text-muted-foreground">
<Trans
id="scheduler.form.reserved_time.hint"
message="指定した日時に一度だけ実行されます。実行後は自動的に削除されます"
message="Will run once at the specified time, then be automatically deleted"
/>
</p>
</div>
@@ -287,7 +287,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
{/* Message Content */}
<div className="space-y-2">
<Label htmlFor="message-content">
<Trans id="scheduler.form.message" message="メッセージ内容" />
<Trans id="scheduler.form.message" message="Message Content" />
</Label>
<div className="relative" ref={completion.containerRef}>
<Textarea
@@ -301,14 +301,16 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
placeholder={i18n._({
id: "scheduler.form.message.placeholder",
message:
"Claude Code に送信するメッセージを入力... (/ でコマンド補完、@ でファイル補完)",
"Type message to send to Claude Code... (/ for commands, @ for files)",
})}
rows={4}
disabled={isSubmitting}
className="resize-none"
aria-label={i18n._(
"Message input with completion support (/ for commands, @ for files)",
)}
aria-label={i18n._({
id: "scheduler.form.message.aria_label",
message:
"Message input with completion support (/ for commands, @ for files)",
})}
aria-expanded={
messageContent.startsWith("/") || messageContent.includes("@")
}
@@ -333,7 +335,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<p className="text-xs text-muted-foreground">
<Trans
id="scheduler.form.message.hint"
message="/ でコマンド補完、@ でファイル補完"
message="/ for commands, @ for files"
/>
</p>
</div>
@@ -344,7 +346,7 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<Label>
<Trans
id="scheduler.form.concurrency_policy"
message="同時実行ポリシー"
message="Concurrency Policy"
/>
</Label>
<Select
@@ -361,13 +363,13 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
<SelectItem value="skip">
<Trans
id="scheduler.form.concurrency_policy.skip"
message="実行中の場合はスキップ"
message="Skip if running"
/>
</SelectItem>
<SelectItem value="run">
<Trans
id="scheduler.form.concurrency_policy.run"
message="実行中でも実行する"
message="Run even if running"
/>
</SelectItem>
</SelectContent>
@@ -382,18 +384,18 @@ export const SchedulerJobDialog: FC<SchedulerJobDialogProps> = ({
onClick={() => onOpenChange(false)}
disabled={isSubmitting}
>
<Trans id="common.cancel" message="キャンセル" />
<Trans id="common.cancel" message="Cancel" />
</Button>
<Button
onClick={handleSubmit}
disabled={!isFormValid || isSubmitting}
>
{isSubmitting ? (
<Trans id="common.saving" message="保存中..." />
<Trans id="common.saving" message="Saving..." />
) : job ? (
<Trans id="common.update" message="更新" />
<Trans id="common.update" message="Update" />
) : (
<Trans id="common.create" message="作成" />
<Trans id="common.create" message="Create" />
)}
</Button>
</DialogFooter>

View File

@@ -40,7 +40,9 @@ export const SessionSidebar: FC<{
{
id: "sessions",
icon: MessageSquareIcon,
title: "Show session list",
title: (
<Trans id="sidebar.show.session.list" message="Show session list" />
),
content: (
<Suspense fallback={<Loading />}>
<SessionsTab
@@ -53,13 +55,23 @@ export const SessionSidebar: FC<{
{
id: "mcp",
icon: PlugIcon,
title: "Show MCP server settings",
title: (
<Trans
id="sidebar.show.mcp.settings"
message="Show MCP server settings"
/>
),
content: <McpTab projectId={projectId} />,
},
{
id: "scheduler",
icon: CalendarClockIcon,
title: "Show scheduler jobs",
title: (
<Trans
id="sidebar.show.scheduler.jobs"
message="Show scheduler jobs"
/>
),
content: (
<SchedulerTab projectId={projectId} sessionId={currentSessionId} />
),

View File

@@ -1266,11 +1266,155 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
42
49
]
],
"translation": "Send"
},
"chat.send_mode.label": {
"message": "Send mode",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 433]
],
"translation": "Send mode"
},
"chat.send_mode.immediate": {
"message": "Send now",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 452]
],
"translation": "Send now"
},
"chat.send_mode.scheduled": {
"message": "Schedule send",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 458]
],
"translation": "Schedule send"
},
"chat.send_mode.scheduled_time": {
"message": "Scheduled time",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 472]
],
"translation": "Scheduled time"
},
"chat.attach_file": {
"message": "Attach",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 408]
],
"translation": "Attach"
},
"chat.placeholder.continue.enter": {
"message": "Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
32
]
],
"translation": "Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)"
},
"chat.placeholder.continue.command_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
39
]
],
"translation": "Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)"
},
"chat.placeholder.continue.shift_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
45
]
],
"translation": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)"
},
"chat.placeholder.resume.enter": {
"message": "Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ResumeChat.tsx",
31
]
],
"translation": "Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)"
},
"chat.placeholder.resume.command_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ResumeChat.tsx",
38
]
],
"translation": "Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)"
},
"chat.placeholder.resume.shift_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ResumeChat.tsx",
44
]
],
"translation": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)"
},
"chat.scheduled_send.success": {
"message": "Message scheduled successfully",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 152]
],
"translation": "Message scheduled successfully"
},
"chat.scheduled_send.success_description": {
"message": "You can view and manage it in the Scheduler tab",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 157]
],
"translation": "You can view and manage it in the Scheduler tab"
},
"chat.scheduled_send.failed": {
"message": "Failed to schedule message",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 168]
],
"translation": "Failed to schedule message"
},
"settings.section.session_display": {
"message": "Session Display",
"placeholders": {},
@@ -1337,6 +1481,10 @@
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
258
],
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/SessionSidebar.tsx",
56
]
],
"translation": "Show MCP server settings"
@@ -1356,10 +1504,26 @@
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
231
],
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/SessionSidebar.tsx",
43
]
],
"translation": "Show session list"
},
"sidebar.show.scheduler.jobs": {
"message": "Show scheduler jobs",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/SessionSidebar.tsx",
62
]
],
"translation": "Show scheduler jobs"
},
"system.info.tab.title": {
"message": "Show system information",
"placeholders": {},
@@ -1777,5 +1941,281 @@
["src/routes/projects/$projectId/sessions/$sessionId/index.tsx", 17]
],
"translation": "The session you are looking for does not exist."
},
"scheduler.dialog.title.edit": {
"message": "Edit Scheduled Job",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
166
]
],
"translation": "Edit Scheduled Job"
},
"scheduler.dialog.title.create": {
"message": "Create Scheduled Job",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
171
]
],
"translation": "Create Scheduled Job"
},
"scheduler.dialog.description": {
"message": "Set up a scheduled job to send messages to Claude Code",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
178
]
],
"translation": "Set up a scheduled job to send messages to Claude Code"
},
"scheduler.form.enabled": {
"message": "Enabled",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
188
]
],
"translation": "Enabled"
},
"scheduler.form.enabled.description": {
"message": "Enable or disable this scheduled job",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
193
]
],
"translation": "Enable or disable this scheduled job"
},
"scheduler.form.name": {
"message": "Job Name",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
208
]
],
"translation": "Job Name"
},
"scheduler.form.name.placeholder": {
"message": "e.g., Daily Report",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
216
]
],
"translation": "e.g., Daily Report"
},
"scheduler.form.schedule_type": {
"message": "Schedule Type",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
227
]
],
"translation": "Schedule Type"
},
"scheduler.form.schedule_type.cron": {
"message": "Recurring (Cron)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
244
]
],
"translation": "Recurring (Cron)"
},
"scheduler.form.schedule_type.reserved": {
"message": "One-time",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
250
]
],
"translation": "One-time"
},
"scheduler.form.reserved_time": {
"message": "Scheduled Date and Time",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
268
]
],
"translation": "Scheduled Date and Time"
},
"scheduler.form.reserved_time.hint": {
"message": "Will run once at the specified time, then be automatically deleted",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
281
]
],
"translation": "Will run once at the specified time, then be automatically deleted"
},
"scheduler.form.message": {
"message": "Message Content",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
290
]
],
"translation": "Message Content"
},
"scheduler.form.message.placeholder": {
"message": "Type message to send to Claude Code... (/ for commands, @ for files)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
304
]
],
"translation": "Type message to send to Claude Code... (/ for commands, @ for files)"
},
"scheduler.form.message.aria_label": {
"message": "Message input with completion support (/ for commands, @ for files)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
311
]
],
"translation": "Message input with completion support (/ for commands, @ for files)"
},
"scheduler.form.message.hint": {
"message": "/ for commands, @ for files",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
337
]
],
"translation": "/ for commands, @ for files"
},
"scheduler.form.concurrency_policy": {
"message": "Concurrency Policy",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
348
]
],
"translation": "Concurrency Policy"
},
"scheduler.form.concurrency_policy.skip": {
"message": "Skip if running",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
365
]
],
"translation": "Skip if running"
},
"scheduler.form.concurrency_policy.run": {
"message": "Run even if running",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
371
]
],
"translation": "Run even if running"
},
"common.cancel": {
"message": "Cancel",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
386
]
],
"translation": "Cancel"
},
"common.saving": {
"message": "Saving...",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
393
]
],
"translation": "Saving..."
},
"common.update": {
"message": "Update",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
395
]
],
"translation": "Update"
},
"common.create": {
"message": "Create",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
397
]
],
"translation": "Create"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1266,11 +1266,155 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
42
49
]
],
"translation": "送信"
},
"chat.send_mode.label": {
"message": "Send mode",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 433]
],
"translation": "送信モード"
},
"chat.send_mode.immediate": {
"message": "Send now",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 452]
],
"translation": "今すぐ送信"
},
"chat.send_mode.scheduled": {
"message": "Schedule send",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 458]
],
"translation": "予約送信"
},
"chat.send_mode.scheduled_time": {
"message": "Scheduled time",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 472]
],
"translation": "送信予定時刻"
},
"chat.attach_file": {
"message": "Attach",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 408]
],
"translation": "添付"
},
"chat.placeholder.continue.enter": {
"message": "Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
32
]
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Enterで送信、または予約送信"
},
"chat.placeholder.continue.command_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
39
]
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Command+Enterで送信、または予約送信"
},
"chat.placeholder.continue.shift_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ContinueChat.tsx",
45
]
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Shift+Enterで送信、または予約送信"
},
"chat.placeholder.resume.enter": {
"message": "Type your message... (Start with / for commands, @ for files, Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ResumeChat.tsx",
31
]
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Enterで送信、または予約送信"
},
"chat.placeholder.resume.command_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Command+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ResumeChat.tsx",
38
]
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Command+Enterで送信、または予約送信"
},
"chat.placeholder.resume.shift_enter": {
"message": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send, or schedule for later)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/resumeChat/ResumeChat.tsx",
44
]
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Shift+Enterで送信、または予約送信"
},
"chat.scheduled_send.success": {
"message": "Message scheduled successfully",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 152]
],
"translation": "メッセージを予約しました"
},
"chat.scheduled_send.success_description": {
"message": "You can view and manage it in the Scheduler tab",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 157]
],
"translation": "スケジューラタブで確認・管理できます"
},
"chat.scheduled_send.failed": {
"message": "Failed to schedule message",
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 168]
],
"translation": "メッセージの予約に失敗しました"
},
"settings.section.session_display": {
"message": "Session Display",
"placeholders": {},
@@ -1337,6 +1481,10 @@
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
258
],
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/SessionSidebar.tsx",
56
]
],
"translation": "MCPサーバー設定を表示"
@@ -1356,10 +1504,26 @@
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
231
],
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/SessionSidebar.tsx",
43
]
],
"translation": "セッション一覧を表示"
},
"sidebar.show.scheduler.jobs": {
"message": "Show scheduler jobs",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/SessionSidebar.tsx",
62
]
],
"translation": "スケジューラジョブを表示"
},
"system.info.tab.title": {
"message": "Show system information",
"placeholders": {},
@@ -1777,5 +1941,281 @@
["src/routes/projects/$projectId/sessions/$sessionId/index.tsx", 17]
],
"translation": "お探しのセッションは存在しません。"
},
"scheduler.dialog.title.edit": {
"message": "Edit Scheduled Job",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
166
]
],
"translation": "スケジュールジョブを編集"
},
"scheduler.dialog.title.create": {
"message": "Create Scheduled Job",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
171
]
],
"translation": "スケジュールジョブを作成"
},
"scheduler.dialog.description": {
"message": "Set up a scheduled job to send messages to Claude Code",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
178
]
],
"translation": "Claude Codeにメッセージを送信するスケジュールジョブを設定します"
},
"scheduler.form.enabled": {
"message": "Enabled",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
188
]
],
"translation": "有効化"
},
"scheduler.form.enabled.description": {
"message": "Enable or disable this scheduled job",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
193
]
],
"translation": "このスケジュールジョブを有効または無効にします"
},
"scheduler.form.name": {
"message": "Job Name",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
208
]
],
"translation": "ジョブ名"
},
"scheduler.form.name.placeholder": {
"message": "e.g., Daily Report",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
216
]
],
"translation": "例: 日次レポート"
},
"scheduler.form.schedule_type": {
"message": "Schedule Type",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
227
]
],
"translation": "スケジュールタイプ"
},
"scheduler.form.schedule_type.cron": {
"message": "Recurring (Cron)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
244
]
],
"translation": "定期実行 (Cron)"
},
"scheduler.form.schedule_type.reserved": {
"message": "One-time",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
250
]
],
"translation": "予約実行"
},
"scheduler.form.reserved_time": {
"message": "Scheduled Date and Time",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
268
]
],
"translation": "実行予定日時"
},
"scheduler.form.reserved_time.hint": {
"message": "Will run once at the specified time, then be automatically deleted",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
281
]
],
"translation": "指定した日時に一度だけ実行されます。実行後は自動的に削除されます"
},
"scheduler.form.message": {
"message": "Message Content",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
290
]
],
"translation": "メッセージ内容"
},
"scheduler.form.message.placeholder": {
"message": "Type message to send to Claude Code... (/ for commands, @ for files)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
304
]
],
"translation": "Claude Codeに送信するメッセージを入力... (/でコマンド補完、@でファイル補完)"
},
"scheduler.form.message.aria_label": {
"message": "Message input with completion support (/ for commands, @ for files)",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
311
]
],
"translation": "補完機能付きメッセージ入力 (/でコマンド、@でファイル)"
},
"scheduler.form.message.hint": {
"message": "/ for commands, @ for files",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
337
]
],
"translation": "/でコマンド補完、@でファイル補完"
},
"scheduler.form.concurrency_policy": {
"message": "Concurrency Policy",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
348
]
],
"translation": "同時実行ポリシー"
},
"scheduler.form.concurrency_policy.skip": {
"message": "Skip if running",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
365
]
],
"translation": "実行中の場合はスキップ"
},
"scheduler.form.concurrency_policy.run": {
"message": "Run even if running",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
371
]
],
"translation": "実行中でも実行する"
},
"common.cancel": {
"message": "Cancel",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
386
]
],
"translation": "キャンセル"
},
"common.saving": {
"message": "Saving...",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
393
]
],
"translation": "保存中..."
},
"common.update": {
"message": "Update",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
395
]
],
"translation": "更新"
},
"common.create": {
"message": "Create",
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/scheduler/SchedulerJobDialog.tsx",
397
]
],
"translation": "作成"
}
}

File diff suppressed because one or more lines are too long