Skip to main content

Authoring Tool

:::info Source Sourced from docs/10-authoring-tool-spec.md in the documentation repo. :::

Companion: 03 authoring-service · 05 API Design · 11 Player

1. Goals

Match Articulate Rise's speed (block-based, opinionated) and Storyline's depth (interactions, branching, variables) — with AI co-author and offline drafts built in from day one.

2. Architectural Layers

LayerResponsibility
Block RegistryDeclarative catalog of block types: schema, editor component, runtime component, validators.
Editor ShellOutline pane, canvas, inspector, AI panel, asset library, preview.
Document ModelYjs CRDT document representing the CourseDraft (modules → lessons → blocks).
PersistenceIndexedDB (Dexie) locally; sync-service for server.
AI LayerAll AI flows go through AIClient (which delegates to ai-gateway-service or local model).
Export PipelineDelegated to content-service (SCORM, HTML, xAPI, offline bundle).

3. Block Registry

interface BlockSpec<TConfig, TData> {
kind: BlockKind; // 'text' | 'image' | 'video' | 'quiz' | 'branching' | 'interaction' | ...
category: 'media' | 'text' | 'assessment' | 'interaction' | 'embed';
schema: JSONSchema; // validates `data`
defaults: TData;
editor: ReactComponent<EditorProps<TConfig, TData>>;
runtime: ReactComponent<RuntimeProps<TConfig, TData>>;
thumbnail: () => ReactNode;
capabilities: BlockCapability[]; // 'requires_media' | 'has_assessment' | 'branching' | 'ai_supported'
validate: (data: TData, ctx: ValidationContext) => ValidationIssue[];
toScorm?: (data: TData) => ScormFragment; // optional bespoke SCORM mapping
toHtml: (data: TData) => HTMLFragment;
toXapi?: (data: TData, evt: PlayerEvent) => StatementFragment;
aiPrompts?: { generate?: PromptId; rewrite?: PromptId; translate?: PromptId; quizFrom?: PromptId; };
}

Plug-in model: blocks live in workspaces (core + first-party + tenant-private). Tenant-private blocks scoped per tenant; never shared across tenants.

Built-in block kinds (v1)

text, heading, list, callout, divider, image, image-grid, video, audio, embed (whitelisted providers only), code-snippet, quiz, branching-scenario, hotspot, drag-drop-classify, sortable, click-reveal, flashcards, accordion, tabs, timeline, gallery, button, downloadable-attachment.

4. Document Model

CourseDraft is a Yjs Y.Doc per draft:

  • modules: Y.Array<Y.Map> (module records)
  • per module lessons: Y.Array<Y.Map>
  • per lesson blocks: Y.Array<Y.Map> with discriminated kind
  • meta: Y.Map for title, locale, settings
  • presence: Awareness for cursors + selections

Conflict-free for live collab. When offline:

  • Local edits are written to the local Y.Doc and an outbox mirror in IndexedDB (per-mutation clientMutationId).
  • On reconnect, sync-service merges the offline updates via Y.js update vectors. If divergence is irreconcilable for a non-CRDT field (rare), conflict-record is created and surfaced in the UI (LWW + diff).

5. Editor Shell UX

┌──────────────────────────────────────────────────────────────────┐
│ [Topbar: title • status • collaborators • Save • Preview • Publish]│
├──────────┬──────────────────────────────────────────┬────────────┤
│ │ │ │
│ Outline │ Canvas │ Inspector │
│ Modules │ - Block list with drag handles │ - Block │
│ Lessons │ - Inline AI suggestions │ props │
│ │ - Live preview of each block │ - Validation│
│ │ │ │
├──────────┴──────────────────────────────────────────┴────────────┤
│ AI Panel (collapsible) · Assets · Comments · Errors │
└──────────────────────────────────────────────────────────────────┘
  • Drag-drop: keyboard-equivalent drag (Up/Down/Home/End) for accessibility.
  • Inline AI: "Add a quiz here", "Rewrite simpler", "Translate to ar-SA" pop above selection.
  • Preview: authentic player render in a sandboxed iframe.
  • Comments: thread per block (collab review).

6. AI Co-Author Flows

All flows go through ai-gateway-service (or local model when offline) via AIClient. Outputs persist as AIBlock markers (status='draft_ai') until explicitly accepted.

FlowPrompt id (semver-pinned)InputsOutput
lesson_from_pdfauthoring/lesson_from_pdf@1.0.0PDF text + localeOutline + blocks
block_from_intentauthoring/block_from_intent@1.0.0intent text + lesson contextSingle block
quiz_from_lessonassessment/quiz_from_lesson@1.0.0lesson text + difficultyQuizBank draft
branching_from_objectiveassessment/branching@1.0.0objective + audienceScenario draft
simplify_textauthoring/simplify@1.0.0text + audienceRewritten text
translatei18n/translate@1.0.0text + source/target localeTranslated text
tts_narratemedia/tts@1.0.0text + voice profileAudio asset
image_diagrammedia/diagram_from_text@1.0.0description + styleImage asset
objectives_metadataauthoring/objectives@1.0.0course outlineLearning objectives + SCORM metadata
auto_captionmedia/captions@1.0.0audio/video assetVTT track

UI affordances per flow:

  • Streaming output rendered in a side panel.
  • Diff view when rewriting an existing block.
  • "Insert / Replace / Discard" buttons.
  • Provenance badge attached to result.

7. Versioning & Publishing

  • Save: every edit autosaved; Yjs updates persisted incrementally.
  • DraftVersion: monotonic per draft; checkpointed every N changes.
  • Submit for Review: draft locks against further edits unless reviewer rejects.
  • Publish: triggers saga via authoring-service → content-service builds PlayPackage → catalog-service registers CourseVersion → marketplace optional next step.
  • Re-publish: new immutable CourseVersion; old versions remain runtime-valid for existing enrollments.
  • Withdraw: existing bundles revoked via sync; new enrollments blocked.

8. SCORM Export Pipeline

Delegated to content-service via authoring.course_draft.published.v1. Authoring contributes:

  • imsmanifest.xml mapping (modules → SCO; lessons → assets; quizzes → SCO with cmi.completion + cmi.score).
  • Resource map: image/video/audio assets with sha + size.
  • objectives from AI-suggested + author-edited list.
  • prerequisites from lesson dependency rules.

Validation: SCORM Cloud test runner in CI; 100% of fixture courses pass.

9. HTML Export Pipeline

Self-contained zip:

  • index.html boots the runtime player code in offline mode.
  • manifest.json (PlayPackage manifest).
  • assets/ directory.
  • No external network requests at runtime.
  • Optional: assistant.json baked-in AI tutor config (works only with internet).

10. xAPI / cmi5 Export

cmi5 launch URL templates included. Statements emitted by player runtime and shipped to LRS endpoint configured per export.

11. Asset Management Inside Author

  • Asset Library panel (media-service projection).
  • Drag onto canvas to insert.
  • AI-generated assets surfaced with provenance.
  • Per-asset variants previewed (image_md, video_720p).
  • Captions auto-generated; author edits VTT.

12. Offline Authoring

  • Full editor works offline (all blocks renderable).
  • AI flows fall back to local model where supported; cloud refresh available when online.
  • Asset upload queued; preview shows local blob until uploaded.
  • Conflicts surfaced via Sync Center.

13. Validation & Errors Panel

  • Per-block validators run on every edit.
  • Lesson-level: missing required, broken refs, accessibility issues (no alt, no captions).
  • Course-level: minimum lesson count, total duration warning, missing default-locale variants.
  • Errors prevent publish; warnings allow but flag.

14. Collaboration

  • Yjs awareness shows collaborator cursors + selections + names.
  • Comment threads per block; resolved comments collapse.
  • Lock-on-edit not used (CRDT eliminates need); long-running publish locks the whole draft against edits.

15. Accessibility

  • Keyboard reachable; logical reading order; visible focus rings.
  • ARIA roles on canvas + outline (treegrid).
  • Block insertion via Cmd-K command palette.
  • Live regions announce AI events ("Generating quiz", "Quiz inserted").

16. Telemetry

  • authoring.draft.block.added.v1 etc. emitted server-side.
  • Frontend analytics: time-to-first-block, session length, AI panel use rate, conflicts/session.
  • AI accept-rate per prompt → eval feedback loop.

17. Testing

  • Unit (block validators, document migrations).
  • Component tests for every block (editor + runtime).
  • Yjs offline merge tests.
  • E2E author-to-publish (J-4, J-5, J-25).
  • AI prompt regression per flow.
  • Visual regression (LTR + RTL + dark).
  • A11y axe scan in editor + every block.

18. Why

Block-based authoring keeps the editor surface manageable while the registry pattern keeps it extensible. CRDT-first model makes collaboration + offline coherent. Putting AI behind explicit flows (with provenance + accept gate) prevents AI sprawl and keeps authors in control — which is the only acceptable design for high-stakes content like compliance training.