From b3fd3c979f13fb72ec6b6a7bc288298734fe0efa Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:00:19 +0900 Subject: [PATCH 01/20] =?UTF-8?q?=E3=83=81=E3=83=A3=E3=83=83=E3=83=88?= =?UTF-8?q?=E3=81=AE=E8=A1=A8=E7=A4=BA=E3=82=92parallel=20routes=E3=81=A7?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(docs)/@chat/chat/[chatId]/page.tsx | 53 +++++ app/(docs)/@chat/chat/page.tsx | 7 + app/(docs)/@chat/default.tsx | 5 + .../@docs}/[lang]/[pageId]/chatForm.tsx | 0 .../@docs}/[lang]/[pageId]/chatHistory.tsx | 0 .../@docs}/[lang]/[pageId]/loading.tsx | 0 .../@docs}/[lang]/[pageId]/page.tsx | 0 .../@docs}/[lang]/[pageId]/pageContent.tsx | 196 ++++++++++++------ .../@docs}/[lang]/[pageId]/pageTransition.tsx | 0 app/(docs)/@docs/default.tsx | 8 + app/(docs)/chatAreaState.tsx | 40 ++++ app/(docs)/default.tsx | 1 + app/(docs)/layout.tsx | 26 +++ app/layout.tsx | 2 +- app/markdown/heading.tsx | 39 +++- 15 files changed, 305 insertions(+), 72 deletions(-) create mode 100644 app/(docs)/@chat/chat/[chatId]/page.tsx create mode 100644 app/(docs)/@chat/chat/page.tsx create mode 100644 app/(docs)/@chat/default.tsx rename app/{ => (docs)/@docs}/[lang]/[pageId]/chatForm.tsx (100%) rename app/{ => (docs)/@docs}/[lang]/[pageId]/chatHistory.tsx (100%) rename app/{ => (docs)/@docs}/[lang]/[pageId]/loading.tsx (100%) rename app/{ => (docs)/@docs}/[lang]/[pageId]/page.tsx (100%) rename app/{ => (docs)/@docs}/[lang]/[pageId]/pageContent.tsx (57%) rename app/{ => (docs)/@docs}/[lang]/[pageId]/pageTransition.tsx (100%) create mode 100644 app/(docs)/@docs/default.tsx create mode 100644 app/(docs)/chatAreaState.tsx create mode 100644 app/(docs)/default.tsx create mode 100644 app/(docs)/layout.tsx diff --git a/app/(docs)/@chat/chat/[chatId]/page.tsx b/app/(docs)/@chat/chat/[chatId]/page.tsx new file mode 100644 index 00000000..b483a7e8 --- /dev/null +++ b/app/(docs)/@chat/chat/[chatId]/page.tsx @@ -0,0 +1,53 @@ +import { ChatAreaStateUpdater } from "@/(docs)/chatAreaState"; +import { StyledMarkdown } from "@/markdown/markdown"; +import clsx from "clsx"; +import Link from "next/link"; + +export default async function ChatPage({ + params, +}: { + params: Promise<{ chatId: string }>; +}) { + const { chatId } = await params; + + // TODO: 実際のchatを取得 + const messages = [ + { role: "user", content: "a" }, + { role: "ai", content: "b" }, + ]; + + return ( + + ); +} diff --git a/app/(docs)/@chat/chat/page.tsx b/app/(docs)/@chat/chat/page.tsx new file mode 100644 index 00000000..30497200 --- /dev/null +++ b/app/(docs)/@chat/chat/page.tsx @@ -0,0 +1,7 @@ +import { ChatAreaStateUpdater } from "../../chatAreaState"; + +// /chat にアクセスしたときチャットを閉じる + +export default function EmptyPage() { + return ; +} diff --git a/app/(docs)/@chat/default.tsx b/app/(docs)/@chat/default.tsx new file mode 100644 index 00000000..db011017 --- /dev/null +++ b/app/(docs)/@chat/default.tsx @@ -0,0 +1,5 @@ +import { ChatAreaStateUpdater } from "../chatAreaState"; + +export default function EmptyPage() { + return ; +} diff --git a/app/[lang]/[pageId]/chatForm.tsx b/app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx similarity index 100% rename from app/[lang]/[pageId]/chatForm.tsx rename to app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx diff --git a/app/[lang]/[pageId]/chatHistory.tsx b/app/(docs)/@docs/[lang]/[pageId]/chatHistory.tsx similarity index 100% rename from app/[lang]/[pageId]/chatHistory.tsx rename to app/(docs)/@docs/[lang]/[pageId]/chatHistory.tsx diff --git a/app/[lang]/[pageId]/loading.tsx b/app/(docs)/@docs/[lang]/[pageId]/loading.tsx similarity index 100% rename from app/[lang]/[pageId]/loading.tsx rename to app/(docs)/@docs/[lang]/[pageId]/loading.tsx diff --git a/app/[lang]/[pageId]/page.tsx b/app/(docs)/@docs/[lang]/[pageId]/page.tsx similarity index 100% rename from app/[lang]/[pageId]/page.tsx rename to app/(docs)/@docs/[lang]/[pageId]/page.tsx diff --git a/app/[lang]/[pageId]/pageContent.tsx b/app/(docs)/@docs/[lang]/[pageId]/pageContent.tsx similarity index 57% rename from app/[lang]/[pageId]/pageContent.tsx rename to app/(docs)/@docs/[lang]/[pageId]/pageContent.tsx index 8d3a85f0..f2772794 100644 --- a/app/[lang]/[pageId]/pageContent.tsx +++ b/app/(docs)/@docs/[lang]/[pageId]/pageContent.tsx @@ -12,9 +12,12 @@ import { MarkdownSection, PageEntry, PagePath, + SectionId, } from "@/lib/docs"; import { ReplacedRange } from "@/markdown/multiHighlight"; import { Heading } from "@/markdown/heading"; +import Link from "next/link"; +import { useChatId } from "@/(docs)/chatAreaState"; /** * MarkdownSectionに追加で、動的な情報を持たせる @@ -159,71 +162,41 @@ export function PageContent(props: PageContentProps) { const [isFormVisible, setIsFormVisible] = useState(false); return ( -
- - 第{pageEntry.index}章: {pageEntry.title} - -
- {dynamicMdContent.map((section, index) => ( - -
{ - sectionRefs.current[index] = el; - }} - > - {/* ドキュメントのコンテンツ */} - -
-
- {/* 右側に表示するチャット履歴欄 */} - {chatHistories - .filter( - (c) => - c.sectionId === section.id || - // 対象のセクションが存在しないものは、introセクション(index=0)にフォールバックする - (index === 0 && - dynamicMdContent.every((sec) => c.sectionId !== sec.id)) - ) - .map(({ chatId, messages }) => ( -
-
- {messages.map((msg, index) => ( -
-
- -
-
- ))} -
-
- ))} -
-
- ))} +
+
+ + 第{pageEntry.index}章: {pageEntry.title} + +
+ {dynamicMdContent.map((section, index) => ( + +
{ + sectionRefs.current[index] = el; + }} + > + {/* ドキュメントのコンテンツ */} + +
+
+ +
+
+ ))} +
); } + +function ChatListForSection(props: { + dynamicMdContent: DynamicMarkdownSection[]; + sectionId: SectionId; +}) { + const { chatHistories } = useChatHistoryContext(); + const { dynamicMdContent, sectionId } = props; + const filteredChatHistories = chatHistories.filter( + (c) => + c.sectionId === sectionId || + // 対象のセクションが存在しないものは、introセクション(index=0)にフォールバックする + (dynamicMdContent[0].id === sectionId && + dynamicMdContent.every((sec) => c.sectionId !== sec.id)) + ); + + const chatId = useChatId(); + + if (filteredChatHistories.length === 0) { + // チャットがないなら何も表示しない + return null; + } + + return ( + <> + {/*PC表示かつチャットを表示していない → チャットリストを表示*/} +
    +
  • チャット
  • + {filteredChatHistories.map(({ chatId }) => ( +
  • + + {chatId} + +
  • + ))} +
+ {/*PCでない or PC表示でチャットを表示している → 小さい吹き出しを表示*/} +
+ + {/**/} + + + + + {filteredChatHistories.length} + +
    + {filteredChatHistories.map(({ chatId }) => ( +
  • + + {chatId} + +
  • + ))} +
+
+ + ); +} diff --git a/app/[lang]/[pageId]/pageTransition.tsx b/app/(docs)/@docs/[lang]/[pageId]/pageTransition.tsx similarity index 100% rename from app/[lang]/[pageId]/pageTransition.tsx rename to app/(docs)/@docs/[lang]/[pageId]/pageTransition.tsx diff --git a/app/(docs)/@docs/default.tsx b/app/(docs)/@docs/default.tsx new file mode 100644 index 00000000..2cacb9a2 --- /dev/null +++ b/app/(docs)/@docs/default.tsx @@ -0,0 +1,8 @@ +export default function EmptyPage() { + return ( +
+ TODO: /chat/チャットid + に直接アクセスした場合には、チャットからそれに対応するページidを取得し、そのドキュメントに自動でリダイレクトする。 +
+ ); +} diff --git a/app/(docs)/chatAreaState.tsx b/app/(docs)/chatAreaState.tsx new file mode 100644 index 00000000..e7196a7e --- /dev/null +++ b/app/(docs)/chatAreaState.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { + createContext, + ReactNode, + useContext, + useEffect, + useState, +} from "react"; + +// 今開いているチャットのid or null を、@chat側でセットして、@docs側で取得するためのコンテキスト + +interface ChatAreaState { + chatId: string | null; + setChatId: (chatId: string | null) => void; +} +const ChatAreaStateContext = createContext(null!); + +export function useChatId() { + const { chatId } = useContext(ChatAreaStateContext); + return chatId; +} +export function ChatAreaStateUpdater(props: { chatId: string | null }) { + const { chatId, setChatId } = useContext(ChatAreaStateContext); + useEffect(() => { + if (chatId !== props.chatId) { + setChatId(props.chatId); + } + }, [chatId, setChatId, props.chatId]); + return null; +} + +export function ChatAreaStateProvider(props: { children: ReactNode }) { + const [chatId, setChatId] = useState(null); + return ( + + {props.children} + + ); +} diff --git a/app/(docs)/default.tsx b/app/(docs)/default.tsx new file mode 100644 index 00000000..70eaae53 --- /dev/null +++ b/app/(docs)/default.tsx @@ -0,0 +1 @@ +export default function EmptyPage() {} diff --git a/app/(docs)/layout.tsx b/app/(docs)/layout.tsx new file mode 100644 index 00000000..bee7fa3e --- /dev/null +++ b/app/(docs)/layout.tsx @@ -0,0 +1,26 @@ +import { ReactNode } from "react"; +import { ChatAreaStateProvider } from "./chatAreaState"; + +// app/(workspace)/layout.tsx +export default function WorkspaceLayout({ + children, + docs, + chat, +}: { + children: ReactNode; + docs: ReactNode; + chat: ReactNode; +}) { + return ( + +
+ {docs} + + {chat} + + {/* children(page.tsx)は今回は使わないか、背景として利用 */} + {children} +
+
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 2a2f41af..fc76c864 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -27,7 +27,7 @@ export default async function RootLayout({ const pagesList = await getPagesList(); return ( - + {/* mocha.css がbodyに背景色などを設定してしまうので、それを上書きしている */} diff --git a/app/markdown/heading.tsx b/app/markdown/heading.tsx index 031b4c7e..397ae4c5 100644 --- a/app/markdown/heading.tsx +++ b/app/markdown/heading.tsx @@ -1,27 +1,54 @@ +import clsx from "clsx"; import { ReactNode } from "react"; export function Heading({ level, children, + className, }: { level: number; children: ReactNode; + className?: string; }) { switch (level) { case 0: return null; case 1: - return

{children}

; + return ( +

+ {children} +

+ ); case 2: - return

{children}

; + return ( +

+ {children} +

+ ); case 3: - return

{children}

; + return ( +

+ {children} +

+ ); case 4: - return

{children}

; + return ( +

+ {children} +

+ ); case 5: // TODO: これ以下は4との差がない (全体的に大きくする必要がある?) - return
{children}
; + return ( +
+ {children} +
+ ); case 6: - return
{children}
; + return ( +
+ {children} +
+ ); } } From 6ff62d9f73e813c55cdc4dc9dd7071ace917d452 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:50:51 +0900 Subject: [PATCH 02/20] =?UTF-8?q?lib/chatHidtory=E3=81=8B=E3=82=89action?= =?UTF-8?q?=E3=81=A8cache=E3=82=92=E5=88=86=E9=9B=A2=E3=80=81chat=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=A7=E5=AE=9F=E9=9A=9B=E3=81=AE=E3=83=81?= =?UTF-8?q?=E3=83=A3=E3=83=83=E3=83=88=E3=82=92=E5=8F=96=E5=BE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(docs)/@chat/chat/[chatId]/page.tsx | 10 ++-- .../@docs/[lang]/[pageId]/chatHistory.tsx | 5 +- app/(docs)/@docs/[lang]/[pageId]/page.tsx | 44 +++++++++++++- app/actions/chatActions.ts | 10 +++- app/actions/getChatAll.ts | 9 +++ app/lib/chatHistory.ts | 60 +++++++++---------- app/markdown/codeBlock.tsx | 2 + 7 files changed, 95 insertions(+), 45 deletions(-) create mode 100644 app/actions/getChatAll.ts diff --git a/app/(docs)/@chat/chat/[chatId]/page.tsx b/app/(docs)/@chat/chat/[chatId]/page.tsx index b483a7e8..56df07c5 100644 --- a/app/(docs)/@chat/chat/[chatId]/page.tsx +++ b/app/(docs)/@chat/chat/[chatId]/page.tsx @@ -1,4 +1,5 @@ import { ChatAreaStateUpdater } from "@/(docs)/chatAreaState"; +import { getChatOne, initContext } from "@/lib/chatHistory"; import { StyledMarkdown } from "@/markdown/markdown"; import clsx from "clsx"; import Link from "next/link"; @@ -10,11 +11,8 @@ export default async function ChatPage({ }) { const { chatId } = await params; - // TODO: 実際のchatを取得 - const messages = [ - { role: "user", content: "a" }, - { role: "ai", content: "b" }, - ]; + const ctx = await initContext(); + const chatData = await getChatOne(chatId, ctx); return (