@@ -5,7 +5,8 @@ import { PythonEmbeddedTerminal } from "../terminal/python/embedded";
55import { Heading } from "./section" ;
66import { type AceLang , EditorComponent } from "../terminal/editor" ;
77import { ExecFile , ExecLang } from "../terminal/exec" ;
8- import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/hljs" ;
8+ import { useChangeTheme } from "./themeToggle" ;
9+ import { tomorrow , atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs" ;
910
1011export function StyledMarkdown ( { content } : { content : string } ) {
1112 return (
@@ -15,6 +16,7 @@ export function StyledMarkdown({ content }: { content: string }) {
1516 ) ;
1617}
1718
19+
1820// TailwindCSSがh1などのタグのスタイルを消してしまうので、手動でスタイルを指定する必要がある
1921const components : Components = {
2022 h1 : ( { children } ) => < Heading level = { 1 } > { children } </ Heading > ,
@@ -33,7 +35,7 @@ const components: Components = {
3335 li : ( { node, ...props } ) => < li className = "my-1" { ...props } /> ,
3436 a : ( { node, ...props } ) => < a className = "link link-info" { ...props } /> ,
3537 strong : ( { node, ...props } ) => (
36- < strong className = "text-primary" { ...props } />
38+ < strong className = "text-primary dark:text-secondary " { ...props } />
3739 ) ,
3840 table : ( { node, ...props } ) => (
3941 < div className = "w-max max-w-full overflow-x-auto mx-auto my-2 rounded-lg border border-base-content/5 shadow-sm" >
@@ -42,130 +44,133 @@ const components: Components = {
4244 ) ,
4345 hr : ( { node, ...props } ) => < hr className = "border-primary my-4" { ...props } /> ,
4446 pre : ( { node, ...props } ) => props . children ,
45- code : ( { node, className, ref, style, ...props } ) => {
46- const match = / ^ l a n g u a g e - ( \w + ) ( - r e p l | - e x e c | - r e a d o n l y ) ? \: ? ( .+ ) ? $ / . exec (
47- className || ""
48- ) ;
49- if ( match ) {
50- if ( match [ 2 ] === "-exec" && match [ 3 ] ) {
51- /*
52- ```python-exec:main.py
47+ code : ( { node, className, ref, style, ...props } ) => < CodeComponent { ...{ node, className, ref, style, ...props } } /> ,
48+ } ;
49+ function CodeComponent ( { node, className, ref, style, ...props } : { node : unknown ; className ?: string ; ref ?: unknown ; style ?: unknown ; [ key : string ] : unknown } ) {
50+ const theme = useChangeTheme ( ) ;
51+ const codetheme = theme === "tomorrow" ? tomorrow : atomOneDark ;
52+ const match = / ^ l a n g u a g e - ( \w + ) ( - r e p l | - e x e c | - r e a d o n l y ) ? \: ? ( .+ ) ? $ / . exec (
53+ className || ""
54+ ) ;
55+ if ( match ) {
56+ if ( match [ 2 ] === "-exec" && match [ 3 ] ) {
57+ /*
58+ ```python-exec:main.py
59+ hello, world!
60+ ```
61+ ↓
62+ ---------------------------
63+ [▶ 実行] `python main.py`
5364 hello, world!
54- ```
55- ↓
56- ---------------------------
57- [▶ 実行] `python main.py`
58- hello, world!
59- ---------------------------
60- */
61- let execLang : ExecLang | undefined = undefined ;
62- switch ( match [ 1 ] ) {
63- case "python" :
64- execLang = "python" ;
65- break ;
66- case "cpp" :
67- case "c++" :
68- execLang = "cpp" ;
69- break ;
70- default :
71- console . warn ( `Unsupported language for exec: ${ match [ 1 ] } ` ) ;
72- break ;
73- }
74- if ( execLang ) {
75- return (
76- < div className = "border border-primary border-2 shadow-md m-2 rounded-lg" >
77- < ExecFile
78- language = { execLang }
79- filenames = { match [ 3 ] . split ( "," ) }
80- content = { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
81- />
82- </ div >
83- ) ;
84- }
85- } else if ( match [ 3 ] ) {
86- // ファイル名指定がある場合、ファイルエディター
87- let aceLang : AceLang | undefined = undefined ;
88- switch ( match [ 1 ] ) {
89- case "python" :
90- aceLang = "python" ;
91- break ;
92- case "cpp" :
93- case "c++" :
94- aceLang = "c_cpp" ;
95- break ;
96- case "json" :
97- aceLang = "json" ;
98- break ;
99- case "csv" :
100- aceLang = "csv" ;
101- break ;
102- case "text" :
103- case "txt" :
104- aceLang = "text" ;
105- break ;
106- default :
107- console . warn ( `Unsupported language for editor: ${ match [ 1 ] } ` ) ;
108- break ;
109- }
65+ ---------------------------
66+ */
67+ let execLang : ExecLang | undefined = undefined ;
68+ switch ( match [ 1 ] ) {
69+ case "python" :
70+ execLang = "python" ;
71+ break ;
72+ case "cpp" :
73+ case "c++" :
74+ execLang = "cpp" ;
75+ break ;
76+ default :
77+ console . warn ( `Unsupported language for exec: ${ match [ 1 ] } ` ) ;
78+ break ;
79+ }
80+ if ( execLang ) {
11081 return (
11182 < div className = "border border-primary border-2 shadow-md m-2 rounded-lg" >
112- < EditorComponent
113- language = { aceLang }
114- tabSize = { 4 }
115- filename = { match [ 3 ] }
116- readonly = { match [ 2 ] === "-readonly" }
117- initContent = { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
83+ < ExecFile
84+ language = { execLang }
85+ filenames = { match [ 3 ] . split ( "," ) }
86+ content = { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
11887 />
11988 </ div >
12089 ) ;
121- } else if ( match [ 2 ] === "-repl" ) {
122- // repl付きの言語指定
123- // 現状はPythonのみ対応
124- switch ( match [ 1 ] ) {
125- case "python" :
126- return (
127- < div className = "bg-base-300 border border-primary border-2 shadow-md m-2 p-4 pr-1 rounded-lg" >
128- < PythonEmbeddedTerminal
129- content = { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
130- />
131- </ div >
132- ) ;
133- default :
134- console . warn ( `Unsupported language for repl: ${ match [ 1 ] } ` ) ;
135- break ;
136- }
90+ }
91+ } else if ( match [ 3 ] ) {
92+ // ファイル名指定がある場合、ファイルエディター
93+ let aceLang : AceLang | undefined = undefined ;
94+ switch ( match [ 1 ] ) {
95+ case "python" :
96+ aceLang = "python" ;
97+ break ;
98+ case "cpp" :
99+ case "c++" :
100+ aceLang = "c_cpp" ;
101+ break ;
102+ case "json" :
103+ aceLang = "json" ;
104+ break ;
105+ case "csv" :
106+ aceLang = "csv" ;
107+ break ;
108+ case "text" :
109+ case "txt" :
110+ aceLang = "text" ;
111+ break ;
112+ default :
113+ console . warn ( `Unsupported language for editor: ${ match [ 1 ] } ` ) ;
114+ break ;
137115 }
138116 return (
139- < SyntaxHighlighter
140- language = { match [ 1 ] }
141- PreTag = "div"
142- className = "border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
143- style = { tomorrow } // todo dark theme (editor.tsx で指定したのと同じテーマを選ぶようにすること)
144- { ...props }
145- >
146- { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
147- </ SyntaxHighlighter >
148- ) ;
149- } else if ( String ( props . children ) . includes ( "\n" ) ) {
150- // 言語指定なしコードブロック
151- return (
152- < SyntaxHighlighter
153- PreTag = "div"
154- className = "border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
155- style = { tomorrow } // todo dark theme
156- { ...props }
157- >
158- { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
159- </ SyntaxHighlighter >
160- ) ;
161- } else {
162- // inline
163- return (
164- < code
165- className = "bg-base-200/60 border border-base-300 px-1 py-0.5 rounded text-sm "
166- { ...props }
167- />
117+ < div className = "border border-primary border-2 shadow-md m-2 rounded-lg" >
118+ < EditorComponent
119+ language = { aceLang }
120+ tabSize = { 4 }
121+ filename = { match [ 3 ] }
122+ readonly = { match [ 2 ] === "-readonly" }
123+ initContent = { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
124+ />
125+ </ div >
168126 ) ;
127+ } else if ( match [ 2 ] === "-repl" ) {
128+ // repl付きの言語指定
129+ // 現状はPythonのみ対応
130+ switch ( match [ 1 ] ) {
131+ case "python" :
132+ return (
133+ < div className = "bg-base-300 border border-primary border-2 shadow-md m-2 p-4 pr-1 rounded-lg" >
134+ < PythonEmbeddedTerminal
135+ content = { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
136+ />
137+ </ div >
138+ ) ;
139+ default :
140+ console . warn ( `Unsupported language for repl: ${ match [ 1 ] } ` ) ;
141+ break ;
142+ }
169143 }
170- } ,
171- } ;
144+ return (
145+ < SyntaxHighlighter
146+ language = { match [ 1 ] }
147+ PreTag = "div"
148+ className = "border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
149+ style = { codetheme }
150+ { ...props }
151+ >
152+ { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
153+ </ SyntaxHighlighter >
154+ ) ;
155+ } else if ( String ( props . children ) . includes ( "\n" ) ) {
156+ // 言語指定なしコードブロック
157+ return (
158+ < SyntaxHighlighter
159+ PreTag = "div"
160+ className = "border border-base-content/50 mx-2 my-2 rounded-lg text-sm p-4!"
161+ style = { codetheme }
162+ { ...props }
163+ >
164+ { String ( props . children || "" ) . replace ( / \n $ / , "" ) }
165+ </ SyntaxHighlighter >
166+ ) ;
167+ } else {
168+ // inline
169+ return (
170+ < code
171+ className = "bg-base-200/60 border border-base-300 px-1 py-0.5 rounded text-sm "
172+ { ...props }
173+ />
174+ ) ;
175+ }
176+ }
0 commit comments