@burdenoff/microfe-technospam 2026.1102.1 → 2026.1102.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/wspace-types.d.ts +7468 -141
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/node_modules/@codemirror/autocomplete/dist/index.js +527 -0
- package/dist/node_modules/@codemirror/autocomplete/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/commands/dist/index.js +1108 -0
- package/dist/node_modules/@codemirror/commands/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/lang-go/dist/index.js +206 -0
- package/dist/node_modules/@codemirror/lang-go/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/lang-javascript/dist/index.js +321 -0
- package/dist/node_modules/@codemirror/lang-javascript/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/lang-python/dist/index.js +265 -0
- package/dist/node_modules/@codemirror/lang-python/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/language/dist/index.js +957 -0
- package/dist/node_modules/@codemirror/language/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/ruby.js +164 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/ruby.js.map +1 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/shell.js +109 -0
- package/dist/node_modules/@codemirror/legacy-modes/mode/shell.js.map +1 -0
- package/dist/node_modules/@codemirror/state/dist/index.js +1739 -0
- package/dist/node_modules/@codemirror/state/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/streamparser/dist/index.js +70 -0
- package/dist/node_modules/@codemirror/streamparser/dist/index.js.map +1 -0
- package/dist/node_modules/@codemirror/view/dist/index.js +5427 -0
- package/dist/node_modules/@codemirror/view/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/common/dist/index.js +896 -0
- package/dist/node_modules/@lezer/common/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/go/dist/index.js +215 -0
- package/dist/node_modules/@lezer/go/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/highlight/dist/index.js +395 -0
- package/dist/node_modules/@lezer/highlight/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/javascript/dist/index.js +437 -0
- package/dist/node_modules/@lezer/javascript/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/lr/dist/index.js +842 -0
- package/dist/node_modules/@lezer/lr/dist/index.js.map +1 -0
- package/dist/node_modules/@lezer/python/dist/index.js +352 -0
- package/dist/node_modules/@lezer/python/dist/index.js.map +1 -0
- package/dist/node_modules/@marijn/find-cluster-break/src/index.js +66 -0
- package/dist/node_modules/@marijn/find-cluster-break/src/index.js.map +1 -0
- package/dist/node_modules/style-mod/src/style-mod.js +74 -0
- package/dist/node_modules/style-mod/src/style-mod.js.map +1 -0
- package/dist/node_modules/w3c-keyname/index.js +90 -0
- package/dist/node_modules/w3c-keyname/index.js.map +1 -0
- package/dist/stories/fixtures/technospamFixtures.d.ts.map +1 -1
- package/dist/stories/journeys/Problems.stories.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoot.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoot.js +187 -178
- package/dist/technospam/TechnoSpamRoot.js.map +1 -1
- package/dist/technospam/api/technospamApi.d.ts +39 -0
- package/dist/technospam/api/technospamApi.d.ts.map +1 -1
- package/dist/technospam/api/technospamApi.js +184 -150
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/MediaCover.js +2 -2
- package/dist/technospam/components/MediaCover.js.map +1 -1
- package/dist/technospam/components/MobileSectionTabs.d.ts +9 -0
- package/dist/technospam/components/MobileSectionTabs.d.ts.map +1 -0
- package/dist/technospam/components/MobileSectionTabs.js +43 -0
- package/dist/technospam/components/MobileSectionTabs.js.map +1 -0
- package/dist/technospam/components/PageLayout.d.ts +7 -1
- package/dist/technospam/components/PageLayout.d.ts.map +1 -1
- package/dist/technospam/components/PageLayout.js +40 -28
- package/dist/technospam/components/PageLayout.js.map +1 -1
- package/dist/technospam/components/SubmissionViews.d.ts +6 -2
- package/dist/technospam/components/SubmissionViews.d.ts.map +1 -1
- package/dist/technospam/components/SubmissionViews.js +6 -5
- package/dist/technospam/components/SubmissionViews.js.map +1 -1
- package/dist/technospam/components/TechnoSpamErrorBoundary.d.ts +6 -0
- package/dist/technospam/components/TechnoSpamErrorBoundary.d.ts.map +1 -1
- package/dist/technospam/components/TechnoSpamErrorBoundary.js +2 -2
- package/dist/technospam/components/TechnoSpamErrorBoundary.js.map +1 -1
- package/dist/technospam/components/editor/CodeEditor.d.ts +26 -0
- package/dist/technospam/components/editor/CodeEditor.d.ts.map +1 -0
- package/dist/technospam/components/editor/CodeEditor.js +102 -0
- package/dist/technospam/components/editor/CodeEditor.js.map +1 -0
- package/dist/technospam/components/editor/RunOutputPanel.d.ts +14 -0
- package/dist/technospam/components/editor/RunOutputPanel.d.ts.map +1 -0
- package/dist/technospam/components/editor/RunOutputPanel.js +127 -0
- package/dist/technospam/components/editor/RunOutputPanel.js.map +1 -0
- package/dist/technospam/components/editor/editorLanguages.d.ts +11 -0
- package/dist/technospam/components/editor/editorLanguages.d.ts.map +1 -0
- package/dist/technospam/components/editor/editorLanguages.js +30 -0
- package/dist/technospam/components/editor/editorLanguages.js.map +1 -0
- package/dist/technospam/components/editor/editorTheme.d.ts +11 -0
- package/dist/technospam/components/editor/editorTheme.d.ts.map +1 -0
- package/dist/technospam/components/editor/editorTheme.js +120 -0
- package/dist/technospam/components/editor/editorTheme.js.map +1 -0
- package/dist/technospam/components/lesson/SaveCourseOffline.js +6 -6
- package/dist/technospam/hooks/usePhoneLayout.d.ts +18 -0
- package/dist/technospam/hooks/usePhoneLayout.d.ts.map +1 -0
- package/dist/technospam/hooks/usePhoneLayout.js +14 -0
- package/dist/technospam/hooks/usePhoneLayout.js.map +1 -0
- package/dist/technospam/hooks/useSolutionRunner.d.ts +35 -0
- package/dist/technospam/hooks/useSolutionRunner.d.ts.map +1 -0
- package/dist/technospam/hooks/useSolutionRunner.js +66 -0
- package/dist/technospam/hooks/useSolutionRunner.js.map +1 -0
- package/dist/technospam/hooks/useTechnoSpamAnalytics.d.ts +1 -1
- package/dist/technospam/hooks/useTechnoSpamAnalytics.d.ts.map +1 -1
- package/dist/technospam/hooks/useTechnoSpamAnalytics.js.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +416 -263
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +89 -87
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +3 -3
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +117 -81
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +21 -17
- package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +3 -3
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +4 -4
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +6 -4
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/dist/technospam/types/index.d.ts +7 -0
- package/dist/technospam/types/index.d.ts.map +1 -1
- package/dist/technospam/utils/mobileTabs.d.ts +45 -0
- package/dist/technospam/utils/mobileTabs.d.ts.map +1 -0
- package/dist/technospam/utils/mobileTabs.js +70 -0
- package/dist/technospam/utils/mobileTabs.js.map +1 -0
- package/dist/test/matchMedia.d.ts +6 -0
- package/dist/test/matchMedia.d.ts.map +1 -0
- package/package.json +16 -6
|
@@ -1,52 +1,64 @@
|
|
|
1
1
|
import { useTechnoSpamNavigate as e } from "../providers/TechnoSpamProvider.js";
|
|
2
2
|
import { useNativeControls as t } from "../hooks/useNativeControls.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { usePhoneLayout as n } from "../hooks/usePhoneLayout.js";
|
|
4
|
+
import { useTr as r } from "../hooks/useTr.js";
|
|
5
|
+
import { cn as i } from "../utils/cn.js";
|
|
6
|
+
import { MobileSectionTabs as a } from "./MobileSectionTabs.js";
|
|
7
|
+
import { useCallback as o, useContext as s } from "react";
|
|
8
|
+
import { ArrowLeft as c } from "lucide-react";
|
|
9
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
10
|
+
import { getApolloContext as d } from "@apollo/client/react";
|
|
11
|
+
import { AuroraBackground as f, PullToRefresh as p } from "@burdenoff/fe-libs/ui";
|
|
8
12
|
//#region src/technospam/components/PageLayout.tsx
|
|
9
|
-
function
|
|
10
|
-
let
|
|
11
|
-
|
|
13
|
+
function m({ title: m, description: h, actions: g, children: _, backPath: v, hero: y = !1, mobileSection: b = !1 }) {
|
|
14
|
+
let x = e(), { impact: S } = t(), C = r(), { isPhone: w, nativeChrome: T } = n(), E = s(d()).client, D = o(async () => {
|
|
15
|
+
E && await E.refetchQueries({ include: "active" });
|
|
16
|
+
}, [E]);
|
|
17
|
+
return /* @__PURE__ */ u("div", {
|
|
12
18
|
className: "flex h-full flex-col bg-bg-surface",
|
|
13
|
-
children: [/* @__PURE__ */
|
|
14
|
-
className:
|
|
19
|
+
children: [/* @__PURE__ */ u("div", {
|
|
20
|
+
className: i("relative flex flex-col gap-3 overflow-hidden border-b border-border-seam px-4 py-4 sm:flex-row sm:items-start sm:justify-between sm:gap-4 sm:px-6 sm:py-5", y ? "bg-accent-primary/12" : "bg-bg-surface"),
|
|
15
21
|
children: [
|
|
16
|
-
|
|
17
|
-
/* @__PURE__ */
|
|
22
|
+
y ? /* @__PURE__ */ l(f, { intensity: "subtle" }) : null,
|
|
23
|
+
/* @__PURE__ */ u("div", {
|
|
18
24
|
className: "relative flex min-w-0 flex-col gap-1",
|
|
19
25
|
children: [
|
|
20
|
-
|
|
26
|
+
v ? /* @__PURE__ */ u("button", {
|
|
21
27
|
type: "button",
|
|
22
28
|
onClick: () => {
|
|
23
|
-
|
|
29
|
+
S("light"), x(v);
|
|
24
30
|
},
|
|
25
|
-
className: "flex w-fit items-center gap-1 text-sm text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]",
|
|
26
|
-
children: [/* @__PURE__ */
|
|
31
|
+
className: "-ml-1 flex min-h-11 w-fit items-center gap-1 px-1 text-sm sm:min-h-0 text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]",
|
|
32
|
+
children: [/* @__PURE__ */ l(c, { size: 15 }), C("technospam.common.back", "Back")]
|
|
27
33
|
}) : null,
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
-
className: "truncate text-
|
|
30
|
-
children:
|
|
34
|
+
/* @__PURE__ */ l("h1", {
|
|
35
|
+
className: "truncate text-2xl font-semibold text-text-primary",
|
|
36
|
+
children: m
|
|
31
37
|
}),
|
|
32
|
-
|
|
33
|
-
className: "max-w-2xl text-sm text-text-secondary",
|
|
34
|
-
children:
|
|
38
|
+
h ? /* @__PURE__ */ l("p", {
|
|
39
|
+
className: "sr-only max-w-2xl text-sm text-text-secondary sm:not-sr-only",
|
|
40
|
+
children: h
|
|
35
41
|
}) : null
|
|
36
42
|
]
|
|
37
43
|
}),
|
|
38
|
-
|
|
44
|
+
g ? /* @__PURE__ */ l("div", {
|
|
39
45
|
className: "relative flex flex-wrap items-center gap-2",
|
|
40
|
-
children:
|
|
46
|
+
children: g
|
|
41
47
|
}) : null
|
|
42
48
|
]
|
|
43
|
-
}), /* @__PURE__ */
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
}), /* @__PURE__ */ u(p, {
|
|
50
|
+
onRefresh: D,
|
|
51
|
+
enabled: T,
|
|
52
|
+
label: C("technospam.common.refreshing", "Refreshing"),
|
|
53
|
+
className: "relative h-auto min-h-0 flex-1 overflow-y-auto p-4 pb-[calc(env(safe-area-inset-bottom)+1rem)] sm:p-6",
|
|
54
|
+
children: [b && w && !T ? /* @__PURE__ */ l("div", {
|
|
55
|
+
className: "mb-4",
|
|
56
|
+
children: /* @__PURE__ */ l(a, {})
|
|
57
|
+
}) : null, _]
|
|
46
58
|
})]
|
|
47
59
|
});
|
|
48
60
|
}
|
|
49
61
|
//#endregion
|
|
50
|
-
export {
|
|
62
|
+
export { m as PageLayout };
|
|
51
63
|
|
|
52
64
|
//# sourceMappingURL=PageLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/technospam/components/PageLayout.tsx"],"sourcesContent":["import type
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/technospam/components/PageLayout.tsx"],"sourcesContent":["import { useCallback, useContext, type ReactNode } from 'react';\nimport { ArrowLeft } from 'lucide-react';\nimport { getApolloContext } from '@apollo/client/react';\nimport { AuroraBackground, PullToRefresh } from '@burdenoff/fe-libs/ui';\nimport { useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport { useNativeControls } from '../hooks/useNativeControls';\nimport { usePhoneLayout } from '../hooks/usePhoneLayout';\nimport { useTr } from '../hooks/useTr';\nimport { cn } from '../utils/cn';\nimport { MobileSectionTabs } from './MobileSectionTabs';\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: ReactNode;\n actions?: ReactNode;\n children: ReactNode;\n backPath?: string;\n /** Render the page header with the living-glass aurora backdrop (module hero). */\n hero?: boolean;\n /**\n * One of the four learner-journey pages (Home, Courses, Practice, Mentors):\n * on a phone-width mobile browser it shows the in-page section switcher that\n * stands in for the installed app's bottom tab bar.\n */\n mobileSection?: boolean;\n}\n\n/**\n * Consistent page chrome for every TechnoSpam module page: a header band\n * (optionally with the aurora hero backdrop), an optional back affordance, an\n * actions slot, and a scrollable body. Mirrors the platform page pattern so\n * TechnoSpam feels native to the shell.\n */\nexport function PageLayout({\n title,\n description,\n actions,\n children,\n backPath,\n hero = false,\n mobileSection = false,\n}: PageLayoutProps) {\n const navigate = useTechnoSpamNavigate();\n const { impact } = useNativeControls();\n const tr = useTr();\n const { isPhone, nativeChrome } = usePhoneLayout();\n // Read the client from context rather than useApolloClient(), which throws\n // without a provider (isolated page tests and stories render without one).\n const client = useContext(getApolloContext()).client;\n\n // Pull-to-refresh re-runs every query the visible page is subscribed to.\n const refresh = useCallback(async () => {\n if (client) await client.refetchQueries({ include: 'active' });\n }, [client]);\n\n return (\n <div className=\"flex h-full flex-col bg-bg-surface\">\n <div\n className={cn(\n 'relative flex flex-col gap-3 overflow-hidden border-b border-border-seam px-4 py-4 sm:flex-row sm:items-start sm:justify-between sm:gap-4 sm:px-6 sm:py-5',\n hero ? 'bg-accent-primary/12' : 'bg-bg-surface'\n )}\n >\n {hero ? <AuroraBackground intensity=\"subtle\" /> : null}\n <div className=\"relative flex min-w-0 flex-col gap-1\">\n {backPath ? (\n <button\n type=\"button\"\n onClick={() => {\n void impact('light');\n navigate(backPath);\n }}\n className=\"-ml-1 flex min-h-11 w-fit items-center gap-1 px-1 text-sm sm:min-h-0 text-text-secondary transition-colors hover:text-text-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <ArrowLeft size={15} />\n {tr('technospam.common.back', 'Back')}\n </button>\n ) : null}\n <h1 className=\"truncate text-2xl font-semibold text-text-primary\">{title}</h1>\n {description ? (\n // Visually dropped on phones to keep the header compact, but still\n // announced to screen readers.\n <p className=\"sr-only max-w-2xl text-sm text-text-secondary sm:not-sr-only\">\n {description}\n </p>\n ) : null}\n </div>\n {actions ? (\n <div className=\"relative flex flex-wrap items-center gap-2\">{actions}</div>\n ) : null}\n </div>\n {/* The body owns scrolling. In the installed app it also carries the\n native pull-to-refresh gesture; elsewhere PullToRefresh is a plain\n scroller. The bottom padding clears the home indicator on phones\n (the shell already reserves the tab bar's own height). The scroll\n classes are repeated here because PullToRefresh's own copies live in\n fe-libs' dist, which a consumer's Tailwind scan may not cover; without\n them wide content (tab lists, tables) would scroll the whole page\n sideways instead of this body. */}\n <PullToRefresh\n onRefresh={refresh}\n enabled={nativeChrome}\n label={tr('technospam.common.refreshing', 'Refreshing')}\n className=\"relative h-auto min-h-0 flex-1 overflow-y-auto p-4 pb-[calc(env(safe-area-inset-bottom)+1rem)] sm:p-6\"\n >\n {mobileSection && isPhone && !nativeChrome ? (\n <div className=\"mb-4\">\n <MobileSectionTabs />\n </div>\n ) : null}\n {children}\n </PullToRefresh>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAiCA,SAAgB,EAAW,EACzB,UACA,gBACA,YACA,aACA,aACA,UAAO,IACP,mBAAgB,MACE;CAClB,IAAM,IAAW,GAAuB,EAClC,EAAE,cAAW,GAAmB,EAChC,IAAK,GAAO,EACZ,EAAE,YAAS,oBAAiB,GAAgB,EAG5C,IAAS,EAAW,GAAkB,CAAC,CAAC,QAGxC,IAAU,EAAY,YAAY;AACtC,EAAI,KAAQ,MAAM,EAAO,eAAe,EAAE,SAAS,UAAU,CAAC;IAC7D,CAAC,EAAO,CAAC;AAEZ,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GACE,WAAW,EACT,6JACA,IAAO,yBAAyB,gBACjC;aAJH;IAMG,IAAO,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA,GAAG;IAClD,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,IACC,kBAAC,UAAD;OACE,MAAK;OACL,eAAe;AAEb,QADK,EAAO,QAAQ,EACpB,EAAS,EAAS;;OAEpB,WAAU;iBANZ,CAQE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAG,0BAA0B,OAAO,CAC9B;WACP;MACJ,kBAAC,MAAD;OAAI,WAAU;iBAAqD;OAAW,CAAA;MAC7E,IAGC,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA,GACF;MACA;;IACL,IACC,kBAAC,OAAD;KAAK,WAAU;eAA8C;KAAc,CAAA,GACzE;IACA;MASN,kBAAC,GAAD;GACE,WAAW;GACX,SAAS;GACT,OAAO,EAAG,gCAAgC,aAAa;GACvD,WAAU;aAJZ,CAMG,KAAiB,KAAW,CAAC,IAC5B,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAqB,CAAA;IACjB,CAAA,GACJ,MACH,EACa;KACZ"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { TecSubmission } from '../api/technospamApi';
|
|
3
3
|
|
|
4
|
+
/** The grading fields the panel renders — a graded submission or a sample run. */
|
|
5
|
+
export type GradedResult = Pick<TecSubmission, 'verdict' | 'passedCount' | 'totalCount' | 'runtimeMs' | 'stderr' | 'testResults'>;
|
|
4
6
|
/**
|
|
5
7
|
* A dark, editor-window styled code surface: mac-style traffic-light dots plus a
|
|
6
8
|
* language label header over a scrollable monospace body so read-only code reads
|
|
@@ -24,8 +26,10 @@ export declare function IoBlock({ label, value }: {
|
|
|
24
26
|
* row here only ever carries its name and status — never the input, the
|
|
25
27
|
* expected output, or what the submitted program actually printed.
|
|
26
28
|
*/
|
|
27
|
-
export declare function TestResultsPanel({ submission, actualOutputLabel, }: {
|
|
28
|
-
submission:
|
|
29
|
+
export declare function TestResultsPanel({ submission, actualOutputLabel, caseNoun, }: {
|
|
30
|
+
submission: GradedResult;
|
|
29
31
|
actualOutputLabel?: string;
|
|
32
|
+
/** What the cases are called in the summary line, e.g. "sample test cases". */
|
|
33
|
+
caseNoun?: string;
|
|
30
34
|
}): import("react").JSX.Element | null;
|
|
31
35
|
//# sourceMappingURL=SubmissionViews.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubmissionViews.d.ts","sourceRoot":"","sources":["../../../src/technospam/components/SubmissionViews.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,sBAAsB,CAAC;AAE7E;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,aAAa,GACd,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,+BAuBA;AAED,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,+BASjF;AAaD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,iBAAiC,
|
|
1
|
+
{"version":3,"file":"SubmissionViews.d.ts","sourceRoot":"","sources":["../../../src/technospam/components/SubmissionViews.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,OAAO,KAAK,EAAE,aAAa,EAAqB,MAAM,sBAAsB,CAAC;AAE7E,kFAAkF;AAClF,MAAM,MAAM,YAAY,GAAG,IAAI,CAC7B,aAAa,EACb,SAAS,GAAG,aAAa,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,GAAG,aAAa,CAClF,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,QAAQ,EACR,aAAa,GACd,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,+BAuBA;AAED,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,+BASjF;AAaD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,iBAAiC,EACjC,QAAuB,GACxB,EAAE;IACD,UAAU,EAAE,YAAY,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,sCA0DA"}
|
|
@@ -55,9 +55,9 @@ function m(e) {
|
|
|
55
55
|
className: "text-status-error-fg"
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
function h({ submission: r, actualOutputLabel: a = "Your output" }) {
|
|
59
|
-
let
|
|
60
|
-
return
|
|
58
|
+
function h({ submission: r, actualOutputLabel: a = "Your output", caseNoun: s = "test cases" }) {
|
|
59
|
+
let c = r.testResults ?? [];
|
|
60
|
+
return c.length === 0 ? null : /* @__PURE__ */ u("div", {
|
|
61
61
|
className: "space-y-3",
|
|
62
62
|
children: [
|
|
63
63
|
/* @__PURE__ */ u("div", {
|
|
@@ -71,7 +71,8 @@ function h({ submission: r, actualOutputLabel: a = "Your output" }) {
|
|
|
71
71
|
r.passedCount,
|
|
72
72
|
" of ",
|
|
73
73
|
r.totalCount,
|
|
74
|
-
"
|
|
74
|
+
" ",
|
|
75
|
+
s
|
|
75
76
|
]
|
|
76
77
|
}),
|
|
77
78
|
/* @__PURE__ */ u("span", {
|
|
@@ -87,7 +88,7 @@ function h({ submission: r, actualOutputLabel: a = "Your output" }) {
|
|
|
87
88
|
}),
|
|
88
89
|
/* @__PURE__ */ l("div", {
|
|
89
90
|
className: "divide-y divide-border-seam overflow-hidden rounded-xl border border-border-seam",
|
|
90
|
-
children:
|
|
91
|
+
children: c.map((n) => {
|
|
91
92
|
let r = m(n.status);
|
|
92
93
|
return /* @__PURE__ */ u("div", {
|
|
93
94
|
className: "space-y-2 bg-bg-surface px-4 py-3",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubmissionViews.js","names":[],"sources":["../../../src/technospam/components/SubmissionViews.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { CheckCircle2, Clock, Code2, EyeOff, MinusCircle, XCircle } from 'lucide-react';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { humanizeEnum } from '../utils/format';\nimport { cn } from '../utils/cn';\nimport type { TecSubmission, TecTestCaseStatus } from '../api/technospamApi';\n\n/**\n * A dark, editor-window styled code surface: mac-style traffic-light dots plus a\n * language label header over a scrollable monospace body so read-only code reads\n * like a real IDE panel rather than a flat block.\n */\nexport function CodePanel({\n label,\n children,\n bodyClassName,\n}: {\n label: string;\n children: ReactNode;\n bodyClassName?: string;\n}) {\n return (\n <div className=\"overflow-hidden rounded-xl border border-border-strong bg-bg-sunken shadow-sm\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border-seam bg-bg-canvas/40 px-4 py-2\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-error-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-warning-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-success-border\" />\n </div>\n <span className=\"inline-flex items-center gap-1.5 font-mono text-xs text-text-secondary\">\n <Code2 size={13} /> {label}\n </span>\n </div>\n <pre\n className={cn(\n 'overflow-auto p-4 font-mono text-xs leading-relaxed text-text-primary',\n bodyClassName\n )}\n >\n {children}\n </pre>\n </div>\n );\n}\n\n/** One labelled monospace input/output block inside a test-case result. */\nexport function IoBlock({ label, value }: { label: string; value?: string | null }) {\n return (\n <div className=\"min-w-0\">\n <p className=\"mb-1 text-xs font-medium uppercase tracking-wide text-text-muted\">{label}</p>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken px-3 py-2 font-mono text-xs leading-relaxed text-text-primary\">\n {value?.length ? value : '—'}\n </pre>\n </div>\n );\n}\n\n/** Icon + tone for a per-test-case status, using semantic tokens only. */\nfunction testCaseStatusTone(status: TecTestCaseStatus): { icon: ReactNode; className: string } {\n if (status === 'PASSED') {\n return { icon: <CheckCircle2 size={14} />, className: 'text-status-success-fg' };\n }\n if (status === 'SKIPPED') {\n return { icon: <MinusCircle size={14} />, className: 'text-text-muted' };\n }\n return { icon: <XCircle size={14} />, className: 'text-status-error-fg' };\n}\n\n/**\n * Per-test-case grading detail for a graded submission — shared by the learner's\n * problem page and the authoring console's grading panel.\n *\n * The backend redacts HIDDEN fixtures before persisting results, so a hidden\n * row here only ever carries its name and status — never the input, the\n * expected output, or what the submitted program actually printed.\n */\nexport function TestResultsPanel({\n submission,\n actualOutputLabel = 'Your output',\n}: {\n submission:
|
|
1
|
+
{"version":3,"file":"SubmissionViews.js","names":[],"sources":["../../../src/technospam/components/SubmissionViews.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { CheckCircle2, Clock, Code2, EyeOff, MinusCircle, XCircle } from 'lucide-react';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { StatusBadge } from './StatusBadge';\nimport { humanizeEnum } from '../utils/format';\nimport { cn } from '../utils/cn';\nimport type { TecSubmission, TecTestCaseStatus } from '../api/technospamApi';\n\n/** The grading fields the panel renders — a graded submission or a sample run. */\nexport type GradedResult = Pick<\n TecSubmission,\n 'verdict' | 'passedCount' | 'totalCount' | 'runtimeMs' | 'stderr' | 'testResults'\n>;\n\n/**\n * A dark, editor-window styled code surface: mac-style traffic-light dots plus a\n * language label header over a scrollable monospace body so read-only code reads\n * like a real IDE panel rather than a flat block.\n */\nexport function CodePanel({\n label,\n children,\n bodyClassName,\n}: {\n label: string;\n children: ReactNode;\n bodyClassName?: string;\n}) {\n return (\n <div className=\"overflow-hidden rounded-xl border border-border-strong bg-bg-sunken shadow-sm\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border-seam bg-bg-canvas/40 px-4 py-2\">\n <div className=\"flex items-center gap-1.5\">\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-error-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-warning-border\" />\n <span className=\"h-2.5 w-2.5 rounded-full bg-status-success-border\" />\n </div>\n <span className=\"inline-flex items-center gap-1.5 font-mono text-xs text-text-secondary\">\n <Code2 size={13} /> {label}\n </span>\n </div>\n <pre\n className={cn(\n 'overflow-auto p-4 font-mono text-xs leading-relaxed text-text-primary',\n bodyClassName\n )}\n >\n {children}\n </pre>\n </div>\n );\n}\n\n/** One labelled monospace input/output block inside a test-case result. */\nexport function IoBlock({ label, value }: { label: string; value?: string | null }) {\n return (\n <div className=\"min-w-0\">\n <p className=\"mb-1 text-xs font-medium uppercase tracking-wide text-text-muted\">{label}</p>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken px-3 py-2 font-mono text-xs leading-relaxed text-text-primary\">\n {value?.length ? value : '—'}\n </pre>\n </div>\n );\n}\n\n/** Icon + tone for a per-test-case status, using semantic tokens only. */\nfunction testCaseStatusTone(status: TecTestCaseStatus): { icon: ReactNode; className: string } {\n if (status === 'PASSED') {\n return { icon: <CheckCircle2 size={14} />, className: 'text-status-success-fg' };\n }\n if (status === 'SKIPPED') {\n return { icon: <MinusCircle size={14} />, className: 'text-text-muted' };\n }\n return { icon: <XCircle size={14} />, className: 'text-status-error-fg' };\n}\n\n/**\n * Per-test-case grading detail for a graded submission — shared by the learner's\n * problem page and the authoring console's grading panel.\n *\n * The backend redacts HIDDEN fixtures before persisting results, so a hidden\n * row here only ever carries its name and status — never the input, the\n * expected output, or what the submitted program actually printed.\n */\nexport function TestResultsPanel({\n submission,\n actualOutputLabel = 'Your output',\n caseNoun = 'test cases',\n}: {\n submission: GradedResult;\n actualOutputLabel?: string;\n /** What the cases are called in the summary line, e.g. \"sample test cases\". */\n caseNoun?: string;\n}) {\n const results = submission.testResults ?? [];\n if (results.length === 0) return null;\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1\">\n <StatusBadge status={submission.verdict} />\n <span className=\"text-sm text-text-secondary\">\n Passed {submission.passedCount} of {submission.totalCount} {caseNoun}\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {submission.runtimeMs} ms\n </span>\n </div>\n\n <div className=\"divide-y divide-border-seam overflow-hidden rounded-xl border border-border-seam\">\n {results.map((result) => {\n const tone = testCaseStatusTone(result.status);\n return (\n <div key={result.testCaseId} className=\"space-y-2 bg-bg-surface px-4 py-3\">\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1\">\n <span\n className={cn(\n 'inline-flex items-center gap-1.5 text-sm font-medium',\n tone.className\n )}\n >\n {tone.icon} {result.name}\n </span>\n <Badge variant=\"outline\">{humanizeEnum(result.status)}</Badge>\n {result.visibility === 'HIDDEN' ? (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <EyeOff size={12} /> hidden test\n </span>\n ) : null}\n <span className=\"ml-auto text-xs text-text-muted\">{result.durationMs} ms</span>\n </div>\n\n {result.visibility === 'PUBLIC' && result.status !== 'SKIPPED' ? (\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-3\">\n <IoBlock label=\"Input\" value={result.input} />\n <IoBlock label=\"Expected\" value={result.expectedOutput} />\n <IoBlock label={actualOutputLabel} value={result.actualOutput} />\n </div>\n ) : null}\n </div>\n );\n })}\n </div>\n\n {submission.stderr ? (\n <CodePanel label=\"stderr\" bodyClassName=\"max-h-48\">\n {submission.stderr}\n </CodePanel>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAmBA,SAAgB,EAAU,EACxB,UACA,aACA,oBAKC;AACD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD,EAAM,WAAU,mDAAoD,CAAA;KACpE,kBAAC,QAAD,EAAM,WAAU,qDAAsD,CAAA;KACtE,kBAAC,QAAD,EAAM,WAAU,qDAAsD,CAAA;KAClE;OACN,kBAAC,QAAD;IAAM,WAAU;cAAhB;KACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;KAAE;KAChB;MACH;MACN,kBAAC,OAAD;GACE,WAAW,EACT,yEACA,EACD;GAEA;GACG,CAAA,CACF;;;AAKV,SAAgB,EAAQ,EAAE,UAAO,YAAmD;AAClF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,KAAD;GAAG,WAAU;aAAoE;GAAU,CAAA,EAC3F,kBAAC,OAAD;GAAK,WAAU;aACZ,GAAO,SAAS,IAAQ;GACrB,CAAA,CACF;;;AAKV,SAAS,EAAmB,GAAmE;AAO7F,QANI,MAAW,WACN;EAAE,MAAM,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;EAAE,WAAW;EAA0B,GAE9E,MAAW,YACN;EAAE,MAAM,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA;EAAE,WAAW;EAAmB,GAEnE;EAAE,MAAM,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;EAAE,WAAW;EAAwB;;AAW3E,SAAgB,EAAiB,EAC/B,eACA,uBAAoB,eACpB,cAAW,gBAMV;CACD,IAAM,IAAU,EAAW,eAAe,EAAE;AAG5C,QAFI,EAAQ,WAAW,IAAU,OAG/B,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAa,QAAQ,EAAW,SAAW,CAAA;KAC3C,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OAA8C;OACpC,EAAW;OAAY;OAAK,EAAW;OAAW;OAAE;OACvD;;KACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;OAAE,EAAW;OAAU;OACrC;;KACH;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MAAW;KACvB,IAAM,IAAO,EAAmB,EAAO,OAAO;AAC9C,YACE,kBAAC,OAAD;MAA6B,WAAU;gBAAvC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SACE,WAAW,EACT,wDACA,EAAK,UACN;mBAJH;UAMG,EAAK;UAAK;UAAE,EAAO;UACf;;QACP,kBAAC,GAAD;SAAO,SAAQ;mBAAW,EAAa,EAAO,OAAO;SAAS,CAAA;QAC7D,EAAO,eAAe,WACrB,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA,EAAA,eACf;aACL;QACJ,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAAmD,EAAO,YAAW,MAAU;;QAC3E;UAEL,EAAO,eAAe,YAAY,EAAO,WAAW,YACnD,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SAAS,OAAM;SAAQ,OAAO,EAAO;SAAS,CAAA;QAC9C,kBAAC,GAAD;SAAS,OAAM;SAAW,OAAO,EAAO;SAAkB,CAAA;QAC1D,kBAAC,GAAD;SAAS,OAAO;SAAmB,OAAO,EAAO;SAAgB,CAAA;QAC7D;WACJ,KACA;QA1BI,EAAO,WA0BX;MAER;IACE,CAAA;GAEL,EAAW,SACV,kBAAC,GAAD;IAAW,OAAM;IAAS,eAAc;cACrC,EAAW;IACF,CAAA,GACV;GACA"}
|
|
@@ -3,6 +3,12 @@ import { Component, ReactNode } from 'react';
|
|
|
3
3
|
interface Props {
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
title?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Rendered instead of the default error view when a child throws. Used for
|
|
8
|
+
* small, recoverable regions (for example a lazily loaded editor chunk that
|
|
9
|
+
* failed to download) where a working fallback beats a page-level error.
|
|
10
|
+
*/
|
|
11
|
+
fallback?: ReactNode;
|
|
6
12
|
}
|
|
7
13
|
interface State {
|
|
8
14
|
hasError: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TechnoSpamErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/technospam/components/TechnoSpamErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlD,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"TechnoSpamErrorBoundary.d.ts","sourceRoot":"","sources":["../../../src/technospam/components/TechnoSpamErrorBoundary.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlD,UAAU,KAAK;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,UAAU,KAAK;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,uBAAwB,SAAQ,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC;gBACtD,KAAK,EAAE,KAAK;IAKxB,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK;IAIpD,MAAM,IAAI,SAAS;CAcpB"}
|
|
@@ -16,13 +16,13 @@ var r = class extends t {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
render() {
|
|
19
|
-
return this.state.hasError ? /* @__PURE__ */ n("div", {
|
|
19
|
+
return this.state.hasError ? this.props.fallback === void 0 ? /* @__PURE__ */ n("div", {
|
|
20
20
|
className: "flex h-full min-h-[50vh] items-center justify-center p-6",
|
|
21
21
|
children: /* @__PURE__ */ n(e, {
|
|
22
22
|
title: this.props.title ?? "Couldn't load this page",
|
|
23
23
|
error: this.state.error ?? void 0
|
|
24
24
|
})
|
|
25
|
-
}) : this.props.children;
|
|
25
|
+
}) : this.props.fallback : this.props.children;
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TechnoSpamErrorBoundary.js","names":[],"sources":["../../../src/technospam/components/TechnoSpamErrorBoundary.tsx"],"sourcesContent":["import { Component, type ReactNode } from 'react';\nimport { ErrorView } from './StateViews';\n\ninterface Props {\n children: ReactNode;\n title?: string;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n}\n\n/**\n * Per-route error boundary for the TechnoSpam MFE.\n *\n * Catches render errors inside TechnoSpam pages (e.g. a GraphQL response with\n * an unexpectedly null connection field) and renders an explicit error state\n * inside the module chrome instead of letting the error bubble to the root\n * boundary or leaving the content area blank.\n */\nexport class TechnoSpamErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n\n static getDerivedStateFromError(error: Error): State {\n return { hasError: true, error };\n }\n\n render(): ReactNode {\n if (this.state.hasError) {\n return (\n <div className=\"flex h-full min-h-[50vh] items-center justify-center p-6\">\n <ErrorView\n title={this.props.title ?? \"Couldn't load this page\"}\n error={this.state.error ?? undefined}\n />\n </div>\n );\n }\n return this.props.children;\n }\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"TechnoSpamErrorBoundary.js","names":[],"sources":["../../../src/technospam/components/TechnoSpamErrorBoundary.tsx"],"sourcesContent":["import { Component, type ReactNode } from 'react';\nimport { ErrorView } from './StateViews';\n\ninterface Props {\n children: ReactNode;\n title?: string;\n /**\n * Rendered instead of the default error view when a child throws. Used for\n * small, recoverable regions (for example a lazily loaded editor chunk that\n * failed to download) where a working fallback beats a page-level error.\n */\n fallback?: ReactNode;\n}\n\ninterface State {\n hasError: boolean;\n error: Error | null;\n}\n\n/**\n * Per-route error boundary for the TechnoSpam MFE.\n *\n * Catches render errors inside TechnoSpam pages (e.g. a GraphQL response with\n * an unexpectedly null connection field) and renders an explicit error state\n * inside the module chrome instead of letting the error bubble to the root\n * boundary or leaving the content area blank.\n */\nexport class TechnoSpamErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false, error: null };\n }\n\n static getDerivedStateFromError(error: Error): State {\n return { hasError: true, error };\n }\n\n render(): ReactNode {\n if (this.state.hasError) {\n if (this.props.fallback !== undefined) return this.props.fallback;\n return (\n <div className=\"flex h-full min-h-[50vh] items-center justify-center p-6\">\n <ErrorView\n title={this.props.title ?? \"Couldn't load this page\"}\n error={this.state.error ?? undefined}\n />\n </div>\n );\n }\n return this.props.children;\n }\n}\n"],"mappings":";;;;AA2BA,IAAa,IAAb,cAA6C,EAAwB;CACnE,YAAY,GAAc;AAExB,EADA,MAAM,EAAM,EACZ,KAAK,QAAQ;GAAE,UAAU;GAAO,OAAO;GAAM;;CAG/C,OAAO,yBAAyB,GAAqB;AACnD,SAAO;GAAE,UAAU;GAAM;GAAO;;CAGlC,SAAoB;AAYlB,SAXI,KAAK,MAAM,WACT,KAAK,MAAM,aAAa,KAAA,IAE1B,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD;IACE,OAAO,KAAK,MAAM,SAAS;IAC3B,OAAO,KAAK,MAAM,SAAS,KAAA;IAC3B,CAAA;GACE,CAAA,GAPsC,KAAK,MAAM,WAUpD,KAAK,MAAM"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface CodeEditorProps {
|
|
2
|
+
value: string;
|
|
3
|
+
onChange: (value: string) => void;
|
|
4
|
+
/** Language slug, e.g. "python"; switches syntax highlighting in place. */
|
|
5
|
+
language: string;
|
|
6
|
+
/** Accessible name for the editing surface. */
|
|
7
|
+
ariaLabel: string;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
/** Mod-Enter (Ctrl/Cmd+Enter). */
|
|
10
|
+
onRun?: () => void;
|
|
11
|
+
/** Mod-Shift-Enter. */
|
|
12
|
+
onSubmit?: () => void;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A CodeMirror 6 code editor (BOFF-4078): line numbers, syntax highlighting
|
|
17
|
+
* for every grader language, bracket matching/closing, undo history and Tab
|
|
18
|
+
* indentation. Press Escape and then Tab to move focus out of the editor.
|
|
19
|
+
*
|
|
20
|
+
* The editor is created once per mount; `value` and `language` changes from
|
|
21
|
+
* the parent (switching language, resetting to the starter code) are applied
|
|
22
|
+
* as transactions so the learner's undo history and cursor survive.
|
|
23
|
+
*/
|
|
24
|
+
export declare function CodeEditor({ value, onChange, language, ariaLabel, placeholder, onRun, onSubmit, className, }: CodeEditorProps): import("react").JSX.Element;
|
|
25
|
+
export default CodeEditor;
|
|
26
|
+
//# sourceMappingURL=CodeEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeEditor.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/editor/CodeEditor.tsx"],"names":[],"mappings":"AAkBA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,uBAAuB;IACvB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE,eAAe,+BAkGjB;AAED,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { Compartment as t, EditorState as n, Prec as r } from "../../../node_modules/@codemirror/state/dist/index.js";
|
|
3
|
+
import { EditorView as i, drawSelection as a, highlightActiveLine as o, highlightActiveLineGutter as s, keymap as c, lineNumbers as l, placeholder as u } from "../../../node_modules/@codemirror/view/dist/index.js";
|
|
4
|
+
import { bracketMatching as d, indentOnInput as f, syntaxHighlighting as p } from "../../../node_modules/@codemirror/language/dist/index.js";
|
|
5
|
+
import { defaultKeymap as m, history as h, historyKeymap as g, indentWithTab as _ } from "../../../node_modules/@codemirror/commands/dist/index.js";
|
|
6
|
+
import { closeBrackets as v, closeBracketsKeymap as y } from "../../../node_modules/@codemirror/autocomplete/dist/index.js";
|
|
7
|
+
import { languageExtension as b } from "./editorLanguages.js";
|
|
8
|
+
import { technospamEditorTheme as x, technospamHighlightStyle as S } from "./editorTheme.js";
|
|
9
|
+
import { useEffect as C, useLayoutEffect as w, useRef as T } from "react";
|
|
10
|
+
import { jsx as E } from "react/jsx-runtime";
|
|
11
|
+
//#region src/technospam/components/editor/CodeEditor.tsx
|
|
12
|
+
function D({ value: D, onChange: O, language: k, ariaLabel: A, placeholder: j, onRun: M, onSubmit: N, className: P }) {
|
|
13
|
+
let F = T(null), I = T(null), L = T(new t()), R = T({
|
|
14
|
+
value: D,
|
|
15
|
+
language: k,
|
|
16
|
+
ariaLabel: A,
|
|
17
|
+
placeholder: j,
|
|
18
|
+
onChange: O,
|
|
19
|
+
onRun: M,
|
|
20
|
+
onSubmit: N
|
|
21
|
+
});
|
|
22
|
+
return w(() => {
|
|
23
|
+
R.current = {
|
|
24
|
+
value: D,
|
|
25
|
+
language: k,
|
|
26
|
+
ariaLabel: A,
|
|
27
|
+
placeholder: j,
|
|
28
|
+
onChange: O,
|
|
29
|
+
onRun: M,
|
|
30
|
+
onSubmit: N
|
|
31
|
+
};
|
|
32
|
+
}), C(() => {
|
|
33
|
+
let e = F.current;
|
|
34
|
+
if (!e) return;
|
|
35
|
+
let t = R.current, C = R, w = new i({
|
|
36
|
+
parent: e,
|
|
37
|
+
state: n.create({
|
|
38
|
+
doc: t.value,
|
|
39
|
+
extensions: [
|
|
40
|
+
l(),
|
|
41
|
+
s(),
|
|
42
|
+
h(),
|
|
43
|
+
a(),
|
|
44
|
+
f(),
|
|
45
|
+
d(),
|
|
46
|
+
v(),
|
|
47
|
+
o(),
|
|
48
|
+
n.tabSize.of(4),
|
|
49
|
+
p(S),
|
|
50
|
+
x,
|
|
51
|
+
L.current.of(b(t.language)),
|
|
52
|
+
t.placeholder ? u(t.placeholder) : [],
|
|
53
|
+
i.contentAttributes.of({
|
|
54
|
+
"aria-label": t.ariaLabel,
|
|
55
|
+
"aria-multiline": "true",
|
|
56
|
+
spellcheck: "false",
|
|
57
|
+
autocorrect: "off",
|
|
58
|
+
autocapitalize: "off"
|
|
59
|
+
}),
|
|
60
|
+
r.highest(c.of([{
|
|
61
|
+
key: "Mod-Enter",
|
|
62
|
+
run: () => (C.current.onRun?.(), !!C.current.onRun)
|
|
63
|
+
}, {
|
|
64
|
+
key: "Mod-Shift-Enter",
|
|
65
|
+
run: () => (C.current.onSubmit?.(), !!C.current.onSubmit)
|
|
66
|
+
}])),
|
|
67
|
+
c.of([
|
|
68
|
+
...y,
|
|
69
|
+
...m,
|
|
70
|
+
...g,
|
|
71
|
+
_
|
|
72
|
+
]),
|
|
73
|
+
i.updateListener.of((e) => {
|
|
74
|
+
e.docChanged && C.current.onChange(e.state.doc.toString());
|
|
75
|
+
})
|
|
76
|
+
]
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
return I.current = w, () => {
|
|
80
|
+
w.destroy(), I.current = null;
|
|
81
|
+
};
|
|
82
|
+
}, []), C(() => {
|
|
83
|
+
let e = I.current;
|
|
84
|
+
if (!e) return;
|
|
85
|
+
let t = e.state.doc.toString();
|
|
86
|
+
t !== D && e.dispatch({ changes: {
|
|
87
|
+
from: 0,
|
|
88
|
+
to: t.length,
|
|
89
|
+
insert: D
|
|
90
|
+
} });
|
|
91
|
+
}, [D]), C(() => {
|
|
92
|
+
I.current?.dispatch({ effects: L.current.reconfigure(b(k)) });
|
|
93
|
+
}, [k]), /* @__PURE__ */ E("div", {
|
|
94
|
+
ref: F,
|
|
95
|
+
"data-testid": "code-editor",
|
|
96
|
+
className: e("font-mono [&_.cm-editor]:h-full [&_.cm-scroller]:overflow-auto", P)
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { D as CodeEditor, D as default };
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=CodeEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeEditor.js","names":[],"sources":["../../../../src/technospam/components/editor/CodeEditor.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef } from 'react';\nimport { Compartment, EditorState, Prec } from '@codemirror/state';\nimport {\n EditorView,\n drawSelection,\n highlightActiveLine,\n highlightActiveLineGutter,\n keymap,\n lineNumbers,\n placeholder as placeholderExtension,\n} from '@codemirror/view';\nimport { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands';\nimport { bracketMatching, indentOnInput, syntaxHighlighting } from '@codemirror/language';\nimport { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';\nimport { languageExtension } from './editorLanguages';\nimport { technospamEditorTheme, technospamHighlightStyle } from './editorTheme';\nimport { cn } from '../../utils/cn';\n\nexport interface CodeEditorProps {\n value: string;\n onChange: (value: string) => void;\n /** Language slug, e.g. \"python\"; switches syntax highlighting in place. */\n language: string;\n /** Accessible name for the editing surface. */\n ariaLabel: string;\n placeholder?: string;\n /** Mod-Enter (Ctrl/Cmd+Enter). */\n onRun?: () => void;\n /** Mod-Shift-Enter. */\n onSubmit?: () => void;\n className?: string;\n}\n\n/**\n * A CodeMirror 6 code editor (BOFF-4078): line numbers, syntax highlighting\n * for every grader language, bracket matching/closing, undo history and Tab\n * indentation. Press Escape and then Tab to move focus out of the editor.\n *\n * The editor is created once per mount; `value` and `language` changes from\n * the parent (switching language, resetting to the starter code) are applied\n * as transactions so the learner's undo history and cursor survive.\n */\nexport function CodeEditor({\n value,\n onChange,\n language,\n ariaLabel,\n placeholder,\n onRun,\n onSubmit,\n className,\n}: CodeEditorProps) {\n const hostRef = useRef<HTMLDivElement | null>(null);\n const viewRef = useRef<EditorView | null>(null);\n const languageCompartment = useRef(new Compartment());\n // Latest props, read by the editor's long-lived listeners and by the\n // one-time setup below (refs are written after render, never during it).\n const latest = useRef({ value, language, ariaLabel, placeholder, onChange, onRun, onSubmit });\n useLayoutEffect(() => {\n latest.current = { value, language, ariaLabel, placeholder, onChange, onRun, onSubmit };\n });\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return undefined;\n const initial = latest.current;\n const callbacks = latest;\n const view = new EditorView({\n parent: host,\n state: EditorState.create({\n doc: initial.value,\n extensions: [\n lineNumbers(),\n highlightActiveLineGutter(),\n history(),\n drawSelection(),\n indentOnInput(),\n bracketMatching(),\n closeBrackets(),\n highlightActiveLine(),\n EditorState.tabSize.of(4),\n syntaxHighlighting(technospamHighlightStyle),\n technospamEditorTheme,\n languageCompartment.current.of(languageExtension(initial.language)),\n initial.placeholder ? placeholderExtension(initial.placeholder) : [],\n EditorView.contentAttributes.of({\n 'aria-label': initial.ariaLabel,\n 'aria-multiline': 'true',\n spellcheck: 'false',\n autocorrect: 'off',\n autocapitalize: 'off',\n }),\n Prec.highest(\n keymap.of([\n {\n key: 'Mod-Enter',\n run: () => {\n callbacks.current.onRun?.();\n return Boolean(callbacks.current.onRun);\n },\n },\n {\n key: 'Mod-Shift-Enter',\n run: () => {\n callbacks.current.onSubmit?.();\n return Boolean(callbacks.current.onSubmit);\n },\n },\n ])\n ),\n keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...historyKeymap, indentWithTab]),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) callbacks.current.onChange(update.state.doc.toString());\n }),\n ],\n }),\n });\n viewRef.current = view;\n return () => {\n view.destroy();\n viewRef.current = null;\n };\n // Built once per mount on purpose: later `value`/`language` changes are\n // applied by the effects below as transactions, and the callbacks are read\n // through `latest.current`, so rebuilding would only lose undo history.\n }, []);\n\n useEffect(() => {\n const view = viewRef.current;\n if (!view) return;\n const current = view.state.doc.toString();\n if (current !== value) {\n view.dispatch({ changes: { from: 0, to: current.length, insert: value } });\n }\n }, [value]);\n\n useEffect(() => {\n viewRef.current?.dispatch({\n effects: languageCompartment.current.reconfigure(languageExtension(language)),\n });\n }, [language]);\n\n return (\n <div\n ref={hostRef}\n data-testid=\"code-editor\"\n className={cn('font-mono [&_.cm-editor]:h-full [&_.cm-scroller]:overflow-auto', className)}\n />\n );\n}\n\nexport default CodeEditor;\n"],"mappings":";;;;;;;;;;;AA0CA,SAAgB,EAAW,EACzB,UACA,aACA,aACA,cACA,aAAA,GACA,UACA,aACA,gBACkB;CAClB,IAAM,IAAU,EAA8B,KAAK,EAC7C,IAAU,EAA0B,KAAK,EACzC,IAAsB,EAAO,IAAI,GAAa,CAAC,EAG/C,IAAS,EAAO;EAAE;EAAO;EAAU;EAAW,aAAA;EAAa;EAAU;EAAO;EAAU,CAAC;AAqF7F,QApFA,QAAsB;AACpB,IAAO,UAAU;GAAE;GAAO;GAAU;GAAW,aAAA;GAAa;GAAU;GAAO;GAAU;GACvF,EAEF,QAAgB;EACd,IAAM,IAAO,EAAQ;AACrB,MAAI,CAAC,EAAM;EACX,IAAM,IAAU,EAAO,SACjB,IAAY,GACZ,IAAO,IAAI,EAAW;GAC1B,QAAQ;GACR,OAAO,EAAY,OAAO;IACxB,KAAK,EAAQ;IACb,YAAY;KACV,GAAa;KACb,GAA2B;KAC3B,GAAS;KACT,GAAe;KACf,GAAe;KACf,GAAiB;KACjB,GAAe;KACf,GAAqB;KACrB,EAAY,QAAQ,GAAG,EAAE;KACzB,EAAmB,EAAyB;KAC5C;KACA,EAAoB,QAAQ,GAAG,EAAkB,EAAQ,SAAS,CAAC;KACnE,EAAQ,cAAc,EAAqB,EAAQ,YAAY,GAAG,EAAE;KACpE,EAAW,kBAAkB,GAAG;MAC9B,cAAc,EAAQ;MACtB,kBAAkB;MAClB,YAAY;MACZ,aAAa;MACb,gBAAgB;MACjB,CAAC;KACF,EAAK,QACH,EAAO,GAAG,CACR;MACE,KAAK;MACL,YACE,EAAU,QAAQ,SAAS,EACpB,EAAQ,EAAU,QAAQ;MAEpC,EACD;MACE,KAAK;MACL,YACE,EAAU,QAAQ,YAAY,EACvB,EAAQ,EAAU,QAAQ;MAEpC,CACF,CAAC,CACH;KACD,EAAO,GAAG;MAAC,GAAG;MAAqB,GAAG;MAAe,GAAG;MAAe;MAAc,CAAC;KACtF,EAAW,eAAe,IAAI,MAAW;AACvC,MAAI,EAAO,cAAY,EAAU,QAAQ,SAAS,EAAO,MAAM,IAAI,UAAU,CAAC;OAC9E;KACH;IACF,CAAC;GACH,CAAC;AAEF,SADA,EAAQ,UAAU,SACL;AAEX,GADA,EAAK,SAAS,EACd,EAAQ,UAAU;;IAKnB,EAAE,CAAC,EAEN,QAAgB;EACd,IAAM,IAAO,EAAQ;AACrB,MAAI,CAAC,EAAM;EACX,IAAM,IAAU,EAAK,MAAM,IAAI,UAAU;AACzC,EAAI,MAAY,KACd,EAAK,SAAS,EAAE,SAAS;GAAE,MAAM;GAAG,IAAI,EAAQ;GAAQ,QAAQ;GAAO,EAAE,CAAC;IAE3E,CAAC,EAAM,CAAC,EAEX,QAAgB;AACd,IAAQ,SAAS,SAAS,EACxB,SAAS,EAAoB,QAAQ,YAAY,EAAkB,EAAS,CAAC,EAC9E,CAAC;IACD,CAAC,EAAS,CAAC,EAGZ,kBAAC,OAAD;EACE,KAAK;EACL,eAAY;EACZ,WAAW,EAAG,kEAAkE,EAAU;EAC1F,CAAA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TecSolutionRun } from '../../api/technospamApi';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Output of the editor's Run button (BOFF-4078): a warm-up notice while the
|
|
5
|
+
* sandbox starts, the per-sample-case verdicts, or the raw stdout/stderr of a
|
|
6
|
+
* custom-input run.
|
|
7
|
+
*/
|
|
8
|
+
export declare function RunOutputPanel({ run, running, warming, error, }: {
|
|
9
|
+
run: TecSolutionRun | null;
|
|
10
|
+
running: boolean;
|
|
11
|
+
warming: boolean;
|
|
12
|
+
error: string | null;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=RunOutputPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunOutputPanel.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/editor/RunOutputPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,yBAAyB,CAAC;AAWpF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,OAAO,EACP,OAAO,EACP,KAAK,GACN,EAAE;IACD,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,+BA0FA"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { CodePanel as t, IoBlock as n, TestResultsPanel as r } from "../SubmissionViews.js";
|
|
3
|
+
import { AlertTriangle as i, Clock as a, Loader2 as o, Terminal as s } from "lucide-react";
|
|
4
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
5
|
+
//#region src/technospam/components/editor/RunOutputPanel.tsx
|
|
6
|
+
function u(e) {
|
|
7
|
+
return e.compileFailed ? {
|
|
8
|
+
ok: !1,
|
|
9
|
+
text: "Compile error"
|
|
10
|
+
} : e.timedOut ? {
|
|
11
|
+
ok: !1,
|
|
12
|
+
text: "Time limit exceeded"
|
|
13
|
+
} : e.memoryLimitExceeded ? {
|
|
14
|
+
ok: !1,
|
|
15
|
+
text: "Memory limit exceeded"
|
|
16
|
+
} : e.exitCode === 0 ? {
|
|
17
|
+
ok: !0,
|
|
18
|
+
text: "Ran successfully"
|
|
19
|
+
} : {
|
|
20
|
+
ok: !1,
|
|
21
|
+
text: `Exited with code ${e.exitCode}`
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function d({ run: d, running: f, warming: p, error: m }) {
|
|
25
|
+
if (p) return /* @__PURE__ */ l("p", {
|
|
26
|
+
role: "status",
|
|
27
|
+
className: "inline-flex items-center gap-2 text-sm text-text-secondary",
|
|
28
|
+
"data-testid": "run-warming",
|
|
29
|
+
children: [/* @__PURE__ */ c(o, {
|
|
30
|
+
size: 14,
|
|
31
|
+
className: "animate-spin"
|
|
32
|
+
}), "Starting the code sandbox — your run will start automatically in a moment."]
|
|
33
|
+
});
|
|
34
|
+
if (f && !d) return /* @__PURE__ */ l("p", {
|
|
35
|
+
role: "status",
|
|
36
|
+
className: "inline-flex items-center gap-2 text-sm text-text-secondary",
|
|
37
|
+
children: [/* @__PURE__ */ c(o, {
|
|
38
|
+
size: 14,
|
|
39
|
+
className: "animate-spin"
|
|
40
|
+
}), " Running your code…"]
|
|
41
|
+
});
|
|
42
|
+
if (m) return /* @__PURE__ */ l("p", {
|
|
43
|
+
role: "alert",
|
|
44
|
+
className: "inline-flex items-start gap-2 text-sm text-status-error-text",
|
|
45
|
+
"data-testid": "run-error",
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ c(i, {
|
|
48
|
+
size: 14,
|
|
49
|
+
className: "mt-0.5 shrink-0"
|
|
50
|
+
}),
|
|
51
|
+
" ",
|
|
52
|
+
m
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
if (!d) return /* @__PURE__ */ c("p", {
|
|
56
|
+
className: "text-sm text-text-muted",
|
|
57
|
+
children: "Run your code against the sample test cases (or your own input) before you submit. Runs are not recorded and do not affect your score."
|
|
58
|
+
});
|
|
59
|
+
if (d.status !== "COMPLETED") return /* @__PURE__ */ l("p", {
|
|
60
|
+
role: "alert",
|
|
61
|
+
className: "inline-flex items-start gap-2 text-sm text-status-warning-text",
|
|
62
|
+
children: [/* @__PURE__ */ c(i, {
|
|
63
|
+
size: 14,
|
|
64
|
+
className: "mt-0.5 shrink-0"
|
|
65
|
+
}), "The code sandbox is not available right now. Please try again shortly."]
|
|
66
|
+
});
|
|
67
|
+
if (d.customOutput) {
|
|
68
|
+
let r = d.customOutput, i = u(r);
|
|
69
|
+
return /* @__PURE__ */ l("div", {
|
|
70
|
+
className: "space-y-3",
|
|
71
|
+
"data-testid": "run-custom-output",
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ l("div", {
|
|
74
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1",
|
|
75
|
+
children: [/* @__PURE__ */ l("span", {
|
|
76
|
+
className: e("inline-flex items-center gap-1.5 text-sm font-medium", i.ok ? "text-status-success-text" : "text-status-error-text"),
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ c(s, { size: 14 }),
|
|
79
|
+
" ",
|
|
80
|
+
i.text
|
|
81
|
+
]
|
|
82
|
+
}), /* @__PURE__ */ l("span", {
|
|
83
|
+
className: "inline-flex items-center gap-1 text-xs text-text-muted",
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ c(a, { size: 12 }),
|
|
86
|
+
" ",
|
|
87
|
+
r.durationMs,
|
|
88
|
+
" ms"
|
|
89
|
+
]
|
|
90
|
+
})]
|
|
91
|
+
}),
|
|
92
|
+
/* @__PURE__ */ l("div", {
|
|
93
|
+
className: "grid grid-cols-1 gap-2 sm:grid-cols-2",
|
|
94
|
+
children: [/* @__PURE__ */ c(n, {
|
|
95
|
+
label: "Input",
|
|
96
|
+
value: r.stdin
|
|
97
|
+
}), /* @__PURE__ */ c(n, {
|
|
98
|
+
label: "Output",
|
|
99
|
+
value: r.stdout
|
|
100
|
+
})]
|
|
101
|
+
}),
|
|
102
|
+
r.stderr ? /* @__PURE__ */ c(t, {
|
|
103
|
+
label: "stderr",
|
|
104
|
+
bodyClassName: "max-h-48",
|
|
105
|
+
children: r.stderr
|
|
106
|
+
}) : null
|
|
107
|
+
]
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return d.verdict ? /* @__PURE__ */ c("div", {
|
|
111
|
+
"data-testid": "run-sample-results",
|
|
112
|
+
children: /* @__PURE__ */ c(r, {
|
|
113
|
+
submission: {
|
|
114
|
+
...d,
|
|
115
|
+
verdict: d.verdict
|
|
116
|
+
},
|
|
117
|
+
caseNoun: "sample test cases"
|
|
118
|
+
})
|
|
119
|
+
}) : /* @__PURE__ */ c("p", {
|
|
120
|
+
className: "text-sm text-text-muted",
|
|
121
|
+
children: "Nothing ran."
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { d as RunOutputPanel };
|
|
126
|
+
|
|
127
|
+
//# sourceMappingURL=RunOutputPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RunOutputPanel.js","names":[],"sources":["../../../../src/technospam/components/editor/RunOutputPanel.tsx"],"sourcesContent":["import { AlertTriangle, Clock, Loader2, Terminal } from 'lucide-react';\nimport { CodePanel, IoBlock, TestResultsPanel } from '../SubmissionViews';\nimport type { TecSolutionRun, TecSolutionRunOutput } from '../../api/technospamApi';\nimport { cn } from '../../utils/cn';\n\nfunction customRunSummary(output: TecSolutionRunOutput): { ok: boolean; text: string } {\n if (output.compileFailed) return { ok: false, text: 'Compile error' };\n if (output.timedOut) return { ok: false, text: 'Time limit exceeded' };\n if (output.memoryLimitExceeded) return { ok: false, text: 'Memory limit exceeded' };\n if (output.exitCode !== 0) return { ok: false, text: `Exited with code ${output.exitCode}` };\n return { ok: true, text: 'Ran successfully' };\n}\n\n/**\n * Output of the editor's Run button (BOFF-4078): a warm-up notice while the\n * sandbox starts, the per-sample-case verdicts, or the raw stdout/stderr of a\n * custom-input run.\n */\nexport function RunOutputPanel({\n run,\n running,\n warming,\n error,\n}: {\n run: TecSolutionRun | null;\n running: boolean;\n warming: boolean;\n error: string | null;\n}) {\n if (warming) {\n return (\n <p\n role=\"status\"\n className=\"inline-flex items-center gap-2 text-sm text-text-secondary\"\n data-testid=\"run-warming\"\n >\n <Loader2 size={14} className=\"animate-spin\" />\n Starting the code sandbox — your run will start automatically in a moment.\n </p>\n );\n }\n if (running && !run) {\n return (\n <p role=\"status\" className=\"inline-flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 size={14} className=\"animate-spin\" /> Running your code…\n </p>\n );\n }\n if (error) {\n return (\n <p\n role=\"alert\"\n className=\"inline-flex items-start gap-2 text-sm text-status-error-text\"\n data-testid=\"run-error\"\n >\n <AlertTriangle size={14} className=\"mt-0.5 shrink-0\" /> {error}\n </p>\n );\n }\n if (!run) {\n return (\n <p className=\"text-sm text-text-muted\">\n Run your code against the sample test cases (or your own input) before you submit. Runs are\n not recorded and do not affect your score.\n </p>\n );\n }\n if (run.status !== 'COMPLETED') {\n return (\n <p role=\"alert\" className=\"inline-flex items-start gap-2 text-sm text-status-warning-text\">\n <AlertTriangle size={14} className=\"mt-0.5 shrink-0\" />\n The code sandbox is not available right now. Please try again shortly.\n </p>\n );\n }\n\n if (run.customOutput) {\n const output = run.customOutput;\n const summary = customRunSummary(output);\n return (\n <div className=\"space-y-3\" data-testid=\"run-custom-output\">\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1\">\n <span\n className={cn(\n 'inline-flex items-center gap-1.5 text-sm font-medium',\n summary.ok ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n <Terminal size={14} /> {summary.text}\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {output.durationMs} ms\n </span>\n </div>\n <div className=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n <IoBlock label=\"Input\" value={output.stdin} />\n <IoBlock label=\"Output\" value={output.stdout} />\n </div>\n {output.stderr ? (\n <CodePanel label=\"stderr\" bodyClassName=\"max-h-48\">\n {output.stderr}\n </CodePanel>\n ) : null}\n </div>\n );\n }\n\n if (run.verdict) {\n return (\n <div data-testid=\"run-sample-results\">\n <TestResultsPanel\n submission={{ ...run, verdict: run.verdict }}\n caseNoun=\"sample test cases\"\n />\n </div>\n );\n }\n return <p className=\"text-sm text-text-muted\">Nothing ran.</p>;\n}\n"],"mappings":";;;;;AAKA,SAAS,EAAiB,GAA6D;AAKrF,QAJI,EAAO,gBAAsB;EAAE,IAAI;EAAO,MAAM;EAAiB,GACjE,EAAO,WAAiB;EAAE,IAAI;EAAO,MAAM;EAAuB,GAClE,EAAO,sBAA4B;EAAE,IAAI;EAAO,MAAM;EAAyB,GAC/E,EAAO,aAAa,IACjB;EAAE,IAAI;EAAM,MAAM;EAAoB,GADX;EAAE,IAAI;EAAO,MAAM,oBAAoB,EAAO;EAAY;;AAS9F,SAAgB,EAAe,EAC7B,QACA,YACA,YACA,YAMC;AACD,KAAI,EACF,QACE,kBAAC,KAAD;EACE,MAAK;EACL,WAAU;EACV,eAAY;YAHd,CAKE,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAiB,CAAA,EAAA,6EAE5C;;AAGR,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,KAAD;EAAG,MAAK;EAAS,WAAU;YAA3B,CACE,kBAAC,GAAD;GAAS,MAAM;GAAI,WAAU;GAAiB,CAAA,EAAA,sBAC5C;;AAGR,KAAI,EACF,QACE,kBAAC,KAAD;EACE,MAAK;EACL,WAAU;EACV,eAAY;YAHd;GAKE,kBAAC,GAAD;IAAe,MAAM;IAAI,WAAU;IAAoB,CAAA;;GAAE;GACvD;;AAGR,KAAI,CAAC,EACH,QACE,kBAAC,KAAD;EAAG,WAAU;YAA0B;EAGnC,CAAA;AAGR,KAAI,EAAI,WAAW,YACjB,QACE,kBAAC,KAAD;EAAG,MAAK;EAAQ,WAAU;YAA1B,CACE,kBAAC,GAAD;GAAe,MAAM;GAAI,WAAU;GAAoB,CAAA,EAAA,yEAErD;;AAIR,KAAI,EAAI,cAAc;EACpB,IAAM,IAAS,EAAI,cACb,IAAU,EAAiB,EAAO;AACxC,SACE,kBAAC,OAAD;GAAK,WAAU;GAAY,eAAY;aAAvC;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MACE,WAAW,EACT,wDACA,EAAQ,KAAK,6BAA6B,yBAC3C;gBAJH;OAME,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;;OAAE,EAAQ;OAC3B;SACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;OAAE,EAAO;OAAW;OAClC;QACH;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAS,OAAM;MAAQ,OAAO,EAAO;MAAS,CAAA,EAC9C,kBAAC,GAAD;MAAS,OAAM;MAAS,OAAO,EAAO;MAAU,CAAA,CAC5C;;IACL,EAAO,SACN,kBAAC,GAAD;KAAW,OAAM;KAAS,eAAc;eACrC,EAAO;KACE,CAAA,GACV;IACA;;;AAcV,QAVI,EAAI,UAEJ,kBAAC,OAAD;EAAK,eAAY;YACf,kBAAC,GAAD;GACE,YAAY;IAAE,GAAG;IAAK,SAAS,EAAI;IAAS;GAC5C,UAAS;GACT,CAAA;EACE,CAAA,GAGH,kBAAC,KAAD;EAAG,WAAU;YAA0B;EAAgB,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Extension } from '@codemirror/state';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Syntax support for every language slug the grading sandbox runs
|
|
5
|
+
* (wspace-technospam-svc `SUPPORTED_LANGUAGES`). Unknown slugs get plain-text
|
|
6
|
+
* editing — line numbers, brackets and history still work.
|
|
7
|
+
*/
|
|
8
|
+
export declare function languageExtension(language: string): Extension;
|
|
9
|
+
/** Whether `languageExtension` highlights this slug (used by tests and the editor label). */
|
|
10
|
+
export declare function hasSyntaxSupport(language: string): boolean;
|
|
11
|
+
//# sourceMappingURL=editorLanguages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorLanguages.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/editor/editorLanguages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAQnD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAyB7D;AAED,6FAA6F;AAC7F,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG1D"}
|