@burdenoff/microfe-technospam 2026.1102.1 → 2026.1102.2
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/api/technospamApi.d.ts +39 -0
- package/dist/technospam/api/technospamApi.d.ts.map +1 -1
- package/dist/technospam/api/technospamApi.js +120 -86
- package/dist/technospam/api/technospamApi.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/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/ApplicationsProblemDetailsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +416 -263
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/package.json +14 -4
|
@@ -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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { StreamLanguage as e } from "../../../node_modules/@codemirror/language/dist/index.js";
|
|
2
|
+
import { javascript as t } from "../../../node_modules/@codemirror/lang-javascript/dist/index.js";
|
|
3
|
+
import { python as n } from "../../../node_modules/@codemirror/lang-python/dist/index.js";
|
|
4
|
+
import { go as r } from "../../../node_modules/@codemirror/lang-go/dist/index.js";
|
|
5
|
+
import { ruby as i } from "../../../node_modules/@codemirror/legacy-modes/mode/ruby.js";
|
|
6
|
+
import { shell as a } from "../../../node_modules/@codemirror/legacy-modes/mode/shell.js";
|
|
7
|
+
//#region src/technospam/components/editor/editorLanguages.ts
|
|
8
|
+
function o(o) {
|
|
9
|
+
switch (o.trim().toLowerCase()) {
|
|
10
|
+
case "javascript":
|
|
11
|
+
case "js":
|
|
12
|
+
case "node": return t();
|
|
13
|
+
case "typescript":
|
|
14
|
+
case "ts": return t({ typescript: !0 });
|
|
15
|
+
case "python":
|
|
16
|
+
case "py": return n();
|
|
17
|
+
case "go":
|
|
18
|
+
case "golang": return r();
|
|
19
|
+
case "ruby":
|
|
20
|
+
case "rb": return e.define(i);
|
|
21
|
+
case "bash":
|
|
22
|
+
case "sh":
|
|
23
|
+
case "shell": return e.define(a);
|
|
24
|
+
default: return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { o as languageExtension };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=editorLanguages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorLanguages.js","names":[],"sources":["../../../../src/technospam/components/editor/editorLanguages.ts"],"sourcesContent":["import type { Extension } from '@codemirror/state';\nimport { StreamLanguage } from '@codemirror/language';\nimport { javascript } from '@codemirror/lang-javascript';\nimport { python } from '@codemirror/lang-python';\nimport { go } from '@codemirror/lang-go';\nimport { ruby } from '@codemirror/legacy-modes/mode/ruby';\nimport { shell } from '@codemirror/legacy-modes/mode/shell';\n\n/**\n * Syntax support for every language slug the grading sandbox runs\n * (wspace-technospam-svc `SUPPORTED_LANGUAGES`). Unknown slugs get plain-text\n * editing — line numbers, brackets and history still work.\n */\nexport function languageExtension(language: string): Extension {\n switch (language.trim().toLowerCase()) {\n case 'javascript':\n case 'js':\n case 'node':\n return javascript();\n case 'typescript':\n case 'ts':\n return javascript({ typescript: true });\n case 'python':\n case 'py':\n return python();\n case 'go':\n case 'golang':\n return go();\n case 'ruby':\n case 'rb':\n return StreamLanguage.define(ruby);\n case 'bash':\n case 'sh':\n case 'shell':\n return StreamLanguage.define(shell);\n default:\n return [];\n }\n}\n\n/** Whether `languageExtension` highlights this slug (used by tests and the editor label). */\nexport function hasSyntaxSupport(language: string): boolean {\n const ext = languageExtension(language);\n return !(Array.isArray(ext) && ext.length === 0);\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,EAAkB,GAA6B;AAC7D,SAAQ,EAAS,MAAM,CAAC,aAAa,EAArC;EACE,KAAK;EACL,KAAK;EACL,KAAK,OACH,QAAO,GAAY;EACrB,KAAK;EACL,KAAK,KACH,QAAO,EAAW,EAAE,YAAY,IAAM,CAAC;EACzC,KAAK;EACL,KAAK,KACH,QAAO,GAAQ;EACjB,KAAK;EACL,KAAK,SACH,QAAO,GAAI;EACb,KAAK;EACL,KAAK,KACH,QAAO,EAAe,OAAO,EAAK;EACpC,KAAK;EACL,KAAK;EACL,KAAK,QACH,QAAO,EAAe,OAAO,EAAM;EACrC,QACE,QAAO,EAAE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { HighlightStyle } from '@codemirror/language';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Syntax colours are Tailwind utility classes over the platform's semantic
|
|
5
|
+
* tokens (never raw colours), so the editor follows the active brand and
|
|
6
|
+
* light/dark theme like the rest of the app.
|
|
7
|
+
*/
|
|
8
|
+
export declare const technospamHighlightStyle: HighlightStyle;
|
|
9
|
+
/** Editor chrome: inherits the surrounding panel and uses semantic token variables. */
|
|
10
|
+
export declare const technospamEditorTheme: import('@codemirror/state').Extension;
|
|
11
|
+
//# sourceMappingURL=editorTheme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorTheme.d.ts","sourceRoot":"","sources":["../../../../src/technospam/components/editor/editorTheme.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,gBAyBnC,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,uCA+BhC,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { EditorView as e } from "../../../node_modules/@codemirror/view/dist/index.js";
|
|
2
|
+
import { tags as t } from "../../../node_modules/@lezer/highlight/dist/index.js";
|
|
3
|
+
import { HighlightStyle as n } from "../../../node_modules/@codemirror/language/dist/index.js";
|
|
4
|
+
//#region src/technospam/components/editor/editorTheme.ts
|
|
5
|
+
var r = n.define([
|
|
6
|
+
{
|
|
7
|
+
tag: [
|
|
8
|
+
t.keyword,
|
|
9
|
+
t.controlKeyword,
|
|
10
|
+
t.moduleKeyword,
|
|
11
|
+
t.operatorKeyword
|
|
12
|
+
],
|
|
13
|
+
class: "text-accent-purple"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
tag: [t.definitionKeyword, t.modifier],
|
|
17
|
+
class: "text-accent-purple"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
tag: [
|
|
21
|
+
t.string,
|
|
22
|
+
t.special(t.string),
|
|
23
|
+
t.regexp
|
|
24
|
+
],
|
|
25
|
+
class: "text-status-success-text"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
tag: [
|
|
29
|
+
t.number,
|
|
30
|
+
t.bool,
|
|
31
|
+
t.null,
|
|
32
|
+
t.atom
|
|
33
|
+
],
|
|
34
|
+
class: "text-status-warning-text"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
tag: [
|
|
38
|
+
t.comment,
|
|
39
|
+
t.lineComment,
|
|
40
|
+
t.blockComment,
|
|
41
|
+
t.docComment
|
|
42
|
+
],
|
|
43
|
+
class: "italic text-text-secondary"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
tag: [
|
|
47
|
+
t.function(t.variableName),
|
|
48
|
+
t.function(t.propertyName),
|
|
49
|
+
t.definition(t.function(t.variableName))
|
|
50
|
+
],
|
|
51
|
+
class: "text-accent-primary"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
tag: [
|
|
55
|
+
t.typeName,
|
|
56
|
+
t.className,
|
|
57
|
+
t.namespace
|
|
58
|
+
],
|
|
59
|
+
class: "text-accent-teal"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
tag: [t.propertyName, t.attributeName],
|
|
63
|
+
class: "text-accent-indigo"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
tag: [t.self, t.special(t.variableName)],
|
|
67
|
+
class: "text-accent-purple"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
tag: [
|
|
71
|
+
t.operator,
|
|
72
|
+
t.punctuation,
|
|
73
|
+
t.bracket
|
|
74
|
+
],
|
|
75
|
+
class: "text-text-secondary"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
tag: t.invalid,
|
|
79
|
+
class: "text-status-error-text"
|
|
80
|
+
}
|
|
81
|
+
]), i = e.theme({
|
|
82
|
+
"&": {
|
|
83
|
+
backgroundColor: "inherit",
|
|
84
|
+
color: "var(--color-text-primary)",
|
|
85
|
+
fontSize: "12px",
|
|
86
|
+
height: "100%"
|
|
87
|
+
},
|
|
88
|
+
"&.cm-focused": { outline: "none" },
|
|
89
|
+
".cm-scroller": {
|
|
90
|
+
fontFamily: "inherit",
|
|
91
|
+
lineHeight: "1.65"
|
|
92
|
+
},
|
|
93
|
+
".cm-content": {
|
|
94
|
+
caretColor: "var(--color-text-primary)",
|
|
95
|
+
padding: "12px 0"
|
|
96
|
+
},
|
|
97
|
+
".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--color-text-primary)" },
|
|
98
|
+
".cm-gutters": {
|
|
99
|
+
backgroundColor: "inherit",
|
|
100
|
+
color: "var(--color-text-muted)",
|
|
101
|
+
border: "none",
|
|
102
|
+
borderRight: "1px solid var(--color-border-subtle)"
|
|
103
|
+
},
|
|
104
|
+
".cm-lineNumbers .cm-gutterElement": { padding: "0 10px 0 12px" },
|
|
105
|
+
".cm-activeLine": { backgroundColor: "var(--color-bg-surface)" },
|
|
106
|
+
".cm-activeLineGutter": {
|
|
107
|
+
backgroundColor: "var(--color-bg-surface)",
|
|
108
|
+
color: "var(--color-text-primary)"
|
|
109
|
+
},
|
|
110
|
+
".cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection": { backgroundColor: "var(--color-bg-accent)" },
|
|
111
|
+
".cm-matchingBracket": {
|
|
112
|
+
backgroundColor: "var(--color-bg-accent)",
|
|
113
|
+
outline: "1px solid var(--color-border-strong)"
|
|
114
|
+
},
|
|
115
|
+
".cm-placeholder": { color: "var(--color-text-placeholder)" }
|
|
116
|
+
});
|
|
117
|
+
//#endregion
|
|
118
|
+
export { i as technospamEditorTheme, r as technospamHighlightStyle };
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=editorTheme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorTheme.js","names":[],"sources":["../../../../src/technospam/components/editor/editorTheme.ts"],"sourcesContent":["import { EditorView } from '@codemirror/view';\nimport { HighlightStyle } from '@codemirror/language';\nimport { tags as t } from '@lezer/highlight';\n\n/**\n * Syntax colours are Tailwind utility classes over the platform's semantic\n * tokens (never raw colours), so the editor follows the active brand and\n * light/dark theme like the rest of the app.\n */\nexport const technospamHighlightStyle = HighlightStyle.define([\n {\n tag: [t.keyword, t.controlKeyword, t.moduleKeyword, t.operatorKeyword],\n class: 'text-accent-purple',\n },\n { tag: [t.definitionKeyword, t.modifier], class: 'text-accent-purple' },\n { tag: [t.string, t.special(t.string), t.regexp], class: 'text-status-success-text' },\n { tag: [t.number, t.bool, t.null, t.atom], class: 'text-status-warning-text' },\n {\n tag: [t.comment, t.lineComment, t.blockComment, t.docComment],\n class: 'italic text-text-secondary',\n },\n {\n tag: [\n t.function(t.variableName),\n t.function(t.propertyName),\n t.definition(t.function(t.variableName)),\n ],\n class: 'text-accent-primary',\n },\n { tag: [t.typeName, t.className, t.namespace], class: 'text-accent-teal' },\n { tag: [t.propertyName, t.attributeName], class: 'text-accent-indigo' },\n { tag: [t.self, t.special(t.variableName)], class: 'text-accent-purple' },\n { tag: [t.operator, t.punctuation, t.bracket], class: 'text-text-secondary' },\n { tag: t.invalid, class: 'text-status-error-text' },\n]);\n\n/** Editor chrome: inherits the surrounding panel and uses semantic token variables. */\nexport const technospamEditorTheme = EditorView.theme({\n '&': {\n backgroundColor: 'inherit',\n color: 'var(--color-text-primary)',\n fontSize: '12px',\n height: '100%',\n },\n '&.cm-focused': { outline: 'none' },\n '.cm-scroller': { fontFamily: 'inherit', lineHeight: '1.65' },\n '.cm-content': { caretColor: 'var(--color-text-primary)', padding: '12px 0' },\n '.cm-cursor, .cm-dropCursor': { borderLeftColor: 'var(--color-text-primary)' },\n '.cm-gutters': {\n backgroundColor: 'inherit',\n color: 'var(--color-text-muted)',\n border: 'none',\n borderRight: '1px solid var(--color-border-subtle)',\n },\n '.cm-lineNumbers .cm-gutterElement': { padding: '0 10px 0 12px' },\n '.cm-activeLine': { backgroundColor: 'var(--color-bg-surface)' },\n '.cm-activeLineGutter': {\n backgroundColor: 'var(--color-bg-surface)',\n color: 'var(--color-text-primary)',\n },\n '.cm-selectionBackground, &.cm-focused .cm-selectionBackground, ::selection': {\n backgroundColor: 'var(--color-bg-accent)',\n },\n '.cm-matchingBracket': {\n backgroundColor: 'var(--color-bg-accent)',\n outline: '1px solid var(--color-border-strong)',\n },\n '.cm-placeholder': { color: 'var(--color-text-placeholder)' },\n});\n"],"mappings":";;;;AASA,IAAa,IAA2B,EAAe,OAAO;CAC5D;EACE,KAAK;GAAC,EAAE;GAAS,EAAE;GAAgB,EAAE;GAAe,EAAE;GAAgB;EACtE,OAAO;EACR;CACD;EAAE,KAAK,CAAC,EAAE,mBAAmB,EAAE,SAAS;EAAE,OAAO;EAAsB;CACvE;EAAE,KAAK;GAAC,EAAE;GAAQ,EAAE,QAAQ,EAAE,OAAO;GAAE,EAAE;GAAO;EAAE,OAAO;EAA4B;CACrF;EAAE,KAAK;GAAC,EAAE;GAAQ,EAAE;GAAM,EAAE;GAAM,EAAE;GAAK;EAAE,OAAO;EAA4B;CAC9E;EACE,KAAK;GAAC,EAAE;GAAS,EAAE;GAAa,EAAE;GAAc,EAAE;GAAW;EAC7D,OAAO;EACR;CACD;EACE,KAAK;GACH,EAAE,SAAS,EAAE,aAAa;GAC1B,EAAE,SAAS,EAAE,aAAa;GAC1B,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC;GACzC;EACD,OAAO;EACR;CACD;EAAE,KAAK;GAAC,EAAE;GAAU,EAAE;GAAW,EAAE;GAAU;EAAE,OAAO;EAAoB;CAC1E;EAAE,KAAK,CAAC,EAAE,cAAc,EAAE,cAAc;EAAE,OAAO;EAAsB;CACvE;EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAC;EAAE,OAAO;EAAsB;CACzE;EAAE,KAAK;GAAC,EAAE;GAAU,EAAE;GAAa,EAAE;GAAQ;EAAE,OAAO;EAAuB;CAC7E;EAAE,KAAK,EAAE;EAAS,OAAO;EAA0B;CACpD,CAAC,EAGW,IAAwB,EAAW,MAAM;CACpD,KAAK;EACH,iBAAiB;EACjB,OAAO;EACP,UAAU;EACV,QAAQ;EACT;CACD,gBAAgB,EAAE,SAAS,QAAQ;CACnC,gBAAgB;EAAE,YAAY;EAAW,YAAY;EAAQ;CAC7D,eAAe;EAAE,YAAY;EAA6B,SAAS;EAAU;CAC7E,8BAA8B,EAAE,iBAAiB,6BAA6B;CAC9E,eAAe;EACb,iBAAiB;EACjB,OAAO;EACP,QAAQ;EACR,aAAa;EACd;CACD,qCAAqC,EAAE,SAAS,iBAAiB;CACjE,kBAAkB,EAAE,iBAAiB,2BAA2B;CAChE,wBAAwB;EACtB,iBAAiB;EACjB,OAAO;EACR;CACD,8EAA8E,EAC5E,iBAAiB,0BAClB;CACD,uBAAuB;EACrB,iBAAiB;EACjB,SAAS;EACV;CACD,mBAAmB,EAAE,OAAO,iCAAiC;CAC9D,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TecSolutionRun } from '../api/technospamApi';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A cold code sandbox can take a few minutes to start. While the API answers
|
|
5
|
+
* WARMING the run is retried on this interval, giving up after the budget so
|
|
6
|
+
* the learner can retry by hand.
|
|
7
|
+
*/
|
|
8
|
+
export declare const RUN_WARMING_RETRY_MS = 4000;
|
|
9
|
+
export declare const RUN_WARMING_BUDGET_MS: number;
|
|
10
|
+
export interface SolutionRunRequest {
|
|
11
|
+
problemId: string;
|
|
12
|
+
language: string;
|
|
13
|
+
code: string;
|
|
14
|
+
/** Custom stdin; omit to run the sample cases. */
|
|
15
|
+
stdin?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface SolutionRunner {
|
|
18
|
+
/** The latest finished (or given-up) run. */
|
|
19
|
+
run: TecSolutionRun | null;
|
|
20
|
+
/** A request is in flight. */
|
|
21
|
+
running: boolean;
|
|
22
|
+
/** The sandbox is starting; the run is being retried automatically. */
|
|
23
|
+
warming: boolean;
|
|
24
|
+
/** Why the last run was refused (quota, bad input…), shown inline. */
|
|
25
|
+
error: string | null;
|
|
26
|
+
execute: (request: SolutionRunRequest) => void;
|
|
27
|
+
reset: () => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Drives `tecRunSolution` for the problem editor (BOFF-4078): sends the run,
|
|
31
|
+
* keeps retrying while the sandbox warms up, and exposes the result. Starting
|
|
32
|
+
* a new run cancels any pending warm-up retry.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useSolutionRunner(): SolutionRunner;
|
|
35
|
+
//# sourceMappingURL=useSolutionRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSolutionRunner.d.ts","sourceRoot":"","sources":["../../../src/technospam/hooks/useSolutionRunner.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3E;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAC1C,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAEhD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IAC3B,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC/C,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CA6GlD"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { userMessageFromError as e } from "../lib/errorMessage.js";
|
|
2
|
+
import { useRunSolution as t } from "../api/technospamApi.js";
|
|
3
|
+
import { useCallback as n, useEffect as r, useRef as i, useState as a } from "react";
|
|
4
|
+
//#region src/technospam/hooks/useSolutionRunner.ts
|
|
5
|
+
var o = 4e3;
|
|
6
|
+
function s() {
|
|
7
|
+
let [s] = t(), [c, l] = a(null), [u, d] = a(!1), [f, p] = a(!1), [m, h] = a(null), g = i(null), _ = i(null), v = i(0), y = () => {
|
|
8
|
+
g.current && clearTimeout(g.current), g.current = null;
|
|
9
|
+
};
|
|
10
|
+
r(() => () => {
|
|
11
|
+
y(), v.current += 1;
|
|
12
|
+
}, []);
|
|
13
|
+
let b = n((t, n) => {
|
|
14
|
+
let r = async () => {
|
|
15
|
+
d(!0);
|
|
16
|
+
try {
|
|
17
|
+
let i = await s({ variables: { input: {
|
|
18
|
+
problemId: t.problemId,
|
|
19
|
+
language: t.language,
|
|
20
|
+
code: t.code,
|
|
21
|
+
...t.stdin == null ? {} : { stdin: t.stdin }
|
|
22
|
+
} } });
|
|
23
|
+
if (n !== v.current) return;
|
|
24
|
+
let a = i.data?.tecRunSolution;
|
|
25
|
+
if (!a) {
|
|
26
|
+
p(!1), _.current = null, l(null), h(e(i.error, "Your code could not be run. Please try again."));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (a.status === "WARMING") {
|
|
30
|
+
if (_.current ??= Date.now(), Date.now() - _.current < 24e4) {
|
|
31
|
+
p(!0), g.current = setTimeout(() => void r(), o);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
p(!1), _.current = null, l({
|
|
35
|
+
...a,
|
|
36
|
+
status: "UNAVAILABLE"
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
p(!1), _.current = null, l(a);
|
|
41
|
+
} catch (t) {
|
|
42
|
+
if (n !== v.current) return;
|
|
43
|
+
p(!1), _.current = null, l(null), h(e(t, "Your code could not be run. Please try again."));
|
|
44
|
+
} finally {
|
|
45
|
+
n === v.current && d(!1);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
return r();
|
|
49
|
+
}, [s]);
|
|
50
|
+
return {
|
|
51
|
+
run: c,
|
|
52
|
+
running: u,
|
|
53
|
+
warming: f,
|
|
54
|
+
error: m,
|
|
55
|
+
execute: n((e) => {
|
|
56
|
+
y(), v.current += 1, _.current = null, h(null), b(e, v.current);
|
|
57
|
+
}, [b]),
|
|
58
|
+
reset: n(() => {
|
|
59
|
+
y(), v.current += 1, _.current = null, l(null), h(null), p(!1), d(!1);
|
|
60
|
+
}, [])
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { s as useSolutionRunner };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=useSolutionRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSolutionRunner.js","names":[],"sources":["../../../src/technospam/hooks/useSolutionRunner.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { useRunSolution, type TecSolutionRun } from '../api/technospamApi';\nimport { userMessageFromError } from '../lib/errorMessage';\n\n/**\n * A cold code sandbox can take a few minutes to start. While the API answers\n * WARMING the run is retried on this interval, giving up after the budget so\n * the learner can retry by hand.\n */\nexport const RUN_WARMING_RETRY_MS = 4_000;\nexport const RUN_WARMING_BUDGET_MS = 4 * 60_000;\n\nexport interface SolutionRunRequest {\n problemId: string;\n language: string;\n code: string;\n /** Custom stdin; omit to run the sample cases. */\n stdin?: string | null;\n}\n\nexport interface SolutionRunner {\n /** The latest finished (or given-up) run. */\n run: TecSolutionRun | null;\n /** A request is in flight. */\n running: boolean;\n /** The sandbox is starting; the run is being retried automatically. */\n warming: boolean;\n /** Why the last run was refused (quota, bad input…), shown inline. */\n error: string | null;\n execute: (request: SolutionRunRequest) => void;\n reset: () => void;\n}\n\n/**\n * Drives `tecRunSolution` for the problem editor (BOFF-4078): sends the run,\n * keeps retrying while the sandbox warms up, and exposes the result. Starting\n * a new run cancels any pending warm-up retry.\n */\nexport function useSolutionRunner(): SolutionRunner {\n const [runSolution] = useRunSolution();\n const [run, setRun] = useState<TecSolutionRun | null>(null);\n const [running, setRunning] = useState(false);\n const [warming, setWarming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const warmingSince = useRef<number | null>(null);\n /** Bumped per user-initiated run so a stale retry never overwrites a newer run. */\n const generation = useRef(0);\n\n const clearTimer = () => {\n if (timer.current) clearTimeout(timer.current);\n timer.current = null;\n };\n\n // On unmount, cancel any pending warm-up retry and invalidate the in-flight\n // request so a late WARMING answer cannot schedule another run after the\n // learner has left the page.\n useEffect(\n () => () => {\n clearTimer();\n generation.current += 1;\n },\n []\n );\n\n const attempt = useCallback(\n (request: SolutionRunRequest, gen: number): Promise<void> => {\n // A local recursive function keeps the warm-up retry inside one closure,\n // so the scheduled retry never references the hook binding itself.\n const go = async (): Promise<void> => {\n setRunning(true);\n try {\n const res = await runSolution({\n variables: {\n input: {\n problemId: request.problemId,\n language: request.language,\n code: request.code,\n ...(request.stdin != null ? { stdin: request.stdin } : {}),\n },\n },\n });\n if (gen !== generation.current) return;\n const result = res.data?.tecRunSolution;\n if (!result) {\n setWarming(false);\n warmingSince.current = null;\n setRun(null);\n setError(\n userMessageFromError(res.error, 'Your code could not be run. Please try again.')\n );\n return;\n }\n if (result.status === 'WARMING') {\n warmingSince.current ??= Date.now();\n if (Date.now() - warmingSince.current < RUN_WARMING_BUDGET_MS) {\n setWarming(true);\n timer.current = setTimeout(() => void go(), RUN_WARMING_RETRY_MS);\n return;\n }\n setWarming(false);\n warmingSince.current = null;\n setRun({ ...result, status: 'UNAVAILABLE' });\n return;\n }\n setWarming(false);\n warmingSince.current = null;\n setRun(result);\n } catch (err: unknown) {\n // Apollo reports GraphQL/network failures via res.error, but anything\n // unexpected must still end the warming state instead of spinning forever.\n if (gen !== generation.current) return;\n setWarming(false);\n warmingSince.current = null;\n setRun(null);\n setError(userMessageFromError(err, 'Your code could not be run. Please try again.'));\n } finally {\n if (gen === generation.current) setRunning(false);\n }\n };\n return go();\n },\n [runSolution]\n );\n\n const execute = useCallback(\n (request: SolutionRunRequest) => {\n clearTimer();\n generation.current += 1;\n warmingSince.current = null;\n setError(null);\n void attempt(request, generation.current);\n },\n [attempt]\n );\n\n const reset = useCallback(() => {\n clearTimer();\n generation.current += 1;\n warmingSince.current = null;\n setRun(null);\n setError(null);\n setWarming(false);\n setRunning(false);\n }, []);\n\n return { run, running, warming, error, execute, reset };\n}\n"],"mappings":";;;;AASA,IAAa,IAAuB;AA6BpC,SAAgB,IAAoC;CAClD,IAAM,CAAC,KAAe,GAAgB,EAChC,CAAC,GAAK,KAAU,EAAgC,KAAK,EACrD,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAQ,EAA6C,KAAK,EAC1D,IAAe,EAAsB,KAAK,EAE1C,IAAa,EAAO,EAAE,EAEtB,UAAmB;AAEvB,EADI,EAAM,WAAS,aAAa,EAAM,QAAQ,EAC9C,EAAM,UAAU;;AAMlB,eACc;AAEV,EADA,GAAY,EACZ,EAAW,WAAW;IAExB,EAAE,CACH;CAED,IAAM,IAAU,GACb,GAA6B,MAA+B;EAG3D,IAAM,IAAK,YAA2B;AACpC,KAAW,GAAK;AAChB,OAAI;IACF,IAAM,IAAM,MAAM,EAAY,EAC5B,WAAW,EACT,OAAO;KACL,WAAW,EAAQ;KACnB,UAAU,EAAQ;KAClB,MAAM,EAAQ;KACd,GAAI,EAAQ,SAAS,OAAkC,EAAE,GAA7B,EAAE,OAAO,EAAQ,OAAO;KACrD,EACF,EACF,CAAC;AACF,QAAI,MAAQ,EAAW,QAAS;IAChC,IAAM,IAAS,EAAI,MAAM;AACzB,QAAI,CAAC,GAAQ;AAIX,KAHA,EAAW,GAAM,EACjB,EAAa,UAAU,MACvB,EAAO,KAAK,EACZ,EACE,EAAqB,EAAI,OAAO,gDAAgD,CACjF;AACD;;AAEF,QAAI,EAAO,WAAW,WAAW;AAE/B,SADA,EAAa,YAAY,KAAK,KAAK,EAC/B,KAAK,KAAK,GAAG,EAAa,UAAA,MAAiC;AAE7D,MADA,EAAW,GAAK,EAChB,EAAM,UAAU,iBAAiB,KAAK,GAAI,EAAE,EAAqB;AACjE;;AAIF,KAFA,EAAW,GAAM,EACjB,EAAa,UAAU,MACvB,EAAO;MAAE,GAAG;MAAQ,QAAQ;MAAe,CAAC;AAC5C;;AAIF,IAFA,EAAW,GAAM,EACjB,EAAa,UAAU,MACvB,EAAO,EAAO;YACP,GAAc;AAGrB,QAAI,MAAQ,EAAW,QAAS;AAIhC,IAHA,EAAW,GAAM,EACjB,EAAa,UAAU,MACvB,EAAO,KAAK,EACZ,EAAS,EAAqB,GAAK,gDAAgD,CAAC;aAC5E;AACR,IAAI,MAAQ,EAAW,WAAS,EAAW,GAAM;;;AAGrD,SAAO,GAAI;IAEb,CAAC,EAAY,CACd;AAuBD,QAAO;EAAE;EAAK;EAAS;EAAS;EAAO,SArBvB,GACb,MAAgC;AAK1B,GAJL,GAAY,EACZ,EAAW,WAAW,GACtB,EAAa,UAAU,MACvB,EAAS,KAAK,EACT,EAAQ,GAAS,EAAW,QAAQ;KAE3C,CAAC,EAAQ,CACV;EAY+C,OAVlC,QAAkB;AAO9B,GANA,GAAY,EACZ,EAAW,WAAW,GACtB,EAAa,UAAU,MACvB,EAAO,KAAK,EACZ,EAAS,KAAK,EACd,EAAW,GAAM,EACjB,EAAW,GAAM;KAChB,EAAE,CAAC;EAEiD"}
|
|
@@ -13,7 +13,7 @@ export interface EntityEventPayload {
|
|
|
13
13
|
entityId?: string;
|
|
14
14
|
entityType?: string;
|
|
15
15
|
}
|
|
16
|
-
export type TechnoSpamEventName = 'technospam.page.viewed' | 'technospam.course.viewed' | 'technospam.course.enroll_clicked' | 'technospam.course.enrolled' | 'technospam.course.progress_updated' | 'technospam.course.certificate_claimed' | 'technospam.lesson.viewed' | 'technospam.lesson.completed' | 'technospam.problem.viewed' | 'technospam.problem.submit_clicked' | 'technospam.problem.submitted' | 'technospam.mentor.viewed' | 'technospam.mentor.book_clicked' | 'technospam.mentor.booked' | 'technospam.mentor.become_mentor_clicked' | 'technospam.mentor.became_mentor' | 'technospam.session.cancel_clicked' | 'technospam.session.cancelled' | 'technospam.session.complete_clicked' | 'technospam.session.completed' | 'technospam.hackathon.viewed' | 'technospam.hackathon.register_team_clicked' | 'technospam.hackathon.team_registered' | 'technospam.hackathon.submit_project_clicked' | 'technospam.hackathon.project_submitted' | 'technospam.search.performed';
|
|
16
|
+
export type TechnoSpamEventName = 'technospam.page.viewed' | 'technospam.course.viewed' | 'technospam.course.enroll_clicked' | 'technospam.course.enrolled' | 'technospam.course.progress_updated' | 'technospam.course.certificate_claimed' | 'technospam.lesson.viewed' | 'technospam.lesson.completed' | 'technospam.problem.viewed' | 'technospam.problem.run_clicked' | 'technospam.problem.submit_clicked' | 'technospam.problem.submitted' | 'technospam.mentor.viewed' | 'technospam.mentor.book_clicked' | 'technospam.mentor.booked' | 'technospam.mentor.become_mentor_clicked' | 'technospam.mentor.became_mentor' | 'technospam.session.cancel_clicked' | 'technospam.session.cancelled' | 'technospam.session.complete_clicked' | 'technospam.session.completed' | 'technospam.hackathon.viewed' | 'technospam.hackathon.register_team_clicked' | 'technospam.hackathon.team_registered' | 'technospam.hackathon.submit_project_clicked' | 'technospam.hackathon.project_submitted' | 'technospam.search.performed';
|
|
17
17
|
export declare function useTechnoSpamAnalytics(): {
|
|
18
18
|
emit: (name: TechnoSpamEventName, payload?: EntityEventPayload & Record<string, unknown>) => void;
|
|
19
19
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTechnoSpamAnalytics.d.ts","sourceRoot":"","sources":["../../../src/technospam/hooks/useTechnoSpamAnalytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,mBAAmB,GAC3B,wBAAwB,GACxB,0BAA0B,GAC1B,kCAAkC,GAClC,4BAA4B,GAC5B,oCAAoC,GACpC,uCAAuC,GACvC,0BAA0B,GAC1B,6BAA6B,GAC7B,2BAA2B,GAC3B,mCAAmC,GACnC,8BAA8B,GAC9B,0BAA0B,GAC1B,gCAAgC,GAChC,0BAA0B,GAC1B,yCAAyC,GACzC,iCAAiC,GACjC,mCAAmC,GACnC,8BAA8B,GAC9B,qCAAqC,GACrC,8BAA8B,GAC9B,6BAA6B,GAC7B,4CAA4C,GAC5C,sCAAsC,GACtC,6CAA6C,GAC7C,wCAAwC,GACxC,6BAA6B,CAAC;AAElC,wBAAgB,sBAAsB;iBAI3B,mBAAmB,YAAW,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAOpF"}
|
|
1
|
+
{"version":3,"file":"useTechnoSpamAnalytics.d.ts","sourceRoot":"","sources":["../../../src/technospam/hooks/useTechnoSpamAnalytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,mBAAmB,GAC3B,wBAAwB,GACxB,0BAA0B,GAC1B,kCAAkC,GAClC,4BAA4B,GAC5B,oCAAoC,GACpC,uCAAuC,GACvC,0BAA0B,GAC1B,6BAA6B,GAC7B,2BAA2B,GAC3B,gCAAgC,GAChC,mCAAmC,GACnC,8BAA8B,GAC9B,0BAA0B,GAC1B,gCAAgC,GAChC,0BAA0B,GAC1B,yCAAyC,GACzC,iCAAiC,GACjC,mCAAmC,GACnC,8BAA8B,GAC9B,qCAAqC,GACrC,8BAA8B,GAC9B,6BAA6B,GAC7B,4CAA4C,GAC5C,sCAAsC,GACtC,6CAA6C,GAC7C,wCAAwC,GACxC,6BAA6B,CAAC;AAElC,wBAAgB,sBAAsB;iBAI3B,mBAAmB,YAAW,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;EAOpF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTechnoSpamAnalytics.js","names":[],"sources":["../../../src/technospam/hooks/useTechnoSpamAnalytics.ts"],"sourcesContent":["/**\n * TechnoSpam product analytics helper.\n *\n * Emits events on the shared event bus. The shell's RybbitSink forwards\n * allow-listed fact events to Rybbit Cloud; until TechnoSpam has a site ID,\n * events still flow to the bus and to backend audit / telemetry sinks.\n *\n * Pattern: emit an intent event before a mutation, then a fact event when the\n * server confirms the change.\n */\n\nimport { useCallback } from 'react';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport interface EntityEventPayload {\n workspaceId?: string;\n entityId?: string;\n entityType?: string;\n}\n\nexport type TechnoSpamEventName =\n | 'technospam.page.viewed'\n | 'technospam.course.viewed'\n | 'technospam.course.enroll_clicked'\n | 'technospam.course.enrolled'\n | 'technospam.course.progress_updated'\n | 'technospam.course.certificate_claimed'\n | 'technospam.lesson.viewed'\n | 'technospam.lesson.completed'\n | 'technospam.problem.viewed'\n | 'technospam.problem.submit_clicked'\n | 'technospam.problem.submitted'\n | 'technospam.mentor.viewed'\n | 'technospam.mentor.book_clicked'\n | 'technospam.mentor.booked'\n | 'technospam.mentor.become_mentor_clicked'\n | 'technospam.mentor.became_mentor'\n | 'technospam.session.cancel_clicked'\n | 'technospam.session.cancelled'\n | 'technospam.session.complete_clicked'\n | 'technospam.session.completed'\n | 'technospam.hackathon.viewed'\n | 'technospam.hackathon.register_team_clicked'\n | 'technospam.hackathon.team_registered'\n | 'technospam.hackathon.submit_project_clicked'\n | 'technospam.hackathon.project_submitted'\n | 'technospam.search.performed';\n\nexport function useTechnoSpamAnalytics() {\n const bus = useEventBus();\n\n const emit = useCallback(\n (name: TechnoSpamEventName, payload: EntityEventPayload & Record<string, unknown> = {}) => {\n bus.emitCustom(name, payload, { component: 'microfe-technospam' });\n },\n [bus]\n );\n\n return { emit };\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"useTechnoSpamAnalytics.js","names":[],"sources":["../../../src/technospam/hooks/useTechnoSpamAnalytics.ts"],"sourcesContent":["/**\n * TechnoSpam product analytics helper.\n *\n * Emits events on the shared event bus. The shell's RybbitSink forwards\n * allow-listed fact events to Rybbit Cloud; until TechnoSpam has a site ID,\n * events still flow to the bus and to backend audit / telemetry sinks.\n *\n * Pattern: emit an intent event before a mutation, then a fact event when the\n * server confirms the change.\n */\n\nimport { useCallback } from 'react';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\n\nexport interface EntityEventPayload {\n workspaceId?: string;\n entityId?: string;\n entityType?: string;\n}\n\nexport type TechnoSpamEventName =\n | 'technospam.page.viewed'\n | 'technospam.course.viewed'\n | 'technospam.course.enroll_clicked'\n | 'technospam.course.enrolled'\n | 'technospam.course.progress_updated'\n | 'technospam.course.certificate_claimed'\n | 'technospam.lesson.viewed'\n | 'technospam.lesson.completed'\n | 'technospam.problem.viewed'\n | 'technospam.problem.run_clicked'\n | 'technospam.problem.submit_clicked'\n | 'technospam.problem.submitted'\n | 'technospam.mentor.viewed'\n | 'technospam.mentor.book_clicked'\n | 'technospam.mentor.booked'\n | 'technospam.mentor.become_mentor_clicked'\n | 'technospam.mentor.became_mentor'\n | 'technospam.session.cancel_clicked'\n | 'technospam.session.cancelled'\n | 'technospam.session.complete_clicked'\n | 'technospam.session.completed'\n | 'technospam.hackathon.viewed'\n | 'technospam.hackathon.register_team_clicked'\n | 'technospam.hackathon.team_registered'\n | 'technospam.hackathon.submit_project_clicked'\n | 'technospam.hackathon.project_submitted'\n | 'technospam.search.performed';\n\nexport function useTechnoSpamAnalytics() {\n const bus = useEventBus();\n\n const emit = useCallback(\n (name: TechnoSpamEventName, payload: EntityEventPayload & Record<string, unknown> = {}) => {\n bus.emitCustom(name, payload, { component: 'microfe-technospam' });\n },\n [bus]\n );\n\n return { emit };\n}\n"],"mappings":";;;AAiDA,SAAgB,IAAyB;CACvC,IAAM,IAAM,GAAa;AASzB,QAAO,EAAE,MAPI,GACV,GAA2B,IAAwD,EAAE,KAAK;AACzF,IAAI,WAAW,GAAM,GAAS,EAAE,WAAW,sBAAsB,CAAC;IAEpE,CAAC,EAAI,CACN,EAEc"}
|