mirror of
https://github.com/aljazceru/claude-code-viewer.git
synced 2025-12-20 06:44:19 +01:00
feat: improve continue chat experience
This commit is contained in:
@@ -120,7 +120,7 @@ export const NewChat: FC<{
|
|||||||
{startNewChat.isPending ? (
|
{startNewChat.isPending ? (
|
||||||
<>
|
<>
|
||||||
<LoaderIcon className="w-4 h-4 animate-spin" />
|
<LoaderIcon className="w-4 h-4 animate-spin" />
|
||||||
Starting... This may take a while.
|
Sending... This may take a while.
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ export const SessionPageContent: FC<{
|
|||||||
|
|
||||||
// 自動スクロール処理
|
// 自動スクロール処理
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isRunningTask && conversations.length !== previousConversationLength) {
|
if (
|
||||||
|
(isRunningTask || isPausedTask) &&
|
||||||
|
conversations.length !== previousConversationLength
|
||||||
|
) {
|
||||||
setPreviousConversationLength(conversations.length);
|
setPreviousConversationLength(conversations.length);
|
||||||
const scrollContainer = scrollContainerRef.current;
|
const scrollContainer = scrollContainerRef.current;
|
||||||
if (scrollContainer) {
|
if (scrollContainer) {
|
||||||
@@ -55,7 +58,7 @@ export const SessionPageContent: FC<{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [conversations, isRunningTask, previousConversationLength]);
|
}, [conversations, isRunningTask, isPausedTask, previousConversationLength]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen">
|
<div className="flex h-screen">
|
||||||
@@ -134,7 +137,7 @@ export const SessionPageContent: FC<{
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="w-full px-20 pb-20 relative z-5">
|
<main className="w-full px-20 pb-10 relative z-5">
|
||||||
<ConversationList
|
<ConversationList
|
||||||
conversations={conversations}
|
conversations={conversations}
|
||||||
getToolResult={getToolResult}
|
getToolResult={getToolResult}
|
||||||
|
|||||||
@@ -36,12 +36,13 @@ export const ResumeChat: FC<{
|
|||||||
return response.json();
|
return response.json();
|
||||||
},
|
},
|
||||||
onSuccess: async (response) => {
|
onSuccess: async (response) => {
|
||||||
setMessage("");
|
|
||||||
if (sessionId !== response.sessionId) {
|
if (sessionId !== response.sessionId) {
|
||||||
router.push(
|
router.push(
|
||||||
`/projects/${projectId}/sessions/${response.sessionId}#message-${response.userMessageId}`,
|
`/projects/${projectId}/sessions/${response.sessionId}#message-${response.userMessageId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMessage("");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ export const ResumeChat: FC<{
|
|||||||
{resumeChat.isPending ? (
|
{resumeChat.isPending ? (
|
||||||
<>
|
<>
|
||||||
<LoaderIcon className="w-4 h-4 animate-spin" />
|
<LoaderIcon className="w-4 h-4 animate-spin" />
|
||||||
Starting... This may take a while.
|
Sending... This may take a while.
|
||||||
</>
|
</>
|
||||||
) : isPausedTask || isRunningTask ? (
|
) : isPausedTask || isRunningTask ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { query } from "@anthropic-ai/claude-code";
|
import { query } from "@anthropic-ai/claude-code";
|
||||||
import { ulid } from "ulid";
|
|
||||||
import prexit from "prexit";
|
import prexit from "prexit";
|
||||||
|
import { ulid } from "ulid";
|
||||||
import { type EventBus, getEventBus } from "../events/EventBus";
|
import { type EventBus, getEventBus } from "../events/EventBus";
|
||||||
import { createMessageGenerator } from "./createMessageGenerator";
|
import { createMessageGenerator } from "./createMessageGenerator";
|
||||||
import type {
|
import type {
|
||||||
@@ -48,14 +48,15 @@ export class ClaudeCodeTaskController {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (existingTask) {
|
if (existingTask) {
|
||||||
return this.continueTask(existingTask, message);
|
return await this.continueTask(existingTask, message);
|
||||||
} else {
|
} else {
|
||||||
return await this.startTask(currentSession, message);
|
return await this.startTask(currentSession, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private continueTask(task: AliveClaudeCodeTask, message: string) {
|
private async continueTask(task: AliveClaudeCodeTask, message: string) {
|
||||||
task.setNextMessage(message);
|
task.setNextMessage(message);
|
||||||
|
await task.awaitFirstMessage();
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +68,13 @@ export class ClaudeCodeTaskController {
|
|||||||
},
|
},
|
||||||
message: string,
|
message: string,
|
||||||
) {
|
) {
|
||||||
const { generateMessages, setNextMessage } =
|
const {
|
||||||
createMessageGenerator(message);
|
generateMessages,
|
||||||
|
setNextMessage,
|
||||||
|
setFirstMessagePromise,
|
||||||
|
resolveFirstMessage,
|
||||||
|
awaitFirstMessage,
|
||||||
|
} = createMessageGenerator(message);
|
||||||
|
|
||||||
const task: PendingClaudeCodeTask = {
|
const task: PendingClaudeCodeTask = {
|
||||||
status: "pending",
|
status: "pending",
|
||||||
@@ -78,6 +84,9 @@ export class ClaudeCodeTaskController {
|
|||||||
cwd: currentSession.cwd,
|
cwd: currentSession.cwd,
|
||||||
generateMessages,
|
generateMessages,
|
||||||
setNextMessage,
|
setNextMessage,
|
||||||
|
setFirstMessagePromise,
|
||||||
|
resolveFirstMessage,
|
||||||
|
awaitFirstMessage,
|
||||||
onMessageHandlers: [],
|
onMessageHandlers: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,25 +129,31 @@ export class ClaudeCodeTaskController {
|
|||||||
|
|
||||||
// 初回の system message だとまだ history ファイルが作成されていないので
|
// 初回の system message だとまだ history ファイルが作成されていないので
|
||||||
if (
|
if (
|
||||||
!resolved &&
|
|
||||||
(message.type === "user" || message.type === "assistant") &&
|
(message.type === "user" || message.type === "assistant") &&
|
||||||
message.uuid !== undefined
|
message.uuid !== undefined
|
||||||
) {
|
) {
|
||||||
const runningTask: RunningClaudeCodeTask = {
|
if (!resolved) {
|
||||||
status: "running",
|
const runningTask: RunningClaudeCodeTask = {
|
||||||
id: task.id,
|
status: "running",
|
||||||
projectId: task.projectId,
|
id: task.id,
|
||||||
cwd: task.cwd,
|
projectId: task.projectId,
|
||||||
generateMessages: task.generateMessages,
|
cwd: task.cwd,
|
||||||
setNextMessage: task.setNextMessage,
|
generateMessages: task.generateMessages,
|
||||||
onMessageHandlers: task.onMessageHandlers,
|
setNextMessage: task.setNextMessage,
|
||||||
userMessageId: message.uuid,
|
resolveFirstMessage: task.resolveFirstMessage,
|
||||||
sessionId: message.session_id,
|
setFirstMessagePromise: task.setFirstMessagePromise,
|
||||||
abortController: abortController,
|
awaitFirstMessage: task.awaitFirstMessage,
|
||||||
};
|
onMessageHandlers: task.onMessageHandlers,
|
||||||
this.tasks.push(runningTask);
|
userMessageId: message.uuid,
|
||||||
aliveTaskResolve(runningTask);
|
sessionId: message.session_id,
|
||||||
resolved = true;
|
abortController: abortController,
|
||||||
|
};
|
||||||
|
this.tasks.push(runningTask);
|
||||||
|
aliveTaskResolve(runningTask);
|
||||||
|
resolved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveFirstMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
@@ -152,6 +167,8 @@ export class ClaudeCodeTaskController {
|
|||||||
...currentTask,
|
...currentTask,
|
||||||
status: "paused",
|
status: "paused",
|
||||||
});
|
});
|
||||||
|
resolved = true;
|
||||||
|
setFirstMessagePromise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +221,9 @@ export class ClaudeCodeTaskController {
|
|||||||
cwd: task.cwd,
|
cwd: task.cwd,
|
||||||
generateMessages: task.generateMessages,
|
generateMessages: task.generateMessages,
|
||||||
setNextMessage: task.setNextMessage,
|
setNextMessage: task.setNextMessage,
|
||||||
|
resolveFirstMessage: task.resolveFirstMessage,
|
||||||
|
setFirstMessagePromise: task.setFirstMessagePromise,
|
||||||
|
awaitFirstMessage: task.awaitFirstMessage,
|
||||||
onMessageHandlers: task.onMessageHandlers,
|
onMessageHandlers: task.onMessageHandlers,
|
||||||
baseSessionId: task.baseSessionId,
|
baseSessionId: task.baseSessionId,
|
||||||
userMessageId: task.userMessageId,
|
userMessageId: task.userMessageId,
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ export type MessageGenerator = () => AsyncGenerator<
|
|||||||
unknown
|
unknown
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const createPromise = () => {
|
const createPromise = <T>() => {
|
||||||
let promiseResolve: ((value: string) => void) | undefined;
|
let promiseResolve: ((value: T) => void) | undefined;
|
||||||
let promiseReject: ((reason?: unknown) => void) | undefined;
|
let promiseReject: ((reason?: unknown) => void) | undefined;
|
||||||
|
|
||||||
const promise = new Promise<string>((resolve, reject) => {
|
const promise = new Promise<T>((resolve, reject) => {
|
||||||
promiseResolve = resolve;
|
promiseResolve = resolve;
|
||||||
promiseReject = reject;
|
promiseReject = reject;
|
||||||
});
|
});
|
||||||
@@ -33,8 +33,12 @@ export const createMessageGenerator = (
|
|||||||
): {
|
): {
|
||||||
generateMessages: MessageGenerator;
|
generateMessages: MessageGenerator;
|
||||||
setNextMessage: (message: string) => void;
|
setNextMessage: (message: string) => void;
|
||||||
|
setFirstMessagePromise: () => void;
|
||||||
|
resolveFirstMessage: () => void;
|
||||||
|
awaitFirstMessage: () => Promise<void>;
|
||||||
} => {
|
} => {
|
||||||
let currentPromise = createPromise();
|
let sendMessagePromise = createPromise<string>();
|
||||||
|
let receivedFirstMessagePromise = createPromise<undefined>();
|
||||||
|
|
||||||
const createMessage = (message: string): SDKUserMessage => {
|
const createMessage = (message: string): SDKUserMessage => {
|
||||||
return {
|
return {
|
||||||
@@ -50,19 +54,34 @@ export const createMessageGenerator = (
|
|||||||
yield createMessage(firstMessage);
|
yield createMessage(firstMessage);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const message = await currentPromise.promise;
|
const message = await sendMessagePromise.promise;
|
||||||
currentPromise = createPromise();
|
sendMessagePromise = createPromise<string>();
|
||||||
|
|
||||||
yield createMessage(message);
|
yield createMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const setNextMessage = (message: string) => {
|
const setNextMessage = (message: string) => {
|
||||||
currentPromise.resolve(message);
|
sendMessagePromise.resolve(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFirstMessagePromise = () => {
|
||||||
|
receivedFirstMessagePromise = createPromise<undefined>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveFirstMessage = () => {
|
||||||
|
receivedFirstMessagePromise.resolve(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
const awaitFirstMessage = async () => {
|
||||||
|
await receivedFirstMessagePromise.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
generateMessages,
|
generateMessages,
|
||||||
setNextMessage,
|
setNextMessage,
|
||||||
|
setFirstMessagePromise,
|
||||||
|
resolveFirstMessage,
|
||||||
|
awaitFirstMessage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ type BaseClaudeCodeTask = {
|
|||||||
cwd: string;
|
cwd: string;
|
||||||
generateMessages: MessageGenerator;
|
generateMessages: MessageGenerator;
|
||||||
setNextMessage: (message: string) => void;
|
setNextMessage: (message: string) => void;
|
||||||
|
resolveFirstMessage: () => void;
|
||||||
|
setFirstMessagePromise: () => void;
|
||||||
|
awaitFirstMessage: () => Promise<void>;
|
||||||
onMessageHandlers: OnMessage[];
|
onMessageHandlers: OnMessage[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,6 +36,7 @@ type CompletedClaudeCodeTask = BaseClaudeCodeTask & {
|
|||||||
sessionId: string;
|
sessionId: string;
|
||||||
userMessageId: string;
|
userMessageId: string;
|
||||||
abortController: AbortController;
|
abortController: AbortController;
|
||||||
|
resolveFirstMessage: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type FailedClaudeCodeTask = BaseClaudeCodeTask & {
|
type FailedClaudeCodeTask = BaseClaudeCodeTask & {
|
||||||
|
|||||||
Reference in New Issue
Block a user