Skip to content

Commit 1f36931

Browse files
authored
Merge pull request #20 from ut-code/wandbox
C++のページを追加
2 parents 7527879 + 6a582f9 commit 1f36931

File tree

15 files changed

+1288
-111
lines changed

15 files changed

+1288
-111
lines changed

app/[docs_id]/markdown.tsx

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import remarkGfm from "remark-gfm";
33
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
44
import { PythonEmbeddedTerminal } from "../terminal/python/embedded";
55
import { Heading } from "./section";
6-
import { EditorComponent } from "../terminal/editor";
7-
import { ExecFile } from "../terminal/exec";
6+
import { AceLang, EditorComponent } from "../terminal/editor";
7+
import { ExecFile, ExecLang } from "../terminal/exec";
88

99
export function StyledMarkdown({ content }: { content: string }) {
1010
return (
@@ -34,6 +34,11 @@ const components: Components = {
3434
strong: ({ node, ...props }) => (
3535
<strong className="text-primary" {...props} />
3636
),
37+
table: ({ node, ...props }) => (
38+
<div className="w-max max-w-full overflow-x-auto mx-auto my-2 rounded-lg border border-base-content/5 shadow-sm">
39+
<table className="table w-max" {...props} />
40+
</div>
41+
),
3742
hr: ({ node, ...props }) => <hr className="border-primary my-4" {...props} />,
3843
pre: ({ node, ...props }) => props.children,
3944
code: ({ node, className, ref, style, ...props }) => {
@@ -52,21 +57,59 @@ const components: Components = {
5257
hello, world!
5358
---------------------------
5459
*/
55-
return (
56-
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
57-
<ExecFile
58-
language={match[1]}
59-
filename={match[3]}
60-
content={String(props.children || "").replace(/\n$/, "")}
61-
/>
62-
</div>
63-
);
60+
let execLang: ExecLang | undefined = undefined;
61+
switch (match[1]) {
62+
case "python":
63+
execLang = "python";
64+
break;
65+
case "cpp":
66+
case "c++":
67+
execLang = "cpp";
68+
break;
69+
default:
70+
console.warn(`Unsupported language for exec: ${match[1]}`);
71+
break;
72+
}
73+
if (execLang) {
74+
return (
75+
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
76+
<ExecFile
77+
language={execLang}
78+
filenames={match[3].split(",")}
79+
content={String(props.children || "").replace(/\n$/, "")}
80+
/>
81+
</div>
82+
);
83+
}
6484
} else if (match[3]) {
6585
// ファイル名指定がある場合、ファイルエディター
86+
let aceLang: AceLang | undefined = undefined;
87+
switch (match[1]) {
88+
case "python":
89+
aceLang = "python";
90+
break;
91+
case "cpp":
92+
case "c++":
93+
aceLang = "c_cpp";
94+
break;
95+
case "json":
96+
aceLang = "json";
97+
break;
98+
case "csv":
99+
aceLang = "csv";
100+
break;
101+
case "text":
102+
case "txt":
103+
aceLang = "text";
104+
break;
105+
default:
106+
console.warn(`Unsupported language for editor: ${match[1]}`);
107+
break;
108+
}
66109
return (
67110
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
68111
<EditorComponent
69-
language={match[1]}
112+
language={aceLang}
70113
tabSize={4}
71114
filename={match[3]}
72115
readonly={match[2] === "-readonly"}

app/[docs_id]/section.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useFile } from "../terminal/file";
1515

1616
// セクション内に埋め込まれているターミナルとファイルエディターの内容をSection側から取得できるよう、
1717
// Contextに保存する
18+
// TODO: C++では複数ファイルを実行する場合がありうるが、ここではfilenameを1つしか受け付けない想定になっている
1819
interface ISectionCodeContext {
1920
addReplOutput: (command: string, output: ReplOutput[]) => void;
2021
addFile: (filename: string) => void;

app/layout.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Sidebar } from "./sidebar";
55
import { ReactNode } from "react";
66
import { PyodideProvider } from "./terminal/python/pyodide";
77
import { FileProvider } from "./terminal/file";
8+
import { WandboxProvider } from "./terminal/wandbox/wandbox";
89

910
export const metadata: Metadata = {
1011
title: "Create Next App",
@@ -22,7 +23,9 @@ export default function RootLayout({
2223
<div className="drawer-content flex flex-col">
2324
<Navbar />
2425
<FileProvider>
25-
<PyodideProvider>{children}</PyodideProvider>
26+
<PyodideProvider>
27+
<WandboxProvider>{children}</WandboxProvider>
28+
</PyodideProvider>
2629
</FileProvider>
2730
</div>
2831
<div className="drawer-side shadow-md">

app/page.tsx

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,79 @@
1+
import Link from "next/link";
2+
import { pagesList } from "./pagesList";
3+
14
export default function Home() {
2-
return <div className="p-4">
3-
This is root page
4-
</div>;
5+
return (
6+
<div className="p-4">
7+
<h1 className="text-4xl font-bold my-4">my.code(); へようこそ</h1>
8+
<p>
9+
my.code();
10+
はプログラミング言語のチュートリアルを提供するウェブサイトです。
11+
</p>
12+
<div
13+
className="grid items-center gap-4 my-4"
14+
style={{
15+
gridTemplateColumns: "repeat(auto-fill, minmax(20rem, 1fr))",
16+
}}
17+
>
18+
{pagesList.map((group) => {
19+
return (
20+
<div
21+
key={group.id}
22+
className="card card-border bg-base-100 card-md shadow-md"
23+
>
24+
<div className="card-body">
25+
<h2 className="card-title">{group.lang}</h2>
26+
<p>{group.description}</p>
27+
<div className="justify-end card-actions">
28+
<Link
29+
href={`${group.id}-${group.pages[0].id}`}
30+
className="btn btn-primary"
31+
>
32+
はじめる
33+
</Link>
34+
</div>
35+
</div>
36+
</div>
37+
);
38+
})}
39+
</div>
40+
<h2 className="text-2xl font-bold my-4">主な特徴</h2>
41+
{/* TODO: デザインがダサい */}
42+
<ul className="list-disc list-inside space-y-4">
43+
<li>
44+
豊富なチュートリアル
45+
<p className="ml-8">
46+
my.code();
47+
ではさまざまなプログラミング言語やフレームワークのチュートリアルを提供しています。
48+
初心者向けの基礎から上級者向けの応用まで、幅広いレベルに対応したチュートリアルが揃っています。
49+
{/* ほんまか? */}
50+
</p>
51+
</li>
52+
<li>
53+
すぐに動かせる実行環境
54+
<p className="ml-8">
55+
my.code();
56+
ではブラウザ上でコードを実行できる環境を整備しており、環境構築の手間なくすぐにコードを実行することができます。
57+
チュートリアル内のサンプルコードはそのまま実行するだけでなく、自由に編集して試すことも可能です。
58+
</p>
59+
</li>
60+
<li>
61+
AIアシスタントによるサポート
62+
<p className="ml-8">
63+
my.code(); ではAIアシスタントが学習をサポートします。
64+
チュートリアルを読んでいてわからないことがあれば、AIアシスタントに質問してみてください。
65+
さらに、実行したサンプルコードの解説やエラーの原因調査、改善提案まで、AIアシスタントがあなたの学習を強力に支援します。
66+
</p>
67+
</li>
68+
<li>
69+
実践的な練習問題
70+
<p className="ml-8">
71+
各チュートリアルには練習問題が含まれており、学んだ内容を実際に試すことができます。
72+
練習問題は段階的に難易度が上がるように設計されており、理解度を深めるのに役立ちます。
73+
書いたコードはその場ですぐにAIアシスタントがレビューし、フィードバックを提供します。
74+
</p>
75+
</li>
76+
</ul>
77+
</div>
78+
);
579
}

app/pagesList.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// docs_id = `${group.id}-${page.id}`
2+
export const pagesList = [
3+
{
4+
id: "python",
5+
lang: "Python",
6+
// TODO: これをいい感じの文章に変える↓
7+
description: "Pythonの基礎から応用までを学べるチュートリアル",
8+
pages: [
9+
{ id: 1, title: "環境構築と基本思想" },
10+
{ id: 2, title: "基本構文とデータ型" },
11+
{ id: 3, title: "リスト、タプル、辞書、セット" },
12+
{ id: 4, title: "制御構文と関数" },
13+
{ id: 5, title: "モジュールとパッケージ" },
14+
{ id: 6, title: "オブジェクト指向プログラミング" },
15+
{
16+
id: 7,
17+
title: "ファイルの入出力とコンテキストマネージャ",
18+
},
19+
{ id: 8, title: "例外処理" },
20+
{ id: 9, title: "ジェネレータとデコレータ" },
21+
],
22+
},
23+
{
24+
id: "cpp",
25+
lang: "C++",
26+
description: "C++の基本から高度な機能までを学べるチュートリアル",
27+
pages: [
28+
{ id: 2, title: "型システムとメモリ" },
29+
{ id: 3, title: "関数と参照" },
30+
],
31+
},
32+
] as const;

app/sidebar.tsx

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,60 @@
11
"use client";
22
import Link from "next/link";
33
import { usePathname } from "next/navigation";
4-
import useSWR, { Fetcher } from 'swr'
4+
import useSWR, { Fetcher } from "swr";
55
import { splitMarkdown } from "./[docs_id]/splitMarkdown";
6+
import { pagesList } from "./pagesList";
67

7-
const fetcher: Fetcher<string, string> = (url) => fetch(url).then((r) => r.text())
8+
const fetcher: Fetcher<string, string> = (url) =>
9+
fetch(url).then((r) => r.text());
810

911
export function Sidebar() {
1012
const pathname = usePathname();
1113
const docs_id = pathname.replace(/^\//, "");
12-
const { data, error, isLoading } = useSWR(
13-
`/docs/${docs_id}.md`,
14-
fetcher
15-
)
14+
const { data, error, isLoading } = useSWR(`/docs/${docs_id}.md`, fetcher);
1615

17-
const pages = [
18-
{ id: "python-1", title: "1. 環境構築と基本思想" },
19-
{ id: "python-2", title: "2. 基本構文とデータ型" },
20-
{ id: "python-3", title: "3. リスト、タプル、辞書、セット" },
21-
{ id: "python-4", title: "4. 制御構文と関数" },
22-
{ id: "python-5", title: "5. モジュールとパッケージ" },
23-
{ id: "python-6", title: "6. オブジェクト指向プログラミング" },
24-
{ id: "python-7", title: "7. ファイルの入出力とコンテキストマネージャ" },
25-
{ id: "python-8", title: "8. 例外処理" },
26-
{ id: "python-9", title: "9. ジェネレータとデコレータ" },
27-
];
16+
if (error) console.error("Sidebar fetch error:", error);
2817

29-
if (error) console.error("Sidebar fetch error:", error)
30-
31-
32-
33-
34-
const splitmdcontent = splitMarkdown(data ?? "")
18+
const splitmdcontent = splitMarkdown(data ?? "");
3519
return (
36-
<div className="bg-base-200 min-h-full w-80 p-4">
20+
<div className="bg-base-200 h-full w-80 overflow-y-auto">
3721
{/* todo: 背景色ほんとにこれでいい? */}
38-
<h2 className="hidden lg:block text-xl font-bold mb-4">
22+
<h2 className="hidden lg:block text-xl font-bold p-4">
3923
{/* サイドバーが常時表示されている場合のみ */}
4024
Navbar Title
4125
</h2>
42-
43-
<ol className="menu w-full list-outside">
44-
{pages.map((page) => (
45-
<li key={page.id}>
46-
<Link href={`/${page.id}`}>{page.title}</Link>
47-
{page.id === docs_id && !isLoading &&(
48-
<ul className="ml-4 mt-2 text-sm">
49-
{splitmdcontent
50-
.slice(1)
51-
.map((section, idx) => (
52-
<li key={idx} style={{ marginLeft: (section.level - 2) + "em"}}>
53-
<Link href={`#${idx+1}`}>{section.title}</Link>
54-
</li>
55-
))}
56-
</ul>
57-
)}
5826

27+
<ul className="menu w-full">
28+
{pagesList.map((group) => (
29+
<li key={group.id}>
30+
<details open={docs_id.startsWith(`${group.id}-`)}>
31+
<summary>{group.lang}</summary>
32+
<ul>
33+
{group.pages.map((page) => (
34+
<li key={page.id}>
35+
<Link href={`${group.id}-${page.id}`}>
36+
<span className="mr-0">{page.id}.</span>
37+
{page.title}
38+
</Link>
39+
{`${group.id}-${page.id}` === docs_id && !isLoading && (
40+
<ul className="ml-4 text-sm">
41+
{splitmdcontent.slice(1).map((section, idx) => (
42+
<li
43+
key={idx}
44+
style={{ marginLeft: section.level - 2 + "em" }}
45+
>
46+
<Link href={`#${idx + 1}`}>{section.title}</Link>
47+
</li>
48+
))}
49+
</ul>
50+
)}
51+
</li>
52+
))}
53+
</ul>
54+
</details>
5955
</li>
6056
))}
61-
</ol>
57+
</ul>
6258
</div>
6359
);
6460
}
65-

app/terminal/editor.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import "ace-builds/src-min-noconflict/theme-twilight";
99
import "ace-builds/src-min-noconflict/ext-language_tools";
1010
import "ace-builds/src-min-noconflict/ext-searchbox";
1111
import "ace-builds/src-min-noconflict/mode-python";
12+
import "ace-builds/src-min-noconflict/mode-c_cpp";
1213
import "ace-builds/src-min-noconflict/mode-json";
1314
import "ace-builds/src-min-noconflict/mode-csv";
1415
import "ace-builds/src-min-noconflict/mode-text";
@@ -17,8 +18,11 @@ import { useSectionCode } from "../[docs_id]/section";
1718
import clsx from "clsx";
1819
// snippetを有効化するにはsnippetもimportする必要がある: import "ace-builds/src-min-noconflict/snippets/python";
1920

21+
// mode-xxxx.js のファイル名と、AceEditorの mode プロパティの値が対応する
22+
export type AceLang = "python" | "c_cpp" | "json" | "csv" | "text";
23+
2024
interface EditorProps {
21-
language?: string;
25+
language?: AceLang;
2226
tabSize: number;
2327
filename: string;
2428
initContent: string;

0 commit comments

Comments
 (0)