Skip to content

Commit 62c9a7d

Browse files
committed
diffをドキュメントに適用
1 parent ff35130 commit 62c9a7d

3 files changed

Lines changed: 61 additions & 24 deletions

File tree

app/[lang]/[pageId]/pageContent.tsx

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { Fragment, useEffect, useRef, useState } from "react";
3+
import { Fragment, useCallback, useEffect, useRef, useState } from "react";
44
import { ChatForm } from "./chatForm";
55
import { Heading, StyledMarkdown } from "./markdown";
66
import { useChatHistoryContext } from "./chatHistory";
@@ -14,9 +14,18 @@ import {
1414
PagePath,
1515
} from "@/lib/docs";
1616

17-
// MarkdownSectionに追加で、ユーザーが今そのセクションを読んでいるかどうか、などの動的な情報を持たせる
17+
/**
18+
* MarkdownSectionに追加で、動的な情報を持たせる
19+
*/
1820
export type DynamicMarkdownSection = MarkdownSection & {
21+
/**
22+
* ユーザーが今そのセクションを読んでいるかどうか
23+
*/
1924
inView: boolean;
25+
/**
26+
* チャットの会話を元にAIが書き換えた後の内容
27+
*/
28+
replacedContent: string;
2029
};
2130

2231
interface PageContentProps {
@@ -31,25 +40,52 @@ export function PageContent(props: PageContentProps) {
3140
const { setSidebarMdContent } = useSidebarMdContext();
3241
const { splitMdContent, pageEntry, path } = props;
3342

34-
// SSR用のローカルstate
35-
const [dynamicMdContent, setDynamicMdContent] = useState<
36-
DynamicMarkdownSection[]
37-
>(
38-
splitMdContent.map((section) => ({
39-
...section,
40-
inView: false,
41-
}))
42-
);
43+
const { chatHistories } = useChatHistoryContext();
4344

44-
useEffect(() => {
45-
// props.splitMdContentが変わったときにローカルstateとcontextの両方を更新
45+
const initDynamicMdContent = useCallback(() => {
4646
const newContent = splitMdContent.map((section) => ({
4747
...section,
4848
inView: false,
49+
replacedContent: section.rawContent,
4950
}));
51+
const chatDiffs = chatHistories.map((chat) => chat.diff).flat();
52+
chatDiffs.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
53+
for (const diff of chatDiffs) {
54+
const targetSection = newContent.find((s) => s.id === diff.sectionId);
55+
if (targetSection) {
56+
if (targetSection.replacedContent.includes(diff.search)) {
57+
targetSection.replacedContent = targetSection.replacedContent.replace(
58+
diff.search,
59+
diff.replace
60+
);
61+
} else {
62+
// TODO: md5ハッシュを参照し過去バージョンのドキュメントへ適用を試みる
63+
console.error(
64+
`Failed to apply diff: search string "${diff.search}" not found in section ${targetSection.id}`
65+
);
66+
}
67+
} else {
68+
console.error(
69+
`Failed to apply diff: section with id "${diff.sectionId}" not found`
70+
);
71+
}
72+
}
73+
74+
return newContent;
75+
}, [splitMdContent, chatHistories]);
76+
77+
// SSR用のローカルstate
78+
const [dynamicMdContent, setDynamicMdContent] = useState<
79+
DynamicMarkdownSection[]
80+
>(() => initDynamicMdContent());
81+
82+
useEffect(() => {
83+
// props.splitMdContentが変わったとき, チャットのdiffが変わった時に
84+
// ローカルstateとcontextの両方を更新
85+
const newContent = initDynamicMdContent();
5086
setDynamicMdContent(newContent);
5187
setSidebarMdContent(path, newContent);
52-
}, [splitMdContent, path, setSidebarMdContent]);
88+
}, [initDynamicMdContent, path, setSidebarMdContent]);
5389

5490
const sectionRefs = useRef<Array<HTMLDivElement | null>>([]);
5591
// sectionRefsの長さをsplitMdContentに合わせる
@@ -87,8 +123,6 @@ export function PageContent(props: PageContentProps) {
87123

88124
const [isFormVisible, setIsFormVisible] = useState(false);
89125

90-
const { chatHistories } = useChatHistoryContext();
91-
92126
return (
93127
<div
94128
className="p-4 mx-auto max-w-full grid"
@@ -110,7 +144,7 @@ export function PageContent(props: PageContentProps) {
110144
}}
111145
>
112146
{/* ドキュメントのコンテンツ */}
113-
<StyledMarkdown content={section.rawContent} />
147+
<StyledMarkdown content={section.replacedContent} />
114148
</div>
115149
<div>
116150
{/* 右側に表示するチャット履歴欄 */}

app/actions/chatActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export async function askAI(params: ChatParams): Promise<ChatResult> {
7777
prompt.push(``);
7878
for (const section of sectionContent) {
7979
prompt.push(`[セクションid: ${section.id}]`);
80-
prompt.push(section.rawContent.trim());
80+
prompt.push(section.replacedContent.trim());
8181
prompt.push(``);
8282
}
8383
prompt.push(``);

app/lib/chatHistory.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,15 @@ export async function addChat(
8989
)
9090
.returning();
9191

92-
const chatDiffs = await drizzle.insert(diff).values(
93-
diffRaw.map((d) => ({
94-
chatId: newChat.chatId,
95-
...d,
96-
}))
97-
);
92+
const chatDiffs = await drizzle
93+
.insert(diff)
94+
.values(
95+
diffRaw.map((d) => ({
96+
chatId: newChat.chatId,
97+
...d,
98+
}))
99+
)
100+
.returning();
98101

99102
revalidateTag(cacheKeyForPage(path, userId));
100103
if (isCloudflare()) {

0 commit comments

Comments
 (0)