kolibri icon indicating copy to clipboard operation
kolibri copied to clipboard

Enable SafeHTMLViewer to leverage ContentViewer plugins

Open rtibbles opened this issue 3 months ago • 0 comments

This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.

Current behavior

Currently, the Safe HTML viewer can only display static HTML content. When HTML content contains embedded media elements (<video>, <audio>, <object> tags, etc.), these are rendered as basic HTML elements without the rich functionality provided by Kolibri's specialized content viewer plugins (video player controls, progress tracking, subtitles, transcripts, PDF viewer features, etc.).

Desired behavior

  1. Dynamic viewer injection: Allow Safe HTML content to automatically use appropriate Kolibri content viewers (media player, PDF viewer, etc.) when encountering compatible embedded elements
  2. Full feature parity: Embedded media should have all the capabilities of their standalone viewers (progress tracking, interactive controls, captions, etc.) - SafeHTMLViewer should be able to aggregate progress across embedded viewers
  3. Clean separation of concerns: Viewer components should work identically whether instantiated from a contentNode props or a DOM element, without needing to know the difference
  4. Extensibility: New viewer types should automatically work in Safe HTML contexts with only element handling code required

High-Level Technical Implementation

  1. Backend: Viewer Capability Declaration
  • Extend ContentRendererHook with css_selectors tuple and allow_object_tag flag
  • Each viewer plugin declares which DOM elements/MIME types it can handle
  • Auto-generate object[type="mimetype"] selectors from preset formats
  • Serialize capability data to frontend via hook registration
  1. Frontend: Viewer Registry & Element Matching
  • Build runtime registry mapping CSS selectors → viewer components (kolibri.canHandleElement(), kolibri.elementViewerComponent())
  • Use DOM .matches(selector) to determine which viewer can handle an element
  • Provide unified API for preset-based and element-based component resolution
  1. Architecture: Centralize Props Handling in ContentViewer
  • Key architectural change: Move all prop handling (contentNode, element, files, etc.) to the ContentViewer wrapper component
  • Viewer components (MediaPlayerIndex, PdfViewerIndex, etc.) receive only processed data via useContentViewer composable
  • Rationale: Viewer components should be agnostic to data source - they work with files/options/progress state, not raw props
  • This enables clean composition: SafeHTML → ContentViewer → (appropriate viewer) with no viewer code changes
  1. DOM-Based File Extraction
  • Implement element-specific extractors (e.g., extractMediaFiles() for video/audio)
  • Extract file metadata from DOM attributes (src, data, poster, children, elements)
  • Generate compatible file objects with proper presets, priorities, and supplementary flags
  1. SafeHTML Integration
  • Update DOMPurify config: allow tags and data attributes, but ensure that the data attribute is properly sanitized
  • During HTML rendering, check each element with kolibri.canHandleElement()
  • Replace matched elements with <ContentViewer element={node}> component
  • ContentViewer handles file extraction and routes to appropriate viewer

rtibbles avatar Oct 12 '25 04:10 rtibbles