Skip to content
Back to top
View this page

# HTML Rendering

Classes for rendering Viper data as HTML in web and desktop applications.

When to use: Use `DocumentNode` to traverse nested Viper documents with full metadata. Use `Html` to generate styled HTML output for Flask web apps or Qt desktop apps (via `QTextEdit.setHtml()`).

## Quick Start

    from flask import Flask, render_template
    from dsviper import CommitDatabase, DocumentNode, Html, ValueKey

    app = Flask(__name__)

    @app.get("/document/<uuid:instance_id>")
    def document_view(instance_id):
        db = CommitDatabase.open("model.cdb")
        db.definitions().inject()

        # Create key and get document accessor
        key = ValueKey.create(MYAPP_C_Entity, str(instance_id))
        attachment_getting = db.state(db.last_commit_id()).attachment_getting()

        # Build document tree for all attachments
        nodes = DocumentNode.create_documents(key, attachment_getting)

        # Render as collapsible HTML details
        content = Html.documents_details(nodes, show_type=True)
        return render_template("document.html", content=content)

## DocumentNode Tree

`DocumentNode` provides a recursive tree structure for navigating Viper documents:

    from dsviper import DocumentNode

    # Create tree from a key
    nodes = DocumentNode.create_documents(key, attachment_getting)

    for node in nodes:
        print(f"{node.string_component()}: {node.string_value()}")

        # Type introspection for conditional rendering
        if node.is_editable():
            if node.is_boolean():
                render_checkbox(node)
            elif node.is_string():
                render_text_input(node)
            elif node.is_enumeration():
                render_select(node)

        # Recursive traversal
        if node.is_expandable():
            for child in node.children():
                render_node(child)

## Custom HTML Rendering

Build custom renderers using `DocumentNode` metadata:

    from dsviper import DocumentNode, ValueKey, ValueEnumeration

    def render_node(node: DocumentNode, level: int = 0) -> str:
        indent = "  " * level
        html = ""

        if node.is_expandable():
            # Collapsible container
            html += f'{indent}<details open>'
            html += f'<summary>{node.string_component()}</summary>'
            for child in node.children():
                html += render_node(child, level + 1)
            html += f'{indent}</details>'
        else:
            # Leaf value
            html += f'{indent}<div>{node.string_component()} = '

            if node.is_editable():
                # Editable field with form input
                html += f'<input name="{node.uuid().encoded()}" '
                html += f'value="{node.string_value()}"/>'
            else:
                html += node.string_value()

            html += '</div>'

        return html

Node metadata available:

Method | Description
--- | ---
`path()` | Path to this node (for `update()` mutations)
`key()` | Document key (for identifying the document)
`attachment()` | Attachment containing this document
`uuid()` | Unique ID for this node (useful for HTML element IDs)
`is_editable()` | Whether this field can be modified
`is_expandable()` | Whether this node has children
`children()` | Child nodes for containers/structures

## Html Helpers

The `Html` class provides ready-to-use rendering:

    from dsviper import Html, DocumentNode

    # Render documents as collapsible details
    nodes = DocumentNode.create_documents(key, attachment_getting)
    content = Html.documents_details(nodes, show_type=True)

    # Render a single value with syntax highlighting
    html = Html.value(my_structure)

    # Pretty-print with indentation
    html = Html.value_pretty(my_structure, show_type=True)

    # Render DSM schema as HTML
    html = Html.dsm_definitions(dsm_defs, show_documentation=True)

    # Build complete HTML document
    page = Html.document(
        title="My Document",
        style=Html.style(),
        body=Html.body(content)
    )

## Qt Desktop Integration

The same `Html` helpers work in Qt/PySide6 applications via `QTextEdit`:

    from PySide6.QtWidgets import QTextEdit
    from dsviper import Html, CommitDatabase

    # In a Qt dialog or widget
    class InspectDialog:
        def __init__(self):
            self.text_edit = QTextEdit()

        def show_definitions(self, db: CommitDatabase):
            # Get DSM definitions with HTML formatting
            dsm_defs = db.definitions().to_dsm_definitions()
            content = Html.dsm_definitions(dsm_defs, show_documentation=True)

            # Build complete HTML document
            style = Html.style()
            body = Html.body(content)
            document = Html.document("DSM Definitions", style, body)

            # Display in QTextEdit
            self.text_edit.setHtml(document)

        def show_value(self, value):
            content = Html.value(value, use_description=True)
            document = Html.document("Value", Html.style(), Html.body(content))
            self.text_edit.setHtml(document)

This pattern is used by `cdbe.py` (CDB Editor) and `dbe.py` (Database Editor) to display definitions and values with syntax highlighting.

## Choosing the Right Approach

Use Case | API | Note
--- | --- | ---
Quick document display | `Html.documents_details()` | Ready-to-use collapsible view
Custom form builder | `DocumentNode` traversal | Full control over rendering
Value debugging | `Html.value_pretty()` | Syntax-highlighted output
Schema documentation | `Html.dsm_definitions()` | Display DSM structure
Qt desktop display | `Html.document()` + `setHtml()` | Works with QTextEdit

## DocumentNode

`dsviper.DocumentNode` | A class used to represent a node in the tree representation of a value.

## Html

`dsviper.Html` | A class used to generate HTML representation.