@codeverta/interactive
v0.1.0
Published
Interactive learning content (Interactive Video, Course Presentation, Branching Scenario): schema, scoring, player and editor.
Maintainers
Readme
@codeverta/interactive
Interactive learning content for the Codeverta LMS, as a standalone React package. Three content types ship in the box, all stored as versioned JSON:
- Interactive Video: a YouTube video with labels, links and questions at chosen seconds. The video pauses at a question and the student must answer before it continues.
- Course Presentation: slides made of text, images, videos, links and questions, with progress and a score summary.
- Branching Scenario: content, decision and ending nodes. Students walk a path to an ending; the editor shows a flow map and refuses to publish a broken graph.
Question types (shared by video and presentation): multiple choice (single or multiple answers), true/false, short answer, drag the answers to their targets, and put steps in order. Drag questions also work by tapping an item and then its destination, so they are usable on touch screens.
The package is storage-agnostic: it never calls an API. Your app saves the JSON, uploads files, and records attempts.
Install
npm install @codeverta/interactivePeer dependencies: react and react-dom (18 or 19). @xyflow/react (12) is an optional peer, needed only for the branching flow map in the editor.
import "@codeverta/interactive/styles.css";
import "@xyflow/react/dist/style.css"; // only if you use the flow mapStudent side
import { InteractivePlayer } from "@codeverta/interactive/player";
<InteractivePlayer
content={storedJson} // validated before rendering
initialProgress={savedSnapshot} // optional: resume an attempt
onProgress={(snapshot) => save(snapshot)}
onComplete={({ input, result }) => submit(input)} // send `input` to your server
locale="id" // or "en"; override single strings with `labels`
/>;result is a client-side score for instant feedback. Treat it as untrusted: send input (the raw answers or the branching path) to your server and score it there.
Creator HTML is sanitized with DOMPurify every time it is rendered.
Creator side
import { InteractiveEditor } from "@codeverta/interactive/editor";
<InteractiveEditor
value={doc} // null starts a new document (shows the type chooser)
onChange={setDoc}
uploadAsset={async (file) => urlOfUploadedFile} // optional: enables upload buttons
renderRichText={({ value, onChange, label }) => <YourRichEditor … />} // optional
/>;The editor shows a live "ready to publish" panel built on validateForPublish.
Scoring and validation (core)
import { score, validateForPublish, parseContent } from "@codeverta/interactive/core";
validateForPublish(doc); // { valid, issues: [{ code, message, ref }] }
score(doc, { answers: { q1: ["a"] } }); // video / presentation
score(doc, { path: [{ nodeId: "n1" }, { nodeId: "d1", choiceId: "c1" }, { nodeId: "e1" }] }); // branchingScoring rules: multiple choice with several correct answers is all-or-nothing; drag and sequence questions give partial credit proportional to correct items; short answers ignore case and surrounding spaces (exact or contains); earned points round to two decimals. A branching score is the score of the ending reached; an ending without a score counts as a pass. Content with no questions passes unscored.
Keep a server in sync
The scoring and validation rules are pinned by conformance fixtures in fixtures/*.json (in the source repository, not the npm tarball). Each case is { name, kind, content, input, expected }. Run the same files against any server implementation (the LMS backend does this in Go) so both sides agree.
Add a new content type
import { registerType } from "@codeverta/interactive/core";
import { registerPlayer } from "@codeverta/interactive/player";
import { registerEditor } from "@codeverta/interactive/editor";
registerType({ type: "my-type", schema, create, validate, score });
registerPlayer("my-type", MyPlayer);
registerEditor("my-type", MyEditor);Nothing in the core changes. Stored documents carry schemaVersion (currently 1).
Theming
Override CSS variables on any parent: --ci-primary, --ci-bg, --ci-text, --ci-border, --ci-radius, --ci-font, and the correct / partial / wrong colours. No Tailwind is required.
Develop
npm run test # vitest: shared fixtures + player and editor behaviour
npm run typecheck
npm run build # tsup + styles.css -> dist/
npm run dev # playground with the editor and the player side by sideLicense: MIT.
