Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions app/[docs_id]/chatHistory.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"use client";

import { ChatWithMessages } from "@/lib/chatHistory";
import { ChatWithMessages, getChat } from "@/lib/chatHistory";
import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";
import useSWR from "swr";

export interface IChatHistoryContext {
chatHistories: ChatWithMessages[];
Expand All @@ -27,19 +28,38 @@ export function useChatHistoryContext() {

export function ChatHistoryProvider({
children,
docs_id,
initialChatHistories,
}: {
children: ReactNode;
docs_id: string;
initialChatHistories: ChatWithMessages[];
}) {
const [chatHistories, setChatHistories] =
useState<ChatWithMessages[]>(initialChatHistories);
// 最初はSSRで取得したinitialChatHistoriesを使用(キャッシュされているので古い可能性がある)
useEffect(() => {
setChatHistories(initialChatHistories);
}, [initialChatHistories]);
// その後、クライアント側で最新のchatHistoriesを改めて取得して更新する
const { data: fetchedChatHistories } = useSWR<ChatWithMessages[]>(
docs_id,
getChat,
{
// リクエストは古くても構わないので1回でいい
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
}
);
useEffect(() => {
if (fetchedChatHistories) {
setChatHistories(fetchedChatHistories);
}
}, [fetchedChatHistories]);

// チャットを更新した際にはクライアントサイドでchatHistoryに反映する
const addChat = (chat: ChatWithMessages) => {
// サーバー側で追加された新しいchatをクライアント側にも反映する
setChatHistories([...chatHistories, chat]);
};

Expand Down
40 changes: 0 additions & 40 deletions app/[docs_id]/dynamicMdContext.tsx

This file was deleted.

12 changes: 12 additions & 0 deletions app/[docs_id]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default function Loading() {
return (
<div className="p-4 mx-auto w-full max-w-200">
<div className="skeleton h-8 w-3/4 my-4">{/* heading1 */}</div>
<div className="skeleton h-20 w-full my-2">{/* <p> */}</div>
<div className="skeleton h-7 w-2/4 mt-4 mb-3">{/* heading2 */}</div>
<div className="skeleton h-40 w-full my-2">{/* <p> */}</div>
<div className="skeleton h-7 w-2/4 mt-4 mb-3">{/* heading2 */}</div>
<div className="skeleton h-40 w-full my-2">{/* <p> */}</div>
</div>
);
}
4 changes: 2 additions & 2 deletions app/[docs_id]/markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Markdown, { Components } from "react-markdown";
import remarkGfm from "remark-gfm";
import SyntaxHighlighter from "react-syntax-highlighter";
import { type AceLang, EditorComponent, getAceLang } from "../terminal/editor";
import { EditorComponent, getAceLang } from "../terminal/editor";
import { ExecFile } from "../terminal/exec";
import { useChangeTheme } from "./themeToggle";
import {
tomorrow,
atomOneDark,
} from "react-syntax-highlighter/dist/esm/styles/hljs";
import { ReactNode } from "react";
import { getRuntimeLang, RuntimeLang } from "@/terminal/runtime";
import { getRuntimeLang } from "@/terminal/runtime";
import { ReplTerminal } from "@/terminal/repl";

export function StyledMarkdown({ content }: { content: string }) {
Expand Down
6 changes: 3 additions & 3 deletions app/[docs_id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MarkdownSection, splitMarkdown } from "./splitMarkdown";
import pyodideLock from "pyodide/pyodide-lock.json";
import { PageContent } from "./pageContent";
import { ChatHistoryProvider } from "./chatHistory";
import { getChat } from "@/lib/chatHistory";
import { getChatFromCache } from "@/lib/chatHistory";

export default async function Page({
params,
Expand Down Expand Up @@ -50,10 +50,10 @@ export default async function Page({
splitMarkdown(text)
);

const initialChatHistories = getChat(docs_id);
const initialChatHistories = getChatFromCache(docs_id);

return (
<ChatHistoryProvider initialChatHistories={await initialChatHistories}>
<ChatHistoryProvider initialChatHistories={await initialChatHistories} docs_id={docs_id}>
<PageContent
documentContent={await mdContent}
splitMdContent={await splitMdContent}
Expand Down
32 changes: 15 additions & 17 deletions app/[docs_id]/pageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MarkdownSection } from "./splitMarkdown";
import { ChatForm } from "./chatForm";
import { Heading, StyledMarkdown } from "./markdown";
import { useChatHistoryContext } from "./chatHistory";
import { useSidebarMdContext } from "./dynamicMdContext";
import { useSidebarMdContext } from "../sidebar";
import clsx from "clsx";

// MarkdownSectionに追加で、ユーザーが今そのセクションを読んでいるかどうか、などの動的な情報を持たせる
Expand All @@ -21,7 +21,7 @@ interface PageContentProps {
}
export function PageContent(props: PageContentProps) {
const { setSidebarMdContent } = useSidebarMdContext();

// SSR用のローカルstate
const [dynamicMdContent, setDynamicMdContent] = useState<
DynamicMarkdownSection[]
Expand All @@ -41,12 +41,7 @@ export function PageContent(props: PageContentProps) {
sectionId: `${props.docs_id}-${i}`,
}));
setDynamicMdContent(newContent);
setSidebarMdContent(newContent);

// クリーンアップ:コンポーネントがアンマウントされたらcontextをクリア
return () => {
setSidebarMdContent([]);
};
setSidebarMdContent(props.docs_id, newContent);
}, [props.splitMdContent, props.docs_id, setSidebarMdContent]);

const sectionRefs = useRef<Array<HTMLDivElement | null>>([]);
Expand All @@ -57,28 +52,31 @@ export function PageContent(props: PageContentProps) {

useEffect(() => {
const handleScroll = () => {
const updateContent = (prevDynamicMdContent: DynamicMarkdownSection[]) => {
const updateContent = (
prevDynamicMdContent: DynamicMarkdownSection[]
) => {
const dynMdContent = prevDynamicMdContent.slice(); // Reactの変更検知のために新しい配列を作成
for (let i = 0; i < sectionRefs.current.length; i++) {
if (sectionRefs.current.at(i) && dynMdContent.at(i)) {
const rect = sectionRefs.current.at(i)!.getBoundingClientRect();
dynMdContent.at(i)!.inView =
rect.top < window.innerHeight && rect.bottom >= 0;
rect.top < window.innerHeight * 0.9 &&
rect.bottom >= window.innerHeight * 0.1;
}
}
return dynMdContent;
};

// ローカルstateとcontextの両方を更新
setDynamicMdContent(updateContent);
setSidebarMdContent(updateContent);
setSidebarMdContent(props.docs_id, updateContent);
};
window.addEventListener("scroll", handleScroll);
handleScroll();
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [setSidebarMdContent]);
}, [setSidebarMdContent, props.docs_id]);

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

Expand Down Expand Up @@ -107,8 +105,9 @@ export function PageContent(props: PageContentProps) {
</div>
<div key={`${index}-chat`}>
{/* 右側に表示するチャット履歴欄 */}
{chatHistories.filter((c) => c.sectionId === section.sectionId).map(
({chatId, messages}) => (
{chatHistories
.filter((c) => c.sectionId === section.sectionId)
.map(({ chatId, messages }) => (
<div
key={chatId}
className="max-w-xs mb-2 p-2 text-sm border border-base-content/10 rounded-sm shadow-sm bg-base-100"
Expand Down Expand Up @@ -136,8 +135,7 @@ export function PageContent(props: PageContentProps) {
))}
</div>
</div>
)
)}
))}
</div>
</>
))}
Expand Down
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Sidebar } from "./sidebar";
import { ReactNode } from "react";
import { EmbedContextProvider } from "./terminal/embedContext";
import { AutoAnonymousLogin } from "./accountMenu";
import { SidebarMdProvider } from "./[docs_id]/dynamicMdContext";
import { SidebarMdProvider } from "./sidebar";
import { RuntimeProvider } from "./terminal/runtime";

export const metadata: Metadata = {
Expand Down
20 changes: 20 additions & 0 deletions app/lib/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const cacheData: Map<string, ArrayBuffer> = new Map();
/**
* nodejsにcache apiがないので、web標準のcache APIに相当するものの自前実装
*/
export const inMemoryCache = {
async put(key: string, response: Response): Promise<void> {
const arrayBuffer = await response.arrayBuffer();
cacheData.set(key, arrayBuffer);
},
async match(key: string): Promise<Response | undefined> {
const arrayBuffer = cacheData.get(key);
if (arrayBuffer) {
return new Response(arrayBuffer);
}
return undefined;
},
async delete(key: string): Promise<boolean> {
return cacheData.delete(key);
},
} as const;
Loading