Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6730bbe
各言語のコードを別ファイルに分離して文字列としてインポート
na-trium-144 Dec 2, 2025
61446ee
Stack trace: のパース用文字列を変更 & signal表示をstderrと区別
na-trium-144 Dec 2, 2025
56e5a45
rust実行環境を実装、スタックトレースのパースを改善
na-trium-144 Dec 3, 2025
57406c7
sampleコードをファイルに分割、js?rawをインポートできるようwebpackの設定を修正
na-trium-144 Dec 3, 2025
7e7daba
update readme
na-trium-144 Dec 3, 2025
17da265
Merge remote-tracking branch 'origin/main' into rust
na-trium-144 Dec 4, 2025
bc0fb05
言語のアイコンを追加してみた
na-trium-144 Dec 4, 2025
b376a96
rust5章まで
na-trium-144 Dec 4, 2025
9b418f4
C++の2〜4章を書き直したくなった
na-trium-144 Dec 4, 2025
502cd7e
removeCommentsとcjkFriendlyを追加
na-trium-144 Dec 4, 2025
dfe4291
Merge remote-tracking branch 'origin/main' into rust
na-trium-144 Dec 5, 2025
175877d
メニューと本文の折り返しなどを改善
na-trium-144 Dec 5, 2025
5e55768
c++-1でのエラー修正
na-trium-144 Dec 5, 2025
d19fd0d
session取得時のエラーハンドリング
na-trium-144 Dec 5, 2025
4406c88
幅が狭い時の実行ボタンの表示を修正
na-trium-144 Dec 5, 2025
56dfaac
rust-8まで
na-trium-144 Dec 5, 2025
3a2b6fa
404とエラーページのカスタマイズ
na-trium-144 Dec 5, 2025
54ec68b
存在しないページについてはチャットのfetchとかをするまえにnotFoundを返す
na-trium-144 Dec 5, 2025
9ac920f
rust-12まで
na-trium-144 Dec 6, 2025
7facaaf
テストを実装
na-trium-144 Dec 6, 2025
430a619
タイトルのパース用に半角コロンで統一
na-trium-144 Dec 6, 2025
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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ npm run lint
- nを8, 10, 12, 15 など変えて何回か出力させ、それを統合していい感じの章立てを決める
- 実際にドキュメントを書かせる
> 以下の内容で`言語名`チュートリアルの第`n`章を書いてください。他の言語でのプログラミングは経験がある人を対象にします。
> タイトルにはレベル1の見出し(#), それ以降の見出しにはレベル2以下(##)を使用してください。
REPLで動作可能なコード例はスクリプトではなくREPLの実行例として書いてください。
> タイトルにはレベル1の見出し(#), それ以降の見出しにはレベル2以下(##)を使用してください。
> canvasは使わずに出力してください。
> REPLで動作可能なコード例はスクリプトではなくREPLの実行例として書いてください。
> コード例はREPLの実行例では \`\`\``言語名`-repl 、ソースファイルの場合は \`\`\``言語名`:ファイル名`.拡張子` ではじまるコードブロックで示してください。ファイル名は被らないようにしてください。
> また、ファイルの場合は \`\`\``言語名`-exec:ファイル名`.拡張子` のコードブロック内に実行結果例を記載してください。
> また、最後には この章のまとめ セクションと、練習問題を2つほど書いてください。練習問題はこの章で学んだ内容を活用してコードを書かせるものにしてください。
Expand All @@ -81,7 +82,6 @@ npm run lint
>
- Gemini出力の調整
- Canvasを使われた場合はやり直す。(Canvasはファイル名付きコードブロックで壊れる)
- 箇条書きの最後に `<!-- end list -->` と出力される場合がある。消す
- 太字がなぜか `**キーワード**` の代わりに `\*\*キーワード\*\*` となっている場合がある。 `\*\*` → `**` の置き換えで対応
- 見出しの前に `-----` (水平線)が入る場合がある。my.code();は水平線の表示に対応しているが、消す方向で統一
- `言語名-repl` にはページ内で一意なIDを追加する (例: `言語名-repl:1`)
Expand Down
7 changes: 6 additions & 1 deletion app/[docs_id]/markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Markdown, { Components, ExtraProps } from "react-markdown";
import remarkGfm from "remark-gfm";
import removeComments from "remark-remove-comments";
import remarkCjkFriendly from "remark-cjk-friendly";
import { EditorComponent, getAceLang } from "../terminal/editor";
import { ExecFile } from "../terminal/exec";
import { JSX, ReactNode } from "react";
Expand All @@ -13,7 +15,10 @@ import {

export function StyledMarkdown({ content }: { content: string }) {
return (
<Markdown remarkPlugins={[remarkGfm]} components={components}>
<Markdown
remarkPlugins={[remarkGfm, removeComments, remarkCjkFriendly]}
components={components}
>
{content}
</Markdown>
);
Expand Down
10 changes: 9 additions & 1 deletion app/[docs_id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { splitMarkdown } from "./splitMarkdown";
import { PageContent } from "./pageContent";
import { ChatHistoryProvider } from "./chatHistory";
import { getChatFromCache } from "@/lib/chatHistory";
import { getLanguageName } from "@/pagesList";
import { getLanguageName, pagesList } from "@/pagesList";

async function getMarkdownContent(docs_id: string): Promise<string> {
try {
Expand Down Expand Up @@ -61,6 +61,14 @@ export default async function Page({
}) {
const { docs_id } = await params;

if (
!pagesList
.find((lang) => docs_id.startsWith(`${lang.id}-`))
?.pages.find((page) => docs_id.endsWith(`-${page.id}`))
) {
notFound();
}

const mdContent = getMarkdownContent(docs_id);
const splitMdContent = mdContent.then((text) => splitMarkdown(text));
const initialChatHistories = getChatFromCache(docs_id);
Expand Down
4 changes: 2 additions & 2 deletions app/[docs_id]/pageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ export function PageContent(props: PageContentProps) {

return (
<div
className="p-4 mx-auto grid"
className="p-4 mx-auto max-w-full grid"
style={{
gridTemplateColumns: `1fr auto`,
}}
>
{dynamicMdContent.map((section, index) => (
<Fragment key={index}>
<div
className="max-w-200"
className="min-w-1/2 max-w-200 text-justify"
id={`${index}`} // 目次からaタグで飛ぶために必要
ref={(el) => {
sectionRefs.current[index] = el;
Expand Down
28 changes: 26 additions & 2 deletions app/[docs_id]/styledSyntaxHighlighter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,21 @@ export type MarkdownLang =
| "rb"
| "cpp"
| "c++"
| "rust"
| "rs"
| "javascript"
| "js"
| "typescript"
| "ts"
| "bash"
| "sh"
| "powershell"
| "json"
| "toml"
| "csv"
| "html"
| "makefile"
| "cmake"
| "text"
| "txt";

Expand All @@ -43,11 +49,16 @@ export type SyntaxHighlighterLang =
| "ruby"
| "c"
| "cpp"
| "rust"
| "javascript"
| "typescript"
| "bash"
| "powershell"
| "html"
| "json";
| "json"
| "ini"
| "makefile"
| "cmake";
export function getSyntaxHighlighterLang(
lang: MarkdownLang | undefined
): SyntaxHighlighterLang | undefined {
Expand All @@ -61,6 +72,9 @@ export function getSyntaxHighlighterLang(
case "cpp":
case "c++":
return "cpp";
case "rust":
case "rs":
return "rust";
case "javascript":
case "js":
return "javascript";
Expand All @@ -70,18 +84,28 @@ export function getSyntaxHighlighterLang(
case "bash":
case "sh":
return "bash";
case "powershell":
return "powershell";
case "json":
return "json";
case "toml":
return "ini";
case "html":
return "html";
case "makefile":
return "makefile";
case "cmake":
return "cmake";
case "csv": // not supported
case "text":
case "txt":
case undefined:
return undefined;
default:
lang satisfies never;
console.error(`getSyntaxHighlighterLang() does not handle language ${lang}`);
console.error(
`getSyntaxHighlighterLang() does not handle language ${lang}`
);
return undefined;
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/accountMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function AccountMenu() {
}
};

if (isPending) {
if (isPending || !session) {
return <div className="w-10 h-10 skeleton rounded-full"></div>;
}

Expand Down
47 changes: 47 additions & 0 deletions app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client"; // Error boundaries must be Client Components

import clsx from "clsx";
import Link from "next/link";

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div className="p-4 flex-1 w-max max-w-full mx-auto flex flex-col items-center justify-center">
<h1 className="text-2xl font-bold mb-4">エラー</h1>
<p>ページの読み込み中にエラーが発生しました。</p>
<pre
className={clsx(
"border-2 border-current/20 mt-4 rounded-box p-4! bg-base-300! text-base-content!",
"max-w-full whitespace-pre-wrap"
)}
>
{error.message}
</pre>
{error.digest && (
<p className="mt-2 text-sm text-base-content/50">
Digest: {error.digest}
</p>
)}
<div className="divider w-full self-auto!" />
<div className="flex flex-row gap-4">
<button
className="btn btn-warning"
onClick={
// Attempt to recover by trying to re-render the segment
() => reset()
}
>
やりなおす
</button>
<Link href="/" className="btn btn-primary">
トップに戻る
</Link>
</div>
</div>
);
}
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function RootLayout({
{/* mocha.css がbodyに背景色などを設定してしまうので、それを上書きしている */}
<AutoAnonymousLogin />
<SidebarMdProvider>
<div className="drawer lg:drawer-open">
<div className="drawer lg:drawer-open min-h-screen">
<input
id="drawer-toggle"
type="checkbox"
Expand Down
14 changes: 14 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Link from "next/link";

export default function NotFound() {
return (
<div className="p-4 flex-1 w-max max-w-full mx-auto flex flex-col items-center justify-center">
<h1 className="text-2xl font-bold mb-4">404</h1>
<p>指定されたページが見つかりません。</p>
<div className="divider w-full self-auto!" />
<Link href="/" className="btn btn-primary">
トップに戻る
</Link>
</div>
);
}
42 changes: 31 additions & 11 deletions app/pagesList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,37 @@ export const pagesList = [
description: "C++の基本から高度な機能までを学べるチュートリアル",
pages: [
{ id: 1, title: "C++の世界へようこそ" },
{ id: 2, title: "型システムとメモリ" },
{ id: 3, title: "関数と参照" },
{ id: 4, title: "ポインタと動的メモリ" },
{ id: 5, title: "クラスの基礎" },
{ id: 6, title: "クラスを使いこなす" },
{ id: 7, title: "継承とポリモーフィズム" },
{ id: 8, title: "テンプレート" },
{ id: 9, title: "STL ①:コンテナ" },
{ id: 10, title: "STL ②:アルゴリズムとラムダ式" },
{ id: 11, title: "RAIIとスマートポインタ" },
{ id: 12, title: "プロジェクトの分割とビルド" },
{ id: 2, title: "型システムと制御構造" },
{ id: 3, title: "データ集合とモダンな操作" },
{ id: 4, title: "ポインタとメモリ管理" },
{ id: 5, title: "関数と参照渡し" },
{ id: 6, title: "プロジェクトの分割とビルド" },
{ id: 7, title: "クラスの基礎" },
{ id: 8, title: "クラスを使いこなす" },
{ id: 9, title: "継承とポリモーフィズム" },
{ id: 10, title: "テンプレート" },
{ id: 11, title: "STL ①:コンテナ" },
{ id: 12, title: "STL ②:アルゴリズムとラムダ式" },
{ id: 13, title: "RAIIとスマートポインタ" },
],
},
{
id: "rust",
lang: "Rust",
description: "a",
pages: [
{ id: 1, title: "Rustの世界へようこそ" },
{ id: 2, title: "基本構文と「不変性」" },
{ id: 3, title: "関数と制御フロー" },
{ id: 4, title: "所有権" },
{ id: 5, title: "借用とスライス" },
{ id: 6, title: "構造体とメソッド構文" },
{ id: 7, title: "列挙型とパターンマッチ" },
{ id: 8, title: "モジュールシステムとパッケージ管理" },
{ id: 9, title: "コレクションと文字列" },
{ id: 10, title: "エラーハンドリング" },
{ id: 11, title: "ジェネリクスとトレイト" },
{ id: 12, title: "ライフタイム" },
],
},
] as const;
Expand Down
17 changes: 15 additions & 2 deletions app/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
} from "react";
import { DynamicMarkdownSection } from "./[docs_id]/pageContent";
import clsx from "clsx";
import { LanguageIcon } from "./terminal/icons";
import { RuntimeLang } from "./terminal/runtime";

export interface ISidebarMdContext {
loadedDocsId: string;
Expand Down Expand Up @@ -159,18 +161,29 @@ export function Sidebar() {
setDetailsOpen(newDetailsOpen);
}}
>
<summary>{group.lang}</summary>
<summary>
<LanguageIcon
className="w-4 h-4"
lang={group.id as RuntimeLang}
/>
{group.lang}
</summary>
<ul>
{group.pages.map((page) => (
<li key={page.id}>
<Link
href={`${group.id}-${page.id}`}
className={clsx(
"text-wrap text-justify",
`${group.id}-${page.id}` === currentDocsId &&
"menu-active"
)}
>
<span className="mr-0">{page.id}.</span>
<span className="w-5 text-right">
<span className="float-right">
{page.id}.
</span>
</span>
{page.title}
</Link>
{`${group.id}-${page.id}` === currentDocsId &&
Expand Down
13 changes: 9 additions & 4 deletions app/terminal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,23 +132,28 @@ EditorComponent コンポーネントを提供します。

### Worker

web worker でコードを実行する実装です。worker側のスクリプトは /public にあります。
web worker でコードを実行する実装です。
workerとの通信部分は言語によらず共通なので、それをworker/runtime.tsxで定義しています。
Contextは言語ごとに分けて(worker/pyodide.ts などで)定義しています。

Pythonの実行環境にはPyodideを使用しています。
PyodideにはKeyboardInterruptを送信する機能があるのでinterrupt()でそれを利用しています。

Rubyの実行環境にはruby.wasmを使用しています。

JavaScriptはeval()を使用しています。runFiles()のAPIだけ実装していません。
JavaScriptはeval()を使用しています。

### Wandbox

wandbox.org のAPIを利用してC++コードを実行しています。C++以外にもいろいろな言語に対応しています
wandbox.org のAPIを利用してコードを実行します

APIから利用可能なコンパイラとオプションのリストが得られるので、言語ごとにそこからオプションを選択するロジックを実装しています。

C++ではg++の中でheadでない最新のものを選択し、warningスイッチオン、boost有効、std=最新を指定しています。
また、コード実行時にシグナルハンドラーをユーザーのコードに挿入し、エラー時にスタックトレースを表示する処理とそれをjs側でパースする処理を実装しています。

Rustは最新のものを選択し、-Cdebuginfo=1を追加しています。
ユーザーのコードをモジュールとしてprog.rsのmain()から呼び出す形に変更しており、ユーザーのコードに `mod foo;` → `use super::foo;`, `fn main()` → `pub fn main()` の改変を加えています。

### TypeScript

[@typescript/vfs](https://www.npmjs.com/package/@typescript/vfs) を使用してブラウザ上でTypeScriptコードをコンパイルし、jsEvalランタイムに渡します。
Loading