Skip to content

Commit 48ad6bb

Browse files
committed
テーマ切り替えのコード修正
1 parent f72a1f9 commit 48ad6bb

File tree

6 files changed

+156
-137
lines changed

6 files changed

+156
-137
lines changed

app/[docs_id]/markdown.tsx

Lines changed: 121 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { PythonEmbeddedTerminal } from "../terminal/python/embedded";
55
import { Heading } from "./section";
66
import { type AceLang, EditorComponent } from "../terminal/editor";
77
import { ExecFile, ExecLang } from "../terminal/exec";
8-
import { ChangeTheme } from "./themeToggle";
8+
import { useChangeTheme } from "./themeToggle";
99
import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/hljs";
1010
import { twilight } from "react-syntax-highlighter/dist/esm/styles/prism";
1111

@@ -45,130 +45,133 @@ const components: Components = {
4545
),
4646
hr: ({ node, ...props }) => <hr className="border-primary my-4" {...props} />,
4747
pre: ({ node, ...props }) => props.children,
48-
code: ({ node, className, ref, style, ...props }) => {
49-
const match = /^language-(\w+)(-repl|-exec|-readonly)?\:?(.+)?$/.exec(
50-
className || ""
51-
);
52-
if (match) {
53-
if (match[2] === "-exec" && match[3]) {
54-
/*
55-
```python-exec:main.py
48+
code: ({ node, className, ref, style, ...props }) => <CodeComponent {...{ node, className, ref, style, ...props }} />,
49+
};
50+
function CodeComponent({ node, className, ref, style, ...props }: { node: any; className?: string; ref?: any; style?: any; [key: string]: any }) {
51+
const theme = useChangeTheme();
52+
const codetheme= theme === "tomorrow" ? tomorrow : twilight;
53+
const match = /^language-(\w+)(-repl|-exec|-readonly)?\:?(.+)?$/.exec(
54+
className || ""
55+
);
56+
if (match) {
57+
if (match[2] === "-exec" && match[3]) {
58+
/*
59+
```python-exec:main.py
60+
hello, world!
61+
```
62+
63+
---------------------------
64+
[▶ 実行] `python main.py`
5665
hello, world!
57-
```
58-
59-
---------------------------
60-
[▶ 実行] `python main.py`
61-
hello, world!
62-
---------------------------
63-
*/
64-
let execLang: ExecLang | undefined = undefined;
65-
switch (match[1]) {
66-
case "python":
67-
execLang = "python";
68-
break;
69-
case "cpp":
70-
case "c++":
71-
execLang = "cpp";
72-
break;
73-
default:
74-
console.warn(`Unsupported language for exec: ${match[1]}`);
75-
break;
76-
}
77-
if (execLang) {
78-
return (
79-
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
80-
<ExecFile
81-
language={execLang}
82-
filenames={match[3].split(",")}
83-
content={String(props.children || "").replace(/\n$/, "")}
84-
/>
85-
</div>
86-
);
87-
}
88-
} else if (match[3]) {
89-
// ファイル名指定がある場合、ファイルエディター
90-
let aceLang: AceLang | undefined = undefined;
91-
switch (match[1]) {
92-
case "python":
93-
aceLang = "python";
94-
break;
95-
case "cpp":
96-
case "c++":
97-
aceLang = "c_cpp";
98-
break;
99-
case "json":
100-
aceLang = "json";
101-
break;
102-
case "csv":
103-
aceLang = "csv";
104-
break;
105-
case "text":
106-
case "txt":
107-
aceLang = "text";
108-
break;
109-
default:
110-
console.warn(`Unsupported language for editor: ${match[1]}`);
111-
break;
112-
}
66+
---------------------------
67+
*/
68+
let execLang: ExecLang | undefined = undefined;
69+
switch (match[1]) {
70+
case "python":
71+
execLang = "python";
72+
break;
73+
case "cpp":
74+
case "c++":
75+
execLang = "cpp";
76+
break;
77+
default:
78+
console.warn(`Unsupported language for exec: ${match[1]}`);
79+
break;
80+
}
81+
if (execLang) {
11382
return (
11483
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
115-
<EditorComponent
116-
language={aceLang}
117-
tabSize={4}
118-
filename={match[3]}
119-
readonly={match[2] === "-readonly"}
120-
initContent={String(props.children || "").replace(/\n$/, "")}
84+
<ExecFile
85+
language={execLang}
86+
filenames={match[3].split(",")}
87+
content={String(props.children || "").replace(/\n$/, "")}
12188
/>
12289
</div>
12390
);
124-
} else if (match[2] === "-repl") {
125-
// repl付きの言語指定
126-
// 現状はPythonのみ対応
127-
switch (match[1]) {
128-
case "python":
129-
return (
130-
<div className="bg-base-300 border border-primary border-2 shadow-md m-2 p-4 pr-1 rounded-lg">
131-
<PythonEmbeddedTerminal
132-
content={String(props.children || "").replace(/\n$/, "")}
133-
/>
134-
</div>
135-
);
136-
default:
137-
console.warn(`Unsupported language for repl: ${match[1]}`);
138-
break;
139-
}
91+
}
92+
} else if (match[3]) {
93+
// ファイル名指定がある場合、ファイルエディター
94+
let aceLang: AceLang | undefined = undefined;
95+
switch (match[1]) {
96+
case "python":
97+
aceLang = "python";
98+
break;
99+
case "cpp":
100+
case "c++":
101+
aceLang = "c_cpp";
102+
break;
103+
case "json":
104+
aceLang = "json";
105+
break;
106+
case "csv":
107+
aceLang = "csv";
108+
break;
109+
case "text":
110+
case "txt":
111+
aceLang = "text";
112+
break;
113+
default:
114+
console.warn(`Unsupported language for editor: ${match[1]}`);
115+
break;
140116
}
141117
return (
142-
<SyntaxHighlighter
143-
language={match[1]}
144-
PreTag="div"
145-
className="border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
146-
style={tomorrow} // todo dark theme (editor.tsx で指定したのと同じテーマを選ぶようにすること)
147-
{...props}
148-
>
149-
{String(props.children || "").replace(/\n$/, "")}
150-
</SyntaxHighlighter>
151-
);
152-
} else if (String(props.children).includes("\n")) {
153-
// 言語指定なしコードブロック
154-
return (
155-
<SyntaxHighlighter
156-
PreTag="div"
157-
className="border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
158-
style={tomorrow} // todo dark theme
159-
{...props}
160-
>
161-
{String(props.children || "").replace(/\n$/, "")}
162-
</SyntaxHighlighter>
163-
);
164-
} else {
165-
// inline
166-
return (
167-
<code
168-
className="bg-base-200/60 border border-base-300 px-1 py-0.5 rounded text-sm "
169-
{...props}
170-
/>
118+
<div className="border border-primary border-2 shadow-md m-2 rounded-lg">
119+
<EditorComponent
120+
language={aceLang}
121+
tabSize={4}
122+
filename={match[3]}
123+
readonly={match[2] === "-readonly"}
124+
initContent={String(props.children || "").replace(/\n$/, "")}
125+
/>
126+
</div>
171127
);
128+
} else if (match[2] === "-repl") {
129+
// repl付きの言語指定
130+
// 現状はPythonのみ対応
131+
switch (match[1]) {
132+
case "python":
133+
return (
134+
<div className="bg-base-300 border border-primary border-2 shadow-md m-2 p-4 pr-1 rounded-lg">
135+
<PythonEmbeddedTerminal
136+
content={String(props.children || "").replace(/\n$/, "")}
137+
/>
138+
</div>
139+
);
140+
default:
141+
console.warn(`Unsupported language for repl: ${match[1]}`);
142+
break;
143+
}
172144
}
173-
},
174-
};
145+
return (
146+
<SyntaxHighlighter
147+
language={match[1]}
148+
PreTag="div"
149+
className="border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
150+
style={codetheme}
151+
{...props}
152+
>
153+
{String(props.children || "").replace(/\n$/, "")}
154+
</SyntaxHighlighter>
155+
);
156+
} else if (String(props.children).includes("\n")) {
157+
// 言語指定なしコードブロック
158+
return (
159+
<SyntaxHighlighter
160+
PreTag="div"
161+
className="border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
162+
style={codetheme}
163+
{...props}
164+
>
165+
{String(props.children || "").replace(/\n$/, "")}
166+
</SyntaxHighlighter>
167+
);
168+
} else {
169+
// inline
170+
return (
171+
<code
172+
className="bg-base-200/60 border border-base-300 px-1 py-0.5 rounded text-sm "
173+
{...props}
174+
/>
175+
);
176+
}
177+
}

app/[docs_id]/themeToggle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22
import { useState, useEffect } from "react";
33

4-
export function ChangeTheme(){
4+
export function useChangeTheme(){
55
const [theme, setTheme] = useState("tomorrow");
66
useEffect(() => {
77
const updateTheme = () => {

app/terminal/editor.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { useFile } from "./file";
2121
import { useEffect } from "react";
2222
import { useSectionCode } from "../[docs_id]/section";
2323
import clsx from "clsx";
24-
import { ChangeTheme } from "../[docs_id]/themeToggle";
24+
import { useChangeTheme } from "../[docs_id]/themeToggle";
2525
// snippetを有効化するにはsnippetもimportする必要がある: import "ace-builds/src-min-noconflict/snippets/python";
2626

2727
// mode-xxxx.js のファイル名と、AceEditorの mode プロパティの値が対応する
@@ -87,7 +87,7 @@ export function EditorComponent(props: EditorProps) {
8787
<AceEditor
8888
name={`ace-editor-${props.filename}`}
8989
mode={props.language}
90-
theme={ChangeTheme()}
90+
theme={useChangeTheme()}
9191
tabSize={props.tabSize}
9292
width="100%"
9393
height={

app/terminal/terminal.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"use client";
22

3-
import { useEffect, useRef, useState } from "react";
3+
import { use, useEffect, useRef, useState } from "react";
44
import { Terminal } from "@xterm/xterm";
55
import { FitAddon } from "@xterm/addon-fit";
66
import "@xterm/xterm/css/xterm.css";
77
import chalk from "chalk";
8-
8+
import { useChangeTheme } from "../[docs_id]/themeToggle";
9+
import { th } from "zod/locales";
910
/**
1011
* 文字列の幅を計算する。
1112
* 厳密にやるなら @xterm/xterm/src/common/input/UnicodeV6.ts を使うとよさそう
@@ -61,16 +62,15 @@ export function useTerminal(props: TerminalProps) {
6162
const terminalInstanceRef = useRef<Terminal | null>(null);
6263
const fitAddonRef = useRef<FitAddon | null>(null);
6364
const [termReady, setTermReady] = useState<boolean>(false);
64-
65+
const theme = useChangeTheme();
6566
const getRowsRef = useRef<(cols: number) => number>(undefined);
6667
getRowsRef.current = props.getRows;
6768
const onReadyRef = useRef<() => void>(undefined);
6869
onReadyRef.current = props.onReady;
69-
70+
7071
// ターミナルの初期化処理
7172
useEffect(() => {
7273
const abortController = new AbortController();
73-
7474
// globals.cssでフォントを指定し読み込んでいるが、
7575
// それが読み込まれる前にterminalを初期化してしまうとバグる。
7676
document.fonts.load("0.875rem Inconsolata Variable").then(() => {
@@ -150,5 +150,21 @@ export function useTerminal(props: TerminalProps) {
150150
};
151151
}, []);
152152

153+
// テーマが変わったときにterminalのテーマを更新する
154+
useEffect(() => {
155+
if (terminalInstanceRef.current) {
156+
const fromCSS = (varName: string) =>
157+
window.getComputedStyle(document.body).getPropertyValue(varName);
158+
159+
terminalInstanceRef.current.options = ({
160+
theme: {
161+
background: fromCSS(theme === "tomorrow" ? "--color-base-300" : "--color-neutral-900"),
162+
foreground: fromCSS("--color-base-content")
163+
}
164+
});
165+
}
166+
}, [theme]);
167+
168+
153169
return { terminalRef, terminalInstanceRef, termReady };
154170
}

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)