Skip to main content

Evalium Rich Content: MVP → Perfect Plan

A polished, implementation-ready roadmap for rich text, tables, and media across Admin authoring and Candidate delivery.


1) strategy Summary (What We’ll Do)

  • Make Evalium’s Block JSON the canonical content model (with schema_version).
  • Use TipTap as the editor runtime, behind strict mappers (TipTap ⇄ Evalium Block JSON).
  • Render via safe Svelte components (no \{@html} for user content) with a strict CSP.
  • Build a robust paste pipeline (Excel/Docs/Sheets → blocks) with table intelligence and guardrails.
  • Add accessibility gates, search indexes, publish/submission snapshots, and a governed media pipeline.

2) Why These Choices (Fit to Product, Tech, and Market)

  • Product: SMB admins live in spreadsheets and word processors; they must paste and go. Candidates need clean, fast, consistent delivery.
  • Tech: Versioned schemas + JSONB + SSR-safe rendering meets performance and security requirements.
  • Market: Differentiators that matter to SMBs: paste-to-import wizard, content QA panel, branded exports.

3) Key Alternatives & Trade-offs

DecisionOption A (Recommended)Option BTrade-off
Canonical schemaEvalium Block JSONTipTap/ProseMirror JSONA = independence & stability; B = faster day-1 but tighter coupling.
Tables scope (MVP)Constrained (no nested tables, limited spans)Full ProseMirror tables nowA = predictable perf/a11y; B = complexity & risk.
Media deliveryDIY (S3/R2 + CDN)Managed (e.g., Cloudflare Stream)A = lower cost, more control; B = faster to operate.
CollaborationOptimistic locking (Phase 2)CRDT (Y.js) (Phase 3)A = simpler; B = real-time but heavier.

4) Phased Roadmap (Detailed How + Why)

Phase 1 — MVP Foundations (Ship the Essentials, Safely)

Scope

  • Rich text (paragraphs, headings, lists, links, inline code).
  • Basic tables (no nesting; limited rowspan/colspan).
  • Images (upload → asset ID → block).
  • Admin Live Preview identical to candidate rendering.

Implementation

  • Schema: schema_version: 1, blocks + inline spans/marks; allow: paragraph, heading, list, table, image.
  • Renderer: Single SSR-safe component tree → semantic HTML; never \{@html} for user content.
  • Editor: TipTap loaded client-only (onMount + dynamic import()), mappers TipTap ⇄ Evalium with round-trip tests.
  • Paste v1: Intercept paste; detect tables/TSV; normalize (strip styles, resolve spans), cap size (e.g., 30×15), convert to blocks; otherwise fallback to TipTap defaults.
  • Media v1 (images): Pre-signed upload; persist asset ID (not URL); store MIME, width/height, hash.
  • Search readiness: content_text + content_tsvector with trigger to stay in sync.
  • A11y basics: Require alt on images; table caption + header association; heading level continuity.
  • Security: Strict CSP (script-src 'self'; scoped img-src/media-src); server-side validation of block JSON.

Acceptance Criteria

  • No user path renders \{@html}; CSP is active and tested.
  • Paste from Excel/Docs/Sheets produces clean blocks; oversized pastes are capped with user feedback.
  • Live Preview matches candidate view for supported features.
  • Image uploads create asset records; blocks reference asset IDs.
  • Library search is fast (indexed tsvector).

Phase 1.5 — Immediate SMB Wins

1) Paste Spreadsheet → Users/Groups/Items Wizard

  • Modal mapping: columns → Name/Email/Group/etc.; inline validation; downloadable error CSV.

2) Content QA Panel

  • Pre-publish checks for alt/captions, heading gaps, broken links, media weight, table size.

3) Branding Carry-Through

  • Apply tenant branding to preview, candidate player, and exports for cohesion.

Acceptance Criteria

  • Wizard imports typical HR spreadsheets with clear error feedback.
  • QA panel blocks publish on critical issues; warnings are actionable.
  • Branded previews/exports match tenant settings.

Phase 2 — Professionalization & Scale

Tables: A11y + Mobile Completeness

  • Responsive fallback (stacked rows; row-header badges), sticky headers, full keyboard traversal.
  • Allow nested lists inside cells (no nested tables).

Media Pipeline (Video/Audio)

  • Asset registry with quotas & usage dashboards; EXIF strip, MIME/antivirus scan.
  • Transcode worker → HLS/DASH; captions/transcript required.
  • Orphan cleanup; plan-based size/time caps.

Snapshots & Versioning

  • On publish: mint immutable (evaluation_id, version, blocks, asset_versions).
  • On submission: store pointer to content version; optional rendered HTML snapshot.

Exports

  • Unified pipeline: blocks → Sanitized HTML / Markdown / Plaintext / PDF.
  • CSV neutralizes = + - @ prefixes (formula injection).

Performance & Ops

  • Editor bundles lazy-loaded; SSR read-only renders.
  • Observability: paste metrics (source, size, parse time/errors), CSP reports.

Acceptance Criteria

  • Tables are usable and compliant on mobile and with keyboards.
  • Video/audio stream reliably; captions/transcripts enforced.
  • Every submission ties to an immutable content version.
  • Exports are consistent and safe; CSV is injection-safe.

Phase 3 — Collaboration, Localization, Enterprise Unlocks

  • Localization & RTL: Per-locale variants under one content ID; status badges; RTL rendering toggle; font fallbacks.
  • Personalization Tokens: \{\{candidate_name\}\}, \{\{evaluation_title\}\} allowed inside text spans only; server-side escaped substitution.
  • Lifecycle & Approvals: Draft → Review → Published; JSON diff viewer; inline reviewer comments.
  • Optional Real-Time Collaboration: Upgrade from optimistic locking to Y.js (TipTap collab) if needed.
  • Analytics Hooks: Lightweight content telemetry (e.g., video drop-off) wired to item analysis.

Acceptance Criteria

  • Locale variants render correctly (including RTL) with progress indicators.
  • Tokens render safely in delivery and exports.
  • Review flow and diffs reduce accidental regressions.

Phase 4 — “Perfect” (Market Leadership)

  • Content Snippets & Design Tokens: Reusable snippets (instructions, rubric notes) with centralized updates; enforce WCAG contrast via tokens (no ad-hoc inline colors).
  • QTI 3.0 Export Adapter: Deterministic mapping for enterprise tenders.
  • Policy-Driven Publishing: Per-tenant rules (max media size, allowed domains, captions mandatory) validated in preflight.
  • Assisted authoring: Deterministic a11y/clarity suggestions (“add alt text”, “tighten heading structure”); AI optional.

Acceptance Criteria

  • Snippets update globally; contrast always passes WCAG.
  • QTI packages validate in target LMS/LTI contexts.
  • Policy violations block publish with clear remediation steps.

5) Engineering Specs (Copy-Ready)

Data Model (Outline)

  • question_content (id, question_id, schema_version, blocks JSONB, content_text, content_tsvector, created_at, updated_at)
  • assets (id, org_id, type, bytes, width, height, duration, hash, created_at, meta)
  • evaluation_versions (id, evaluation_id, version, blocks JSONB, asset_versions JSONB, created_at, created_by)
  • submissions add: content_version_id

API Contracts (Outline)

  • POST /content/validate → schema & business-rule validation.
  • POST /assets → pre-signed upload; POST /assets/complete → finalize (collect MIME/w×h/duration/hash).
  • POST /evaluations/\{id\}/publish → create version; returns version_id.
  • GET /evaluations/\{id\}/versions/\{version_id\} → immutable snapshot for audits.
  • POST /import/spreadsheet → users/groups/items with mapping and error CSV.

Renderer & Security

  • Single RichRenderer; SSR for read views; strict no \{@html} for user content.
  • CSP: lock script-src 'self'; restrict img-src/media-src to CDN; disallow inline/event handlers.
  • Server-side validation of blocks; deny unknown marks/attrs; strip external media hosts by default.

Paste Pipeline (v1 → v2)

  1. Detect text/html (table) or text/tab-separated-values.
  2. DOMParser → normalize (remove styles, resolve spans, collapse whitespace).
  3. Enforce caps (cells, depth, total text) with friendly truncation message.
  4. Convert → Evalium blocks; log metrics (source, size, duration, errors).

Accessibility Gates (Blocking)

  • Images: non-empty alt.
  • Video/Audio: captions or transcript.
  • Tables: caption, header cells with scope; responsive fallback enabled.
  • Headings: no level jumps.
  • Contrast: enforced via theme tokens (no inline colors).

Performance & Observability

  • Lazy-load editor bundles; SSR only for read.
  • Budgets: Renderer TTI; paste parse < 200 ms for ≤ 30×15 tables (warn beyond).
  • Metrics: paste size/source/time; CSP report-only → enforce.

6) Required Decisions (ADRs to Record)

  1. Canonical Schema: Evalium Block JSON + versioning.
  2. Tables Scope (MVP): No nested tables; limited spans; caps on cells.
  3. Media strategy: DIY S3/R2+CDN vs Managed Streaming; asset IDs in blocks.
  4. Collaboration Approach: Optimistic locking now; revisit Y.js later.

7) Test Matrix & Definition of Done (DoD)

Functional

  • Round-trip TipTap ⇄ Evalium mapping (golden tests).
  • Paste fuzz tests (random tables/marks).
  • QA gate blocks on critical a11y failures.

Security

  • No \{@html} used in user content paths.
  • CSP violations monitored; zero high-severity findings.
  • CSV export neutralizes formula injection.

Performance

  • Renderer SSR TTFB/TTI within budget.
  • Paste within budget for typical SMB content.

Data

  • content_text/tsvector maintained by trigger; indexed queries stay fast.
  • Snapshots referenced by every submission.

8) What You Get

  • A safe, fast MVP that keeps authors productive and candidates focused.
  • A scalable path (a11y, media ops, snapshots, search) without rework.
  • SMB-visible wins: paste-to-import wizard, content QA panel, consistent branding.