From c7b1b7a1d74188c9779b3d8a43dfeffedba9d017 Mon Sep 17 00:00:00 2001
From: Cris Lucero
Date: Wed, 30 Sep 2026 19:12:47 +0800
Subject: [PATCH] feat: add a Study Hub button to visualize and trace code
Adds a 'Visualize and trace in Study Hub' link on the editor toolbar and
on each visible test case. It opens the Study Hub tracer with the
student's current code, and for a test case its input and expected
output, so they can step through the run and see where it goes wrong.
The data travels in the URL fragment, so it never reaches a server. Only
what the student can already see is sent: hidden test cases get no link
and solution code is never used. NEXT_PUBLIC_STUDYHUB_URL sets the Study
Hub address (default https://studyhub.dcism.org).
---
.env.example | 4 +-
src/app/problem/[id]/page.tsx | 11 +++++
src/components/problem/code-editor.tsx | 39 ++++++++++++++---
src/components/problem/test-case-bar.tsx | 4 ++
src/components/problem/test-case.tsx | 54 +++++++++++++++++++++++-
src/lib/studyhub.ts | 45 ++++++++++++++++++++
6 files changed, 147 insertions(+), 10 deletions(-)
create mode 100644 src/lib/studyhub.ts
diff --git a/.env.example b/.env.example
index c76051c..7bdc3ba 100644
--- a/.env.example
+++ b/.env.example
@@ -5,4 +5,6 @@ DB_PASSWORD=
DB_NAME=ciscode_db
PORT=3000
-JWT_SECRET=default_secret_key
\ No newline at end of file
+JWT_SECRET=default_secret_key
+# Study Hub base URL (no trailing slash); defaults to https://studyhub.dcism.org
+NEXT_PUBLIC_STUDYHUB_URL=
diff --git a/src/app/problem/[id]/page.tsx b/src/app/problem/[id]/page.tsx
index 2e55686..adeb9e3 100644
--- a/src/app/problem/[id]/page.tsx
+++ b/src/app/problem/[id]/page.tsx
@@ -15,6 +15,7 @@ import { TestCaseResponseType } from "@/dtos/testcase.dto";
import { runTestCase } from "@/lib/fetchers/code.fetchers";
import { fetchProblem } from "@/lib/fetchers/problem.fetchers";
import { submitCode } from "@/lib/fetchers/submission.fetchers";
+import { studyHubTraceUrl } from "@/lib/studyhub";
import { ProblemPageEnum } from "@/lib/types/enums/problempage.enum";
import SubmissionStatusEnum from "@/lib/types/enums/problemstatus.enum";
import TestCaseSubmissionStatusEnum from "@/lib/types/enums/submissionstatus.enum";
@@ -278,6 +279,11 @@ export default function Problem() {
code={code}
onCodeChange={handleCodeChange}
handleReset={handleReset}
+ studyHubTraceUrl={studyHubTraceUrl({
+ problemId: problem.id,
+ title: problem.title,
+ code,
+ })}
/>
}
TestCases={
@@ -288,6 +294,11 @@ export default function Problem() {
onCheckCode={handleCheckCode}
submitted={submitted}
sending={!code || sending}
+ studyHubTrace={{
+ problemId: problem.id,
+ title: problem.title,
+ code,
+ }}
/>
}
/>
diff --git a/src/components/problem/code-editor.tsx b/src/components/problem/code-editor.tsx
index 07ee454..09e40cd 100644
--- a/src/components/problem/code-editor.tsx
+++ b/src/components/problem/code-editor.tsx
@@ -5,7 +5,7 @@ import CodeMirror from "@uiw/react-codemirror";
import { useEditorSettings } from "@/hooks/use-editor-settings";
import ProblemCard from "./problem-card";
import EditorSettings from "./editor-settings";
-import { CodeXml, RefreshCcw } from "lucide-react";
+import { CodeXml, Footprints, RefreshCcw } from "lucide-react";
import {
HoverCard,
HoverCardContent,
@@ -17,12 +17,14 @@ interface CodeEditorProps {
code: string;
onCodeChange?: (value: string | undefined) => void;
handleReset?: () => void;
+ studyHubTraceUrl?: string;
}
const CodeEditor: React.FC = ({
code,
onCodeChange,
handleReset,
+ studyHubTraceUrl,
}) => {
const { settings, update_setting, get_extensions, reset_settings } =
useEditorSettings();
@@ -41,12 +43,35 @@ const CodeEditor: React.FC = ({