From 0ec51719f4b38826cfa7e0417063d8722544ddc4 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Fri, 28 Aug 2026 19:58:28 +0000 Subject: [PATCH 1/5] =?UTF-8?q?unsandbox=E3=82=92=E4=BD=BF=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/runtime-tests.yml | 18 +++- README.md | 21 ++++ app/(docs)/@chat/chat/[chatId]/chatArea.tsx | 11 +- app/(docs)/@chat/chat/[chatId]/page.tsx | 6 +- app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx | 14 +-- app/(docs)/@docs/[lang]/[pageId]/page.tsx | 1 - .../@docs/[lang]/[pageId]/pageTransition.tsx | 22 +++- app/(docs)/useSendChat.ts | 20 ++-- app/about/ai/page.tsx | 3 +- app/api/chat/regenerate-section/route.ts | 17 +-- app/layout.tsx | 40 +++---- app/lib/chatGenerator.ts | 5 +- app/lib/chatHistory.ts | 26 ++--- app/lib/gemini.ts | 24 +++-- app/markdown/markdown.tsx | 2 +- app/markdown/remarkTerm.ts | 2 +- app/navbar.tsx | 21 ++-- app/schema/auth.ts | 6 +- app/schema/chat.ts | 5 +- app/terminal/editor.tsx | 5 +- app/terminal/embedContext.tsx | 6 +- next.config.ts | 1 + package.json | 1 + packages/jsEval/tests/console.spec.ts | 6 +- packages/runtime/README.md | 55 +++++----- packages/runtime/src/typescript/runtime.tsx | 101 +++++++++--------- packages/runtime/src/wandbox/api.ts | 44 ++++---- packages/runtime/src/wandbox/runtime.tsx | 9 +- packages/runtime/src/worker/jsEval.ts | 7 +- packages/runtime/src/worker/pyodide.ts | 7 +- packages/runtime/src/worker/ruby.ts | 7 +- packages/runtime/src/worker/ruby.worker.ts | 14 ++- packages/runtime/src/worker/runtime.tsx | 32 +++--- packages/runtime/vitest.config.ts | 3 + pnpm-lock.yaml | 29 +++++ pnpm-workspace.yaml | 1 + 36 files changed, 360 insertions(+), 232 deletions(-) diff --git a/.github/workflows/runtime-tests.yml b/.github/workflows/runtime-tests.yml index ae94c842..44d7848e 100644 --- a/.github/workflows/runtime-tests.yml +++ b/.github/workflows/runtime-tests.yml @@ -18,4 +18,20 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'pnpm' - run: pnpm install --frozen-lockfile - - run: pnpm run --filter @my-code/runtime test + - name: Install Boost + run: sudo apt-get update && sudo apt-get install -y libboost-dev + - name: Start unsandbox + run: | + pnpm exec unsandbox --port 3055 & + for i in {1..30}; do + if curl -s http://localhost:3055/api/list.json > /dev/null; then + echo "unsandbox is ready" + break + fi + echo "Waiting for unsandbox..." + sleep 1 + done + - name: Run runtime tests + env: + WANDBOX_URL: http://localhost:3055 + run: pnpm run --filter @my-code/runtime test diff --git a/README.md b/README.md index 622b16a0..a8b9e01d 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ SENTRY_PROJECT= SENTRY_URL= SENTRY_AUTH_TOKEN= SENTRY_DSN= +# WANDBOX_URL=http://localhost:3055 # C++/Rustのローカル実行用(任意) ``` * チャット用にGeminiのAPIキーまたはOpenRouterのAPIキーのいずれかが必要です。未設定の場合チャットが使えません @@ -71,6 +72,26 @@ pnpm run lint ``` でコードをチェックします。出てくるwarningやerrorはできるだけ直しましょう。 +### C++ / Rust のコード実行環境 (unsandbox) + +C++ および Rust のコード実行は、デフォルトでは [Wandbox](https://wandbox.org/) の公開 API を使用しますが、ローカルに [unsandbox](https://github.com/ut-code/unsandbox) を立ち上げて実行することも可能です(ローカル実行のほうが高速です)。 + +```bash +# unsandbox を起動(例: ポート 3055) +pnpm exec unsandbox --port 3055 +``` + +`.env` または `.env.local` に以下を設定すると、Next.js 開発サーバーからローカルの unsandbox を利用できます。 + +```dotenv +WANDBOX_URL=http://localhost:3055 +``` + +※ テスト実行時のみ切り替える場合は環境変数を直接指定して実行できます。 +```bash +WANDBOX_URL=http://localhost:3055 pnpm --filter @my-code/runtime test +``` + ### データベースのスキーマ * データベースのスキーマ(./app/schema/hoge.ts)を編集した場合、 `pnpm run db-generate` (`pnpm exec drizzle-kit generate`) でmigrationファイルを作成し、 `pnpm run db-migrate` (`pnpm exec drizzle-kit migrate`) でデータベースに反映します。 diff --git a/app/(docs)/@chat/chat/[chatId]/chatArea.tsx b/app/(docs)/@chat/chat/[chatId]/chatArea.tsx index 2e1d6af6..4cd3a01f 100644 --- a/app/(docs)/@chat/chat/[chatId]/chatArea.tsx +++ b/app/(docs)/@chat/chat/[chatId]/chatArea.tsx @@ -5,7 +5,12 @@ import { useStreamingChatContext } from "@/(docs)/streamingChatContext"; import { useSendChat } from "@/(docs)/useSendChat"; import { deleteChatAction } from "@/actions/deleteChat"; import { ChatWithMessages } from "@/lib/chatHistory"; -import { DynamicMarkdownSection, LangId, MarkdownSection, PageSlug } from "@/lib/docs"; +import { + DynamicMarkdownSection, + LangId, + MarkdownSection, + PageSlug, +} from "@/lib/docs"; import { Heading } from "@/markdown/heading"; import { StyledMarkdown } from "@/markdown/markdown"; import { usePagesListForLang } from "@/pagesListContext"; @@ -147,7 +152,9 @@ export function ChatAreaContent(props: Props) { {targetSection?.title && targetSection.title !== "sandbox" && (
  • - + {targetSection.title}
  • diff --git a/app/(docs)/@chat/chat/[chatId]/page.tsx b/app/(docs)/@chat/chat/[chatId]/page.tsx index de25ff13..a7a88d00 100644 --- a/app/(docs)/@chat/chat/[chatId]/page.tsx +++ b/app/(docs)/@chat/chat/[chatId]/page.tsx @@ -6,11 +6,7 @@ import { getChatOne, initContext, } from "@/lib/chatHistory"; -import { - getMarkdownSections, - LangId, - PageSlug, -} from "@/lib/docs"; +import { getMarkdownSections, LangId, PageSlug } from "@/lib/docs"; import { ChatAreaContainer, ChatAreaContent } from "./chatArea"; import { cacheLife, cacheTag } from "next/cache"; import { isCloudflare } from "@/lib/detectCloudflare"; diff --git a/app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx b/app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx index f77ee613..31e104d2 100644 --- a/app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx +++ b/app/(docs)/@docs/[lang]/[pageId]/chatForm.tsx @@ -1,11 +1,6 @@ "use client"; -import { - useState, - FormEvent, - useEffect, - useMemo, -} from "react"; +import { useState, FormEvent, useEffect, useMemo } from "react"; // import useSWR from "swr"; // import { // getQuestionExample, @@ -23,7 +18,12 @@ interface ChatFormProps { close: () => void; } -export function ChatForm({ path, langName, sectionContent, close }: ChatFormProps) { +export function ChatForm({ + path, + langName, + sectionContent, + close, +}: ChatFormProps) { // const [messages, updateChatHistory] = useChatHistory(sectionId); const [inputValue, setInputValue] = useState(""); const [questionScope, setQuestionScope] = useState<"page" | "language">( diff --git a/app/(docs)/@docs/[lang]/[pageId]/page.tsx b/app/(docs)/@docs/[lang]/[pageId]/page.tsx index b0e5d67e..7ff1cacc 100644 --- a/app/(docs)/@docs/[lang]/[pageId]/page.tsx +++ b/app/(docs)/@docs/[lang]/[pageId]/page.tsx @@ -88,4 +88,3 @@ export default async function Page({ ); } - diff --git a/app/(docs)/@docs/[lang]/[pageId]/pageTransition.tsx b/app/(docs)/@docs/[lang]/[pageId]/pageTransition.tsx index f3d21713..37ccbea9 100644 --- a/app/(docs)/@docs/[lang]/[pageId]/pageTransition.tsx +++ b/app/(docs)/@docs/[lang]/[pageId]/pageTransition.tsx @@ -7,7 +7,11 @@ interface PageTransitionProps { nextPage?: PageEntry; } -export function PageTransition({ lang, prevPage, nextPage }: PageTransitionProps) { +export function PageTransition({ + lang, + prevPage, + nextPage, +}: PageTransitionProps) { return (
    @@ -16,8 +20,12 @@ export function PageTransition({ lang, prevPage, nextPage }: PageTransitionProps href={`/${lang}/${prevPage.slug}`} className="group flex-1 btn btn-ghost border-2 border-base-content/20 hover:border-primary flex flex-col items-start h-auto py-2 normal-case text-left" > - 前のページ - « {prevPage.name} + + 前のページ + + + « {prevPage.name} + )}
    @@ -27,8 +35,12 @@ export function PageTransition({ lang, prevPage, nextPage }: PageTransitionProps href={`/${lang}/${nextPage.slug}`} className="group flex-1 btn btn-ghost border-2 border-base-content/20 hover:border-primary flex flex-col items-end h-auto py-2 normal-case text-right" > - 次のページ - {nextPage.name} » + + 次のページ + + + {nextPage.name} » + )}
    diff --git a/app/(docs)/useSendChat.ts b/app/(docs)/useSendChat.ts index 727005f0..79346038 100644 --- a/app/(docs)/useSendChat.ts +++ b/app/(docs)/useSendChat.ts @@ -7,10 +7,7 @@ import { DynamicMarkdownSection, PagePath } from "@/lib/docs"; import { ChatStreamEvent } from "@/api/chat/route"; import { revalidateChatAction } from "@/actions/revalidateChat"; import { useStreamingChatContext } from "./streamingChatContext"; -import { - ReplCommand, - ReplOutput, -} from "@my-code/runtime/interface"; +import { ReplCommand, ReplOutput } from "@my-code/runtime/interface"; export interface SendChatParams { path: PagePath; @@ -113,7 +110,10 @@ export function useSendChat() { // 2. 新チャットの再検証 await revalidateChatAction(event.chatId, event.pagePath); if (deleteChatOnCreated) { - await revalidateChatAction(deleteChatOnCreated, event.pagePath); + await revalidateChatAction( + deleteChatOnCreated, + event.pagePath + ); } // 3. セクションのスクロール @@ -139,7 +139,10 @@ export function useSendChat() { await revalidateChatAction(chatId, chatPagePath); } if (deleteChatOnCreated) { - await revalidateChatAction(deleteChatOnCreated, chatPagePath); + await revalidateChatAction( + deleteChatOnCreated, + chatPagePath + ); } streamingChatContext.finishStreaming(); router.refresh(); @@ -170,10 +173,7 @@ export function useSendChat() { } })(); }, - [ - router, - streamingChatContext, - ] + [router, streamingChatContext] ); return { diff --git a/app/about/ai/page.tsx b/app/about/ai/page.tsx index d335a252..12e3ff03 100644 --- a/app/about/ai/page.tsx +++ b/app/about/ai/page.tsx @@ -4,7 +4,8 @@ import content from "./content.md?raw"; export const metadata: Metadata = { title: "AI質問機能について", - description: "my.code(); のAI質問機能の詳細と利用上の注意事項について説明します。", + description: + "my.code(); のAI質問機能の詳細と利用上の注意事項について説明します。", }; export default function AiPage() { diff --git a/app/api/chat/regenerate-section/route.ts b/app/api/chat/regenerate-section/route.ts index cb0a8dfe..bb0c8e66 100644 --- a/app/api/chat/regenerate-section/route.ts +++ b/app/api/chat/regenerate-section/route.ts @@ -28,7 +28,7 @@ export type RegenerateStreamEvent = /** * そのセクションの全chatを作成日順に再作成&削除します。 - * + * * 既存のchatのdiffを無視して最新のcontentを渡して1つ目のchatを生成 * →そのdiffを適用したcontentに対して2つ目のchatを作成 * →その2つのdiffを適用したcontentに対して3つ目のchat... @@ -85,12 +85,11 @@ export async function POST(request: NextRequest) { fallbackToPastVersion: false, }); - const currentSectionContent: DynamicMarkdownSection[] = baseSections.map( - (s) => ({ + const currentSectionContent: DynamicMarkdownSection[] = + baseSections.map((s) => ({ ...s, inView: false, - }) - ); + })); const deletedChatIds: string[] = []; const createdChatIds: string[] = []; @@ -100,8 +99,12 @@ export async function POST(request: NextRequest) { send({ type: "progress", current: i, total: targetChats.length }); try { - const firstUserMsg = oldChat.messages.find((m) => m.role === "user"); - const userQuestion = firstUserMsg ? firstUserMsg.content : oldChat.title; + const firstUserMsg = oldChat.messages.find( + (m) => m.role === "user" + ); + const userQuestion = firstUserMsg + ? firstUserMsg.content + : oldChat.title; // 1. Generate new chat on server const result = await generateSingleChat({ diff --git a/app/layout.tsx b/app/layout.tsx index b2f33601..ee154bc9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -42,28 +42,28 @@ export default async function RootLayout({ - -
    - -
    - - {children} -
    -
    -
    - - + +