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