diff --git a/app/[docs_id]/chatForm.tsx b/app/[docs_id]/chatForm.tsx index 256af61..feec78d 100644 --- a/app/[docs_id]/chatForm.tsx +++ b/app/[docs_id]/chatForm.tsx @@ -21,7 +21,13 @@ interface ChatFormProps { execResults: Record; } -export function ChatForm({ documentContent, sectionId, replOutputs, fileContents, execResults }: ChatFormProps) { +export function ChatForm({ + documentContent, + sectionId, + replOutputs, + fileContents, + execResults, +}: ChatFormProps) { const [messages, updateChatHistory] = useChatHistory(sectionId); const [inputValue, setInputValue] = useState(""); const [isLoading, setIsLoading] = useState(false); @@ -54,9 +60,10 @@ export function ChatForm({ documentContent, sectionId, replOutputs, fileContents updateChatHistory([userMessage]); let userQuestion = inputValue; - if(!userQuestion && exampleData){ + if (!userQuestion && exampleData) { // 質問が空欄なら、質問例を使用 - userQuestion = exampleData[Math.floor(exampleChoice * exampleData.length)]; + userQuestion = + exampleData[Math.floor(exampleChoice * exampleData.length)]; setInputValue(userQuestion); } @@ -69,7 +76,11 @@ export function ChatForm({ documentContent, sectionId, replOutputs, fileContents }); if (result.error) { - const errorMessage: Message = { sender: "ai", text: `エラー: ${result.error}`, isError: true }; + const errorMessage: Message = { + sender: "ai", + text: `エラー: ${result.error}`, + isError: true, + }; updateChatHistory([userMessage, errorMessage]); } else { const aiMessage: Message = { sender: "ai", text: result.response }; @@ -83,12 +94,20 @@ export function ChatForm({ documentContent, sectionId, replOutputs, fileContents const handleClearHistory = () => { updateChatHistory([]); }; - + return ( <> {isFormVisible && ( -
-
+ +
-
+
{messages.map((msg, index) => ( -
-
+
@@ -178,7 +211,6 @@ export function ChatForm({ documentContent, sectionId, replOutputs, fileContents AIが考え中です…
)} - ); -} \ No newline at end of file +} diff --git a/app/[docs_id]/chatServer.ts b/app/[docs_id]/chatServer.ts index 3135ec5..3a11a95 100644 --- a/app/[docs_id]/chatServer.ts +++ b/app/[docs_id]/chatServer.ts @@ -1,5 +1,3 @@ "use server"; -export async function hello() { - -} +export async function hello() {} diff --git a/app/[docs_id]/markdown.tsx b/app/[docs_id]/markdown.tsx index 7d31b97..ffdfd0a 100644 --- a/app/[docs_id]/markdown.tsx +++ b/app/[docs_id]/markdown.tsx @@ -6,7 +6,10 @@ import { Heading } from "./section"; import { type AceLang, EditorComponent } from "../terminal/editor"; import { ExecFile, ExecLang } from "../terminal/exec"; import { useChangeTheme } from "./themeToggle"; -import { tomorrow, atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs"; +import { + tomorrow, + atomOneDark, +} from "react-syntax-highlighter/dist/esm/styles/hljs"; export function StyledMarkdown({ content }: { content: string }) { return ( @@ -16,7 +19,6 @@ export function StyledMarkdown({ content }: { content: string }) { ); } - // TailwindCSSがh1などのタグのスタイルを消してしまうので、手動でスタイルを指定する必要がある const components: Components = { h1: ({ children }) => {children}, @@ -44,11 +46,25 @@ const components: Components = { ), hr: ({ node, ...props }) =>
, pre: ({ node, ...props }) => props.children, - code: ({ node, className, ref, style, ...props }) => , + code: ({ node, className, ref, style, ...props }) => ( + + ), }; -function CodeComponent({ node, className, ref, style, ...props }: { node: unknown; className?: string; ref?: unknown; style?: unknown; [key: string]: unknown }) { +function CodeComponent({ + node, + className, + ref, + style, + ...props +}: { + node: unknown; + className?: string; + ref?: unknown; + style?: unknown; + [key: string]: unknown; +}) { const theme = useChangeTheme(); - const codetheme= theme === "tomorrow" ? tomorrow : atomOneDark; + const codetheme = theme === "tomorrow" ? tomorrow : atomOneDark; const match = /^language-(\w+)(-repl|-exec|-readonly)?\:?(.+)?$/.exec( className || "" ); diff --git a/app/[docs_id]/section.tsx b/app/[docs_id]/section.tsx index d06cfdc..4aada64 100644 --- a/app/[docs_id]/section.tsx +++ b/app/[docs_id]/section.tsx @@ -73,13 +73,13 @@ export function Section({ section, sectionId }: SectionProps) {
{section.title} - +
); diff --git a/app/[docs_id]/splitMarkdown.ts b/app/[docs_id]/splitMarkdown.ts index 21dbbbf..55fc0d0 100644 --- a/app/[docs_id]/splitMarkdown.ts +++ b/app/[docs_id]/splitMarkdown.ts @@ -11,9 +11,7 @@ export interface MarkdownSection { * Markdownコンテンツを見出しごとに分割し、 * 見出しのレベルとタイトル、内容を含むオブジェクトの配列を返す。 */ -export function splitMarkdown( - content: string -): MarkdownSection[] { +export function splitMarkdown(content: string): MarkdownSection[] { const tree = unified().use(remarkParse).use(remarkGfm).parse(content); // console.log(tree.children.map(({ type, position }) => ({ type, position: JSON.stringify(position) }))); const headingNodes = tree.children.filter((node) => node.type === "heading"); diff --git a/app/[docs_id]/themeToggle.tsx b/app/[docs_id]/themeToggle.tsx index 91b5404..b9ee650 100644 --- a/app/[docs_id]/themeToggle.tsx +++ b/app/[docs_id]/themeToggle.tsx @@ -1,75 +1,73 @@ "use client"; -import { useState, useEffect} from "react"; +import { useState, useEffect } from "react"; -export function useChangeTheme(){ - const [theme, setTheme] = useState("tomorrow"); - useEffect(() => { +export function useChangeTheme() { + const [theme, setTheme] = useState("tomorrow"); + useEffect(() => { + const updateTheme = () => { + const theme = document.documentElement.getAttribute("data-theme"); + setTheme(theme === "dark" ? "twilight" : "tomorrow"); + }; - const updateTheme = () => { - const theme = document.documentElement.getAttribute("data-theme"); - setTheme(theme === "dark" ? "twilight" : "tomorrow"); - }; - - const observer = new MutationObserver(updateTheme); - observer.observe(document.documentElement, { - attributes: true, - attributeFilter: ["data-theme"], - }); - - - return () => observer.disconnect(); - }, []); - return theme; + const observer = new MutationObserver(updateTheme); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-theme"], + }); -}; + return () => observer.disconnect(); + }, []); + return theme; +} export function ThemeToggle() { const theme = useChangeTheme(); const isChecked = theme === "twilight"; useEffect(() => { const checkIsDarkSchemePreferred = () => - window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches ?? false; + window?.matchMedia?.("(prefers-color-scheme:dark)")?.matches ?? false; const initialTheme = checkIsDarkSchemePreferred() ? "dark" : "light"; document.documentElement.setAttribute("data-theme", initialTheme); }, []); - + return ( + + + + + { + const isdark = e.target.checked; + const theme = isdark ? "dark" : "light"; + document.documentElement.setAttribute("data-theme", theme); + }} + /> + + + + ); } diff --git a/app/actions/chatActions.ts b/app/actions/chatActions.ts index 773e7f5..b126e93 100644 --- a/app/actions/chatActions.ts +++ b/app/actions/chatActions.ts @@ -1,4 +1,4 @@ -'use server'; +"use server"; import { z } from "zod"; import { generateContent } from "./gemini"; @@ -9,23 +9,58 @@ interface FormState { } const ChatSchema = z.object({ - userQuestion: z.string().min(1, { message: "メッセージを入力してください。" }), - documentContent: z.string().min(1, { message: "コンテキストとなるドキュメントがありません。"}), - replOutputs: z.array(z.object({ - command: z.string(), - output: z.array(z.object({ - type: z.enum(["stdout", "stderr", "error", "return", "trace", "system"]), - message: z.string(), - })), - })).optional(), - fileContents: z.array(z.object({ - name: z.string(), - content: z.string(), - })).optional(), - execResults: z.record(z.string(), z.array(z.object({ - type: z.enum(["stdout", "stderr", "error", "return", "trace", "system"]), - message: z.string(), - }))).optional(), + userQuestion: z + .string() + .min(1, { message: "メッセージを入力してください。" }), + documentContent: z + .string() + .min(1, { message: "コンテキストとなるドキュメントがありません。" }), + replOutputs: z + .array( + z.object({ + command: z.string(), + output: z.array( + z.object({ + type: z.enum([ + "stdout", + "stderr", + "error", + "return", + "trace", + "system", + ]), + message: z.string(), + }) + ), + }) + ) + .optional(), + fileContents: z + .array( + z.object({ + name: z.string(), + content: z.string(), + }) + ) + .optional(), + execResults: z + .record( + z.string(), + z.array( + z.object({ + type: z.enum([ + "stdout", + "stderr", + "error", + "return", + "trace", + "system", + ]), + message: z.string(), + }) + ) + ) + .optional(), }); type ChatParams = z.input; @@ -39,14 +74,21 @@ export async function askAI(params: ChatParams): Promise { error: parseResult.error.issues.map((e) => e.message).join(", "), }; } - - const { userQuestion, documentContent, replOutputs, fileContents, execResults } = parseResult.data; + + const { + userQuestion, + documentContent, + replOutputs, + fileContents, + execResults, + } = parseResult.data; try { // ターミナルログの文字列を構築 let terminalLogsSection = ""; if (replOutputs && replOutputs.length > 0) { - terminalLogsSection = "\n# ターミナルのログ(ユーザーが入力したコマンドとその実行結果)\n"; + terminalLogsSection = + "\n# ターミナルのログ(ユーザーが入力したコマンドとその実行結果)\n"; for (const replCmd of replOutputs) { terminalLogsSection += `\n## コマンド: ${replCmd.command}\n`; terminalLogsSection += "```\n"; @@ -82,7 +124,7 @@ export async function askAI(params: ChatParams): Promise { execResultsSection += "```\n"; } } - + const prompt = ` 以下のPythonチュートリアルのドキュメントの内容を正確に理解し、ユーザーからの質問に対して、初心者にも分かりやすく、丁寧な解説を提供してください。 @@ -110,8 +152,11 @@ ${userQuestion} } catch (error: unknown) { console.error("Error calling Generative AI:", error); if (error instanceof Error) { - return { response: '', error: `AIへのリクエスト中にエラーが発生しました: ${error.message}` }; + return { + response: "", + error: `AIへのリクエスト中にエラーが発生しました: ${error.message}`, + }; } - return { response: '', error: '予期せぬエラーが発生しました。' }; + return { response: "", error: "予期せぬエラーが発生しました。" }; } -} \ No newline at end of file +} diff --git a/app/actions/gemini.ts b/app/actions/gemini.ts index 67d0925..f89651f 100644 --- a/app/actions/gemini.ts +++ b/app/actions/gemini.ts @@ -9,13 +9,13 @@ export async function generateContent(prompt: string) { }; const ai = new GoogleGenAI({ apiKey: process.env.API_KEY! }); - + try { return await ai.models.generateContent(params); } catch (e: unknown) { if (String(e).includes("User location is not supported")) { // For the new API, we can use httpOptions to set a custom baseUrl - const aiWithProxy = new GoogleGenAI({ + const aiWithProxy = new GoogleGenAI({ apiKey: process.env.API_KEY!, httpOptions: { baseUrl: "https://gemini-proxy.utcode.net", diff --git a/app/globals.css b/app/globals.css index 19a48e4..fc25546 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,12 +1,12 @@ @import "tailwindcss"; -@plugin "daisyui" -{ - themes: light --default, dark --prefersdark; -}; +@plugin "daisyui" { + themes: + light --default, + dark --prefersdark; +} @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); - /* CDNからダウンロードするURLを指定したらなんかエラー出るので、npmでインストールしてlayout.tsxでimportすることにした */ @theme { --font-mono: "Inconsolata Variable", "Noto Sans JP Variable", monospace; diff --git a/app/hooks/useChathistory.ts b/app/hooks/useChathistory.ts index 732fbd7..1a96c62 100644 --- a/app/hooks/useChathistory.ts +++ b/app/hooks/useChathistory.ts @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useCallback } from 'react'; +import { useState, useEffect, useCallback } from "react"; export interface Message { sender: "user" | "ai"; @@ -10,7 +10,7 @@ export interface Message { export const useChatHistory = (sectionId: string) => { const [messages, setMessages] = useState([]); - + const CHAT_HISTORY_KEY = `my-code-chat-history-${sectionId}`; useEffect(() => { @@ -27,14 +27,17 @@ export const useChatHistory = (sectionId: string) => { } }, [CHAT_HISTORY_KEY]); - const updateChatHistory = useCallback((newMessages: Message[]) => { - setMessages(newMessages); - if (newMessages.length > 0) { - localStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(newMessages)); - } else { - localStorage.removeItem(CHAT_HISTORY_KEY); - } - }, [CHAT_HISTORY_KEY]); + const updateChatHistory = useCallback( + (newMessages: Message[]) => { + setMessages(newMessages); + if (newMessages.length > 0) { + localStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(newMessages)); + } else { + localStorage.removeItem(CHAT_HISTORY_KEY); + } + }, + [CHAT_HISTORY_KEY] + ); return [messages, updateChatHistory] as const; -}; \ No newline at end of file +}; diff --git a/app/page.tsx b/app/page.tsx index 5166a77..97da6d4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -4,11 +4,23 @@ import { pagesList } from "./pagesList"; export default function Home() { return (
-

my.code(); へようこそ

-

- my.code(); - はプログラミング言語のチュートリアルを提供するウェブサイトです。 -

+
+
+
+

my.code(); へようこそ

+

+ my.code(); + はプログラミング言語のチュートリアルを提供するウェブサイトです。 +

+

+ ブラウザ上で動かせる実行環境とAIアシスタントで、あなたの学習を強力にサポートします。 +

+
+
+
+

+ チュートリアルを選ぶ +

{pagesList.map((group) => { return ( -

{group.lang}

{group.description}

- - はじめる - +
はじめる
-
+ ); })}
-

主な特徴

- {/* TODO: デザインがダサい */} -
    -
  • - 豊富なチュートリアル -

    - my.code(); - ではさまざまなプログラミング言語やフレームワークのチュートリアルを提供しています。 - 初心者向けの基礎から上級者向けの応用まで、幅広いレベルに対応したチュートリアルが揃っています。 - {/* ほんまか? */} -

    -
  • -
  • - すぐに動かせる実行環境 -

    - my.code(); - ではブラウザ上でコードを実行できる環境を整備しており、環境構築の手間なくすぐにコードを実行することができます。 - チュートリアル内のサンプルコードはそのまま実行するだけでなく、自由に編集して試すことも可能です。 -

    -
  • -
  • - AIアシスタントによるサポート -

    - my.code(); ではAIアシスタントが学習をサポートします。 - チュートリアルを読んでいてわからないことがあれば、AIアシスタントに質問してみてください。 - さらに、実行したサンプルコードの解説やエラーの原因調査、改善提案まで、AIアシスタントがあなたの学習を強力に支援します。 -

    -
  • -
  • - 実践的な練習問題 -

    - 各チュートリアルには練習問題が含まれており、学んだ内容を実際に試すことができます。 - 練習問題は段階的に難易度が上がるように設計されており、理解度を深めるのに役立ちます。 - 書いたコードはその場ですぐにAIアシスタントがレビューし、フィードバックを提供します。 -

    -
  • -
+

主な特徴

+
+
+
+

📚 豊富なチュートリアル

+

+ my.code(); + ではさまざまなプログラミング言語やフレームワークのチュートリアルを提供しています。 + 初心者向けの基礎から上級者向けの応用まで、幅広いレベルに対応したチュートリアルが揃っています。 +

+
+
+
+
+

+ ⚡ すぐに動かせる実行環境 +

+

+ my.code(); + ではブラウザ上でコードを実行できる環境を整備しており、環境構築の手間なくすぐにコードを実行することができます。 + チュートリアル内のサンプルコードはそのまま実行するだけでなく、自由に編集して試すことも可能です。 +

+
+
+
+
+

+ 🤖 AIアシスタントによるサポート +

+

+ my.code(); ではAIアシスタントが学習をサポートします。 + チュートリアルを読んでいてわからないことがあれば、AIアシスタントに質問してみてください。 + さらに、実行したサンプルコードの解説やエラーの原因調査、改善提案まで、AIアシスタントがあなたの学習を強力に支援します。 +

+
+
+
+
+

✏️ 実践的な練習問題

+

+ 各チュートリアルには練習問題が含まれており、学んだ内容を実際に試すことができます。 + 練習問題は段階的に難易度が上がるように設計されており、理解度を深めるのに役立ちます。 + 書いたコードはその場ですぐにAIアシスタントがレビューし、フィードバックを提供します。 +

+
+
+
); } diff --git a/app/pagesList.ts b/app/pagesList.ts index 442b2fa..7df1274 100644 --- a/app/pagesList.ts +++ b/app/pagesList.ts @@ -34,7 +34,7 @@ export const pagesList = [ { id: 7, title: "継承とポリモーフィズム" }, { id: 8, title: "テンプレート" }, { id: 9, title: "STL ①:コンテナ" }, - { id: 10, title: "STL ②:アルゴリズムとラムダ式"}, + { id: 10, title: "STL ②:アルゴリズムとラムダ式" }, { id: 11, title: "RAIIとスマートポインタ" }, { id: 12, title: "プロジェクトの分割とビルド" }, ], @@ -42,10 +42,10 @@ export const pagesList = [ ] as const; // ${lang_id}-${page_id} から言語名を取得 -export function getLanguageName(docs_id: string){ +export function getLanguageName(docs_id: string) { const lang_id = docs_id.split("-")[0]; const lang = pagesList.find((lang) => lang.id === lang_id)?.lang; - if(!lang){ + if (!lang) { throw new Error(`Unknown language id: ${lang_id}`); } return lang; diff --git a/app/sidebar.tsx b/app/sidebar.tsx index 5c9d77b..2107648 100644 --- a/app/sidebar.tsx +++ b/app/sidebar.tsx @@ -13,7 +13,7 @@ export function Sidebar() { const pathname = usePathname(); const docs_id = pathname.replace(/^\//, ""); const { data, error, isLoading } = useSWR(`/docs/${docs_id}.md`, fetcher); - + if (error) console.error("Sidebar fetch error:", error); const splitmdcontent = splitMarkdown(data ?? ""); @@ -26,8 +26,6 @@ export function Sidebar() { - -
    {pagesList.map((group) => (
  • diff --git a/app/terminal/editor.css b/app/terminal/editor.css index 7a77dda..e50868b 100644 --- a/app/terminal/editor.css +++ b/app/terminal/editor.css @@ -1,11 +1,11 @@ .embedded-editor > .ace_gutter { - border-bottom-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; } .embedded-editor > .ace_scroller { - border-bottom-right-radius: 0.5rem; - border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; + border-top-right-radius: 0.5rem; } .embedded-editor > .ace_editor { - border-bottom-left-radius: 0.5rem; - border-bottom-right-radius: 0.5rem; -} \ No newline at end of file + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} diff --git a/app/terminal/editor.tsx b/app/terminal/editor.tsx index 93afa9f..bed41ed 100644 --- a/app/terminal/editor.tsx +++ b/app/terminal/editor.tsx @@ -2,20 +2,23 @@ import dynamic from "next/dynamic"; // https://github.com/securingsincity/react-ace/issues/27 により普通のimportができない -const AceEditor = dynamic(async () => { - const ace = await import("react-ace"); - // テーマは色分けが今のTerminal側のハイライト(highlight.js)の実装に近いものを適当に選んだ - await import("ace-builds/src-min-noconflict/theme-tomorrow"); - await import("ace-builds/src-min-noconflict/theme-twilight"); - await import("ace-builds/src-min-noconflict/ext-language_tools"); - await import("ace-builds/src-min-noconflict/ext-searchbox"); - await import("ace-builds/src-min-noconflict/mode-python"); - await import("ace-builds/src-min-noconflict/mode-c_cpp"); - await import("ace-builds/src-min-noconflict/mode-json"); - await import("ace-builds/src-min-noconflict/mode-csv"); - await import("ace-builds/src-min-noconflict/mode-text"); - return ace; -}, { ssr: false }); +const AceEditor = dynamic( + async () => { + const ace = await import("react-ace"); + // テーマは色分けが今のTerminal側のハイライト(highlight.js)の実装に近いものを適当に選んだ + await import("ace-builds/src-min-noconflict/theme-tomorrow"); + await import("ace-builds/src-min-noconflict/theme-twilight"); + await import("ace-builds/src-min-noconflict/ext-language_tools"); + await import("ace-builds/src-min-noconflict/ext-searchbox"); + await import("ace-builds/src-min-noconflict/mode-python"); + await import("ace-builds/src-min-noconflict/mode-c_cpp"); + await import("ace-builds/src-min-noconflict/mode-json"); + await import("ace-builds/src-min-noconflict/mode-csv"); + await import("ace-builds/src-min-noconflict/mode-text"); + return ace; + }, + { ssr: false } +); import "./editor.css"; import { useFile } from "./file"; import { useEffect } from "react"; @@ -35,7 +38,7 @@ interface EditorProps { readonly?: boolean; } export function EditorComponent(props: EditorProps) { - const theme= useChangeTheme(); + const theme = useChangeTheme(); const { files, writeFile } = useFile(); const code = files[props.filename] || props.initContent; const sectionContext = useSectionCode(); diff --git a/app/terminal/file.tsx b/app/terminal/file.tsx index a4d6801..1fcd476 100644 --- a/app/terminal/file.tsx +++ b/app/terminal/file.tsx @@ -42,7 +42,7 @@ export function FileProvider({ children }: { children: ReactNode }) { if (files[pathname][name] !== content) { files[pathname][name] = content; // Reactが変更を検知できるようfiles[pathname]をコピーした別オブジェクトに置き換え - files[pathname] = {...files[pathname]}; + files[pathname] = { ...files[pathname] }; return { ...files }; } else { return files; diff --git a/app/terminal/python/pyodide.tsx b/app/terminal/python/pyodide.tsx index 1fb34b7..3f75202 100644 --- a/app/terminal/python/pyodide.tsx +++ b/app/terminal/python/pyodide.tsx @@ -209,9 +209,9 @@ export function PyodideProvider({ children }: { children: ReactNode }) { }); } } - + const pyReadFile = pyodide.runPython(READALLFILE_CODE) as PyCallable; - for(const [file, content] of pyReadFile() as [string, string][]){ + for (const [file, content] of pyReadFile() as [string, string][]) { writeFile(file, content); } @@ -280,10 +280,10 @@ export function PyodideProvider({ children }: { children: ReactNode }) { } const pyReadFile = pyodide.runPython(READALLFILE_CODE) as PyCallable; - for(const [file, content] of pyReadFile() as [string, string][]){ + for (const [file, content] of pyReadFile() as [string, string][]) { writeFile(file, content); } - + const output = [...pyodideOutput.current]; pyodideOutput.current = []; // 出力をクリア return output; diff --git a/app/terminal/terminal.tsx b/app/terminal/terminal.tsx index f433144..aaf2b8a 100644 --- a/app/terminal/terminal.tsx +++ b/app/terminal/terminal.tsx @@ -67,7 +67,7 @@ export function useTerminal(props: TerminalProps) { getRowsRef.current = props.getRows; const onReadyRef = useRef<() => void>(undefined); onReadyRef.current = props.onReady; - + // ターミナルの初期化処理 useEffect(() => { const abortController = new AbortController(); @@ -124,7 +124,11 @@ export function useTerminal(props: TerminalProps) { // https://github.com/xtermjs/xterm.js/issues/2478 // my.code();ではCtrl+Cでのkeyboardinterruptは要らないので、コピーペーストに置き換えてしまう term.attachCustomKeyEventHandler((arg) => { - if (arg.ctrlKey && (arg.key === "c" || arg.key === "x") && arg.type === "keydown") { + if ( + arg.ctrlKey && + (arg.key === "c" || arg.key === "x") && + arg.type === "keydown" + ) { const selection = term.getSelection(); if (selection) { navigator.clipboard.writeText(selection); @@ -168,19 +172,20 @@ export function useTerminal(props: TerminalProps) { // テーマが変わったときにterminalのテーマを更新する useEffect(() => { - if (terminalInstanceRef.current) { - const fromCSS = (varName: string) => - window.getComputedStyle(document.body).getPropertyValue(varName); - - terminalInstanceRef.current.options = ({ - theme: { - background: fromCSS(theme === "tomorrow" ? "--color-base-300" : "--color-neutral-900"), - foreground: fromCSS("--color-base-content") - } - }); - } -}, [theme]); - + if (terminalInstanceRef.current) { + const fromCSS = (varName: string) => + window.getComputedStyle(document.body).getPropertyValue(varName); + + terminalInstanceRef.current.options = { + theme: { + background: fromCSS( + theme === "tomorrow" ? "--color-base-300" : "--color-neutral-900" + ), + foreground: fromCSS("--color-base-content"), + }, + }; + } + }, [theme]); return { terminalRef, terminalInstanceRef, termReady }; } diff --git a/app/terminal/wandbox/wandbox.tsx b/app/terminal/wandbox/wandbox.tsx index 2abcdb3..1ff1468 100644 --- a/app/terminal/wandbox/wandbox.tsx +++ b/app/terminal/wandbox/wandbox.tsx @@ -56,7 +56,7 @@ export function WandboxProvider({ children }: { children: ReactNode }) { { type: "error" as const, message: "Wandbox is not ready yet." }, ]; } - console.log(files) + console.log(files); switch (lang) { case "C++": return cppRunFiles(compilerList, files, filenames); diff --git a/package-lock.json b/package-lock.json index 5f7ce77..af06a38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "eslint-config-next": "<15.4", "prettier": "^3.6.2", "tailwindcss": "^4", - "typescript": "^5", + "typescript": "5.9.3", "wrangler": "^4.27.0" } }, @@ -11421,17 +11421,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", + "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/type-utils": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -11445,9 +11445,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.38.0", + "@typescript-eslint/parser": "^8.46.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -11461,16 +11461,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz", + "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4" }, "engines": { @@ -11482,18 +11482,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", + "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.46.0", + "@typescript-eslint/types": "^8.46.0", "debug": "^4.3.4" }, "engines": { @@ -11504,18 +11504,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", + "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11526,9 +11526,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", + "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", "dev": true, "license": "MIT", "engines": { @@ -11539,19 +11539,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", + "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -11564,13 +11564,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", "dev": true, "license": "MIT", "engines": { @@ -11582,16 +11582,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", + "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.46.0", + "@typescript-eslint/tsconfig-utils": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -11607,7 +11607,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -11667,16 +11667,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz", + "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11687,17 +11687,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", + "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.46.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -19892,9 +19892,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 1bd23f1..b36aac4 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "eslint-config-next": "<15.4", "prettier": "^3.6.2", "tailwindcss": "^4", - "typescript": "^5", + "typescript": "5.9.3", "wrangler": "^4.27.0" } }