-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsection.tsx
More file actions
110 lines (103 loc) · 3.72 KB
/
section.tsx
File metadata and controls
110 lines (103 loc) · 3.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"use client";
import {
createContext,
ReactNode,
useCallback,
useContext,
useState,
} from "react";
import { type MarkdownSection } from "./splitMarkdown";
import { StyledMarkdown } from "./markdown";
import { ChatForm } from "./chatForm";
import { ReplCommand, ReplOutput } from "../terminal/repl";
import { useFile } from "../terminal/file";
// セクション内に埋め込まれているターミナルとファイルエディターの内容をSection側から取得できるよう、
// Contextに保存する
// TODO: C++では複数ファイルを実行する場合がありうるが、ここではfilenameを1つしか受け付けない想定になっている
interface ISectionCodeContext {
addReplOutput: (command: string, output: ReplOutput[]) => void;
addFile: (filename: string) => void;
setExecResult: (filename: string, output: ReplOutput[]) => void;
}
const SectionCodeContext = createContext<ISectionCodeContext | null>(null);
export const useSectionCode = () => useContext(SectionCodeContext);
interface SectionProps {
section: MarkdownSection;
sectionId: string;
}
// 1つのセクションのタイトルと内容を表示する。内容はMarkdownとしてレンダリングする
export function Section({ section, sectionId }: SectionProps) {
const [replOutputs, setReplOutputs] = useState<ReplCommand[]>([]);
const [execResults, setExecResults] = useState<Record<string, ReplOutput[]>>(
{}
);
const [filenames, setFilenames] = useState<string[]>([]);
const { files } = useFile();
const fileContents: { name: string; content: string }[] = filenames.map(
(name) => ({ name, content: files[name] || "" })
);
const addReplOutput = useCallback(
(command: string, output: ReplOutput[]) =>
setReplOutputs((outs) => [...outs, { command, output }]),
[]
);
const addFile = useCallback(
(filename: string) =>
setFilenames((filenames) =>
filenames.includes(filename) ? filenames : [...filenames, filename]
),
[]
);
const setExecResult = useCallback(
(filename: string, output: ReplOutput[]) =>
setExecResults((results) => {
results[filename] = output;
return results;
}),
[]
);
// replOutputs: section内にあるターミナルにユーザーが入力したコマンドとその実行結果
// fileContents: section内にあるファイルエディターの内容
// execResults: section内にあるファイルの実行結果
// console.log(section.title, replOutputs, fileContents, execResults);
return (
<SectionCodeContext.Provider
value={{ addReplOutput, addFile, setExecResult }}
>
<div>
<Heading level={section.level}>{section.title}</Heading>
<StyledMarkdown content={section.content} />
<ChatForm
documentContent={section.content}
sectionId={sectionId}
replOutputs={replOutputs}
fileContents={fileContents}
execResults={execResults}
/>
</div>
</SectionCodeContext.Provider>
);
}
export function Heading({
level,
children,
}: {
level: number;
children: ReactNode;
}) {
switch (level) {
case 1:
return <h1 className="text-2xl font-bold my-4">{children}</h1>;
case 2:
return <h2 className="text-xl font-bold mt-4 mb-3 ">{children}</h2>;
case 3:
return <h3 className="text-lg font-bold mt-4 mb-2">{children}</h3>;
case 4:
return <h4 className="text-base font-bold mt-3 mb-2">{children}</h4>;
case 5:
// TODO: これ以下は4との差がない (全体的に大きくする必要がある?)
return <h5 className="text-base font-bold mt-3 mb-2">{children}</h5>;
case 6:
return <h6 className="text-base font-bold mt-3 mb-2">{children}</h6>;
}
}