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
| Layer | Responsibility |
|---|---|
| Block Registry | Declarative catalog of block types: schema, editor component, runtime component, validators. |
| Editor Shell | Outline pane, canvas, inspector, AI panel, asset library, preview. |
| Document Model | Yjs CRDT document representing the CourseDraft (modules → lessons → blocks). |
| Persistence | IndexedDB (Dexie) locally; sync-service for server. |
| AI Layer | All AI flows go through AIClient (which delegates to ai-gateway-service or local model). |
| Export Pipeline | Delegated 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 discriminatedkind meta: Y.Mapfor title, locale, settingspresence: Awarenessfor 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.
| Flow | Prompt id (semver-pinned) | Inputs | Output |
|---|---|---|---|
lesson_from_pdf | authoring/lesson_from_pdf@1.0.0 | PDF text + locale | Outline + blocks |
block_from_intent | authoring/block_from_intent@1.0.0 | intent text + lesson context | Single block |
quiz_from_lesson | assessment/quiz_from_lesson@1.0.0 | lesson text + difficulty | QuizBank draft |
branching_from_objective | assessment/branching@1.0.0 | objective + audience | Scenario draft |
simplify_text | authoring/simplify@1.0.0 | text + audience | Rewritten text |
translate | i18n/translate@1.0.0 | text + source/target locale | Translated text |
tts_narrate | media/tts@1.0.0 | text + voice profile | Audio asset |
image_diagram | media/diagram_from_text@1.0.0 | description + style | Image asset |
objectives_metadata | authoring/objectives@1.0.0 | course outline | Learning objectives + SCORM metadata |
auto_caption | media/captions@1.0.0 | audio/video asset | VTT 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.xmlmapping (modules → SCO; lessons → assets; quizzes → SCO with cmi.completion + cmi.score).- Resource map: image/video/audio assets with sha + size.
objectivesfrom AI-suggested + author-edited list.prerequisitesfrom lesson dependency rules.
Validation: SCORM Cloud test runner in CI; 100% of fixture courses pass.
9. HTML Export Pipeline
Self-contained zip:
index.htmlboots the runtime player code in offline mode.manifest.json(PlayPackage manifest).assets/directory.- No external network requests at runtime.
- Optional:
assistant.jsonbaked-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.v1etc. 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.