Outline Module

The outline module is the first self-contained plugin for the Knowledge Platform. It implements a hierarchical document outline backed by a typed graph.

Graph Type: outline

graph TD
    ROOT["OutlineItem (root)"]
    CH1["OutlineItem (child)"]
    CH2["OutlineItem (child)"]
    GCH1["OutlineItem (grandchild)"]

    ROOT -->|ParentOf| CH1
    ROOT -->|ParentOf| CH2
    CH1 -->|ParentOf| GCH1

Node Type: OutlineItem

Attribute Type Required Default Description
title str Yes Heading text for this item
content str No "" Optional body text
position int No 0 Sibling sort order (ascending)

Edge Type: ParentOf

Property Value
Source types OutlineItem only
Target types OutlineItem only
Attributes None
Description Directed parent-to-child relationship

OutlineService

OutlineService is the high-level API for working with outline graphs. It wraps IGraphService and adds outline-specific operations like adding items with parent relationships, subtree removal, and tree projection.

Creating an Outline

from knowledge_platform.modules.outline.service import OutlineService

svc = OutlineService(graph_service)
graph = svc.create_outline(workspace_id, "My Book")
# Graph now contains one root OutlineItem with title="My Book"

Building a Hierarchy

# Add a chapter under the root
root_node = list(graph.nodes("OutlineItem"))[0]
chapter = svc.add_item(graph.id, parent_id=root_node.id, title="Chapter 1", position=0)

# Add a section under the chapter
section = svc.add_item(graph.id, parent_id=chapter.id, title="1.1 Introduction", position=0)

# Add a root-level item (no parent)
appendix = svc.add_item(graph.id, parent_id=None, title="Appendix", position=1)

Updating an Item

Only non-None arguments are merged:

updated = svc.update_item(chapter.id, title="Chapter 1: Getting Started")
# content and position are unchanged

Removing an Item and Its Subtree

svc.remove_item(graph.id, chapter.id)
# Removes chapter and all its descendants (sections, subsections, etc.)

Moving an Item

svc.move_item(
    graph.id,
    node_id=section.id,
    new_parent_id=appendix.id,
    new_position=0,
)
# Removes existing ParentOf edge, creates new one under appendix

Getting the Tree Projection

roots = svc.get_tree(graph.id)
for root in roots:
    print(root.title)
    for child in root.children:
        print(f"  {child.title}")

Tree Projection: OutlineTreeProjection

OutlineTreeProjection converts a flat graph into an ordered tree structure. It is used internally by OutlineService.get_tree() and by the UI view model.

How it Works

  1. Collect all OutlineItem nodes.
  2. Build a parent_id → [child_node, ...] mapping from ParentOf edges.
  3. Sort each sibling list by the position attribute.
  4. Identify roots: nodes with no incoming ParentOf edges.
  5. Recursively build OutlineTreeNode trees from roots.

OutlineTreeNode

OutlineTreeNode is the view object returned by the projection. It provides convenient property accessors:

tree_node.title     # str
tree_node.content   # str
tree_node.position  # int
tree_node.depth     # int (0 = root)
tree_node.node_id   # NodeId
tree_node.children  # list[OutlineTreeNode]

Flat Iteration

projection = OutlineTreeProjection()
roots = projection.project(graph)
flat = projection.flat_list(roots)  # depth-first order
for item in flat:
    indent = "  " * item.depth
    print(f"{indent}{item.title}")

Module Entry Point: OutlineModule

from knowledge_platform.modules.outline.module import OutlineModule

module = OutlineModule()
print(module.module_id)     # "outline"
print(module.display_name)  # "Outline"
print(module.primary_graph_type)  # "outline"
# module.graph_types contains an OutlineGraphType instance

OutlineModule.create_widget() lazily imports OutlineView to avoid loading PySide6 in headless environments. The module also implements create_document() and list_documents() so the host can create and open outlines without importing OutlineService directly.

API Reference

knowledge_platform.modules.outline.graph_type.OutlineGraphType

Bases: GraphType

Semantic type for outline graphs.

An outline is a rooted tree of :class:OutlineItem nodes connected by :class:ParentOf edges (parent → child). There is exactly one root node (an :class:OutlineItem with no incoming ParentOf edges).

Class attributes

type_name: "outline" node_schemas: {"OutlineItem": OUTLINE_ITEM_SCHEMA} edge_schemas: {"ParentOf": PARENT_OF_SCHEMA}

Source code in src/knowledge_platform/modules/outline/graph_type.py
class OutlineGraphType(GraphType):
    """Semantic type for outline graphs.

    An outline is a rooted tree of :class:`OutlineItem` nodes connected by
    :class:`ParentOf` edges (parent → child).  There is exactly one root node
    (an :class:`OutlineItem` with no incoming ``ParentOf`` edges).

    Class attributes:
        type_name: ``"outline"``
        node_schemas: ``{"OutlineItem": OUTLINE_ITEM_SCHEMA}``
        edge_schemas: ``{"ParentOf": PARENT_OF_SCHEMA}``
    """

    type_name = "outline"
    node_schemas = {"OutlineItem": OUTLINE_ITEM_SCHEMA}
    edge_schemas = {"ParentOf": PARENT_OF_SCHEMA}

knowledge_platform.modules.outline.service.OutlineService

High-level operations for creating and managing outline graphs.

This service wraps :class:~knowledge_platform.services.interfaces.IGraphService and adds outline-specific helpers (e.g. adding child items, re-ordering).

Parameters:

Name Type Description Default
graph_service IGraphService

Underlying application graph service.

required
Source code in src/knowledge_platform/modules/outline/service.py
class OutlineService:
    """High-level operations for creating and managing outline graphs.

    This service wraps :class:`~knowledge_platform.services.interfaces.IGraphService`
    and adds outline-specific helpers (e.g. adding child items, re-ordering).

    Args:
        graph_service: Underlying application graph service.
    """

    GRAPH_TYPE = "outline"
    NODE_TYPE = "OutlineItem"
    EDGE_TYPE = "ParentOf"

    def __init__(self, graph_service: IGraphService) -> None:
        self._gs = graph_service
        self._projection = OutlineTreeProjection()

    # ------------------------------------------------------------------
    # Graph lifecycle
    # ------------------------------------------------------------------

    def create_outline(self, workspace_id: WorkspaceId, name: str) -> Graph:
        """Create a new outline graph with a default root item.

        Args:
            workspace_id: Owning workspace.
            name: Display name for the outline.

        Returns:
            Newly created outline :class:`Graph` (with one root node).
        """
        graph = self._gs.create_graph(workspace_id, self.GRAPH_TYPE, name)
        self._gs.add_node(graph.id, self.NODE_TYPE, {"title": name, "content": "", "position": 0})
        logger.info("outline.created", graph_id=graph.id, name=name)
        return self._gs.get_graph(graph.id)

    def get_outline(self, graph_id: GraphId) -> Graph:
        """Return an outline graph.

        Args:
            graph_id: Target graph identifier.

        Returns:
            The :class:`Graph`.
        """
        return self._gs.get_graph(graph_id)

    def list_outlines(self, workspace_id: WorkspaceId) -> list[Graph]:
        """Return all outline graphs in a workspace.

        Args:
            workspace_id: Owning workspace.

        Returns:
            List of outline :class:`Graph` instances.
        """
        return [g for g in self._gs.list_graphs(workspace_id) if g.type_name == self.GRAPH_TYPE]

    # ------------------------------------------------------------------
    # Item operations
    # ------------------------------------------------------------------

    def add_item(
        self,
        graph_id: GraphId,
        parent_id: NodeId | None,
        title: str,
        content: str = "",
        position: int = 0,
    ) -> Node:
        """Add a new :class:`OutlineItem` node, optionally as a child of *parent_id*.

        Args:
            graph_id: Owning outline graph.
            parent_id: If provided, creates a ``ParentOf`` edge from *parent_id*
                to the new node.  Pass ``None`` to create a root-level item.
            title: Node title.
            content: Optional body text.
            position: Sibling sort order.

        Returns:
            The created :class:`~knowledge_platform.core.node.Node`.
        """
        node = self._gs.add_node(
            graph_id,
            self.NODE_TYPE,
            {"title": title, "content": content, "position": position},
        )
        if parent_id is not None:
            self._gs.add_edge(graph_id, parent_id, node.id, self.EDGE_TYPE, {})
        logger.info("outline.item.added", node_id=node.id, parent_id=parent_id)
        return node

    def update_item(
        self,
        node_id: NodeId,
        title: str | None = None,
        content: str | None = None,
        position: int | None = None,
    ) -> Node:
        """Update fields on an :class:`OutlineItem`.

        Only non-``None`` arguments are merged into the node's attributes.

        Args:
            node_id: Target node.
            title: New title, or ``None`` to leave unchanged.
            content: New content, or ``None`` to leave unchanged.
            position: New position, or ``None`` to leave unchanged.

        Returns:
            Updated :class:`~knowledge_platform.core.node.Node`.
        """
        updates: dict[str, object] = {}
        if title is not None:
            updates["title"] = title
        if content is not None:
            updates["content"] = content
        if position is not None:
            updates["position"] = position
        node = self._gs.update_node(node_id, updates)
        logger.info("outline.item.updated", node_id=node_id)
        return node

    def remove_item(self, graph_id: GraphId, node_id: NodeId) -> None:
        """Remove an :class:`OutlineItem` and all its descendant items.

        Performs a depth-first traversal to collect all descendants before
        removing them bottom-up, avoiding dangling edge references.

        Args:
            graph_id: Owning outline graph.
            node_id: Root of the sub-tree to remove.
        """
        graph = self._gs.get_graph(graph_id)
        to_remove = self._collect_subtree(graph, node_id)
        for nid in reversed(to_remove):
            self._gs.remove_node(graph_id, nid)
        logger.info("outline.item.removed", node_id=node_id, total=len(to_remove))

    def move_item(
        self,
        graph_id: GraphId,
        node_id: NodeId,
        new_parent_id: NodeId | None,
        new_position: int,
    ) -> None:
        """Move an item to a new parent and/or position.

        Removes any existing ``ParentOf`` edge that points to *node_id* and
        creates a new one if *new_parent_id* is provided.

        Args:
            graph_id: Owning outline graph.
            node_id: Node to move.
            new_parent_id: New parent node ID, or ``None`` for root level.
            new_position: New sibling position.
        """
        graph = self._gs.get_graph(graph_id)

        # Remove existing ParentOf edge pointing to this node.
        for edge in list(graph.incoming_edges(node_id, type_name=self.EDGE_TYPE)):
            self._gs.remove_edge(graph_id, edge.id)

        # Attach to new parent.
        if new_parent_id is not None:
            self._gs.add_edge(graph_id, new_parent_id, node_id, self.EDGE_TYPE, {})

        # Update position.
        self._gs.update_node(node_id, {"position": new_position})
        logger.info("outline.item.moved", node_id=node_id, new_parent_id=new_parent_id)

    # ------------------------------------------------------------------
    # Projection
    # ------------------------------------------------------------------

    def get_tree(self, graph_id: GraphId) -> list[OutlineTreeNode]:
        """Return the tree projection for an outline graph.

        Args:
            graph_id: Target outline graph.

        Returns:
            Ordered list of root :class:`OutlineTreeNode` instances.
        """
        graph = self._gs.get_graph(graph_id)
        return self._projection.project(graph)

    # ------------------------------------------------------------------
    # Helpers
    # ------------------------------------------------------------------

    def _collect_subtree(self, graph: Graph, root_id: NodeId) -> list[NodeId]:
        """Return *root_id* plus all descendant node IDs (depth-first).

        Args:
            graph: The graph to traverse.
            root_id: Starting node.

        Returns:
            Ordered list of node IDs (root first, then descendants).
        """
        result: list[NodeId] = []

        def walk(nid: NodeId) -> None:
            result.append(nid)
            for edge in graph.outgoing_edges(nid, type_name=self.EDGE_TYPE):
                walk(edge.target_id)

        walk(root_id)
        return result

Functions

add_item
add_item(
    graph_id: GraphId,
    parent_id: NodeId | None,
    title: str,
    content: str = "",
    position: int = 0,
) -> Node

Add a new :class:OutlineItem node, optionally as a child of parent_id.

Parameters:

Name Type Description Default
graph_id GraphId

Owning outline graph.

required
parent_id NodeId | None

If provided, creates a ParentOf edge from parent_id to the new node. Pass None to create a root-level item.

required
title str

Node title.

required
content str

Optional body text.

''
position int

Sibling sort order.

0

Returns:

Type Description
Node

The created :class:~knowledge_platform.core.node.Node.

Source code in src/knowledge_platform/modules/outline/service.py
def add_item(
    self,
    graph_id: GraphId,
    parent_id: NodeId | None,
    title: str,
    content: str = "",
    position: int = 0,
) -> Node:
    """Add a new :class:`OutlineItem` node, optionally as a child of *parent_id*.

    Args:
        graph_id: Owning outline graph.
        parent_id: If provided, creates a ``ParentOf`` edge from *parent_id*
            to the new node.  Pass ``None`` to create a root-level item.
        title: Node title.
        content: Optional body text.
        position: Sibling sort order.

    Returns:
        The created :class:`~knowledge_platform.core.node.Node`.
    """
    node = self._gs.add_node(
        graph_id,
        self.NODE_TYPE,
        {"title": title, "content": content, "position": position},
    )
    if parent_id is not None:
        self._gs.add_edge(graph_id, parent_id, node.id, self.EDGE_TYPE, {})
    logger.info("outline.item.added", node_id=node.id, parent_id=parent_id)
    return node
create_outline
create_outline(
    workspace_id: WorkspaceId, name: str
) -> Graph

Create a new outline graph with a default root item.

Parameters:

Name Type Description Default
workspace_id WorkspaceId

Owning workspace.

required
name str

Display name for the outline.

required

Returns:

Type Description
Graph

Newly created outline :class:Graph (with one root node).

Source code in src/knowledge_platform/modules/outline/service.py
def create_outline(self, workspace_id: WorkspaceId, name: str) -> Graph:
    """Create a new outline graph with a default root item.

    Args:
        workspace_id: Owning workspace.
        name: Display name for the outline.

    Returns:
        Newly created outline :class:`Graph` (with one root node).
    """
    graph = self._gs.create_graph(workspace_id, self.GRAPH_TYPE, name)
    self._gs.add_node(graph.id, self.NODE_TYPE, {"title": name, "content": "", "position": 0})
    logger.info("outline.created", graph_id=graph.id, name=name)
    return self._gs.get_graph(graph.id)
get_outline
get_outline(graph_id: GraphId) -> Graph

Return an outline graph.

Parameters:

Name Type Description Default
graph_id GraphId

Target graph identifier.

required

Returns:

Name Type Description
The Graph

class:Graph.

Source code in src/knowledge_platform/modules/outline/service.py
def get_outline(self, graph_id: GraphId) -> Graph:
    """Return an outline graph.

    Args:
        graph_id: Target graph identifier.

    Returns:
        The :class:`Graph`.
    """
    return self._gs.get_graph(graph_id)
get_tree
get_tree(graph_id: GraphId) -> list[OutlineTreeNode]

Return the tree projection for an outline graph.

Parameters:

Name Type Description Default
graph_id GraphId

Target outline graph.

required

Returns:

Type Description
list[OutlineTreeNode]

Ordered list of root :class:OutlineTreeNode instances.

Source code in src/knowledge_platform/modules/outline/service.py
def get_tree(self, graph_id: GraphId) -> list[OutlineTreeNode]:
    """Return the tree projection for an outline graph.

    Args:
        graph_id: Target outline graph.

    Returns:
        Ordered list of root :class:`OutlineTreeNode` instances.
    """
    graph = self._gs.get_graph(graph_id)
    return self._projection.project(graph)
list_outlines
list_outlines(workspace_id: WorkspaceId) -> list[Graph]

Return all outline graphs in a workspace.

Parameters:

Name Type Description Default
workspace_id WorkspaceId

Owning workspace.

required

Returns:

Type Description
list[Graph]

List of outline :class:Graph instances.

Source code in src/knowledge_platform/modules/outline/service.py
def list_outlines(self, workspace_id: WorkspaceId) -> list[Graph]:
    """Return all outline graphs in a workspace.

    Args:
        workspace_id: Owning workspace.

    Returns:
        List of outline :class:`Graph` instances.
    """
    return [g for g in self._gs.list_graphs(workspace_id) if g.type_name == self.GRAPH_TYPE]
move_item
move_item(
    graph_id: GraphId,
    node_id: NodeId,
    new_parent_id: NodeId | None,
    new_position: int,
) -> None

Move an item to a new parent and/or position.

Removes any existing ParentOf edge that points to node_id and creates a new one if new_parent_id is provided.

Parameters:

Name Type Description Default
graph_id GraphId

Owning outline graph.

required
node_id NodeId

Node to move.

required
new_parent_id NodeId | None

New parent node ID, or None for root level.

required
new_position int

New sibling position.

required
Source code in src/knowledge_platform/modules/outline/service.py
def move_item(
    self,
    graph_id: GraphId,
    node_id: NodeId,
    new_parent_id: NodeId | None,
    new_position: int,
) -> None:
    """Move an item to a new parent and/or position.

    Removes any existing ``ParentOf`` edge that points to *node_id* and
    creates a new one if *new_parent_id* is provided.

    Args:
        graph_id: Owning outline graph.
        node_id: Node to move.
        new_parent_id: New parent node ID, or ``None`` for root level.
        new_position: New sibling position.
    """
    graph = self._gs.get_graph(graph_id)

    # Remove existing ParentOf edge pointing to this node.
    for edge in list(graph.incoming_edges(node_id, type_name=self.EDGE_TYPE)):
        self._gs.remove_edge(graph_id, edge.id)

    # Attach to new parent.
    if new_parent_id is not None:
        self._gs.add_edge(graph_id, new_parent_id, node_id, self.EDGE_TYPE, {})

    # Update position.
    self._gs.update_node(node_id, {"position": new_position})
    logger.info("outline.item.moved", node_id=node_id, new_parent_id=new_parent_id)
remove_item
remove_item(graph_id: GraphId, node_id: NodeId) -> None

Remove an :class:OutlineItem and all its descendant items.

Performs a depth-first traversal to collect all descendants before removing them bottom-up, avoiding dangling edge references.

Parameters:

Name Type Description Default
graph_id GraphId

Owning outline graph.

required
node_id NodeId

Root of the sub-tree to remove.

required
Source code in src/knowledge_platform/modules/outline/service.py
def remove_item(self, graph_id: GraphId, node_id: NodeId) -> None:
    """Remove an :class:`OutlineItem` and all its descendant items.

    Performs a depth-first traversal to collect all descendants before
    removing them bottom-up, avoiding dangling edge references.

    Args:
        graph_id: Owning outline graph.
        node_id: Root of the sub-tree to remove.
    """
    graph = self._gs.get_graph(graph_id)
    to_remove = self._collect_subtree(graph, node_id)
    for nid in reversed(to_remove):
        self._gs.remove_node(graph_id, nid)
    logger.info("outline.item.removed", node_id=node_id, total=len(to_remove))
update_item
update_item(
    node_id: NodeId,
    title: str | None = None,
    content: str | None = None,
    position: int | None = None,
) -> Node

Update fields on an :class:OutlineItem.

Only non-None arguments are merged into the node's attributes.

Parameters:

Name Type Description Default
node_id NodeId

Target node.

required
title str | None

New title, or None to leave unchanged.

None
content str | None

New content, or None to leave unchanged.

None
position int | None

New position, or None to leave unchanged.

None

Returns:

Name Type Description
Updated Node

class:~knowledge_platform.core.node.Node.

Source code in src/knowledge_platform/modules/outline/service.py
def update_item(
    self,
    node_id: NodeId,
    title: str | None = None,
    content: str | None = None,
    position: int | None = None,
) -> Node:
    """Update fields on an :class:`OutlineItem`.

    Only non-``None`` arguments are merged into the node's attributes.

    Args:
        node_id: Target node.
        title: New title, or ``None`` to leave unchanged.
        content: New content, or ``None`` to leave unchanged.
        position: New position, or ``None`` to leave unchanged.

    Returns:
        Updated :class:`~knowledge_platform.core.node.Node`.
    """
    updates: dict[str, object] = {}
    if title is not None:
        updates["title"] = title
    if content is not None:
        updates["content"] = content
    if position is not None:
        updates["position"] = position
    node = self._gs.update_node(node_id, updates)
    logger.info("outline.item.updated", node_id=node_id)
    return node

knowledge_platform.modules.outline.projection.OutlineTreeProjection

Projects an outline :class:Graph into an ordered tree structure.

The projection traverses ParentOf edges (parent → child) from each node to build a tree. Nodes with no incoming ParentOf edges are considered roots (a well-formed outline has exactly one root).

Example::

projection = OutlineTreeProjection()
roots = projection.project(graph)
for root in roots:
    print(root.title)
    for child in root.children:
        print("  ", child.title)
Source code in src/knowledge_platform/modules/outline/projection.py
class OutlineTreeProjection:
    """Projects an outline :class:`Graph` into an ordered tree structure.

    The projection traverses ``ParentOf`` edges (parent → child) from each
    node to build a tree.  Nodes with no incoming ``ParentOf`` edges are
    considered roots (a well-formed outline has exactly one root).

    Example::

        projection = OutlineTreeProjection()
        roots = projection.project(graph)
        for root in roots:
            print(root.title)
            for child in root.children:
                print("  ", child.title)
    """

    def project(self, graph: Graph) -> list[OutlineTreeNode]:
        """Build a forest of :class:`OutlineTreeNode` trees from *graph*.

        Args:
            graph: Outline graph to project.

        Returns:
            Ordered list of root :class:`OutlineTreeNode` instances (normally
            a single-element list for a well-formed outline).
        """
        # Identify all OutlineItem nodes.
        all_nodes = {n.id: n for n in graph.nodes(type_name="OutlineItem")}

        # Build parent→[child] mapping from ParentOf edges.
        children_map: dict[NodeId, list[Node]] = {nid: [] for nid in all_nodes}
        has_parent: set[NodeId] = set()

        for edge in graph.edges(type_name="ParentOf"):
            parent_id = edge.source_id
            child_id = edge.target_id
            if parent_id in all_nodes and child_id in all_nodes:
                children_map[parent_id].append(all_nodes[child_id])
                has_parent.add(child_id)

        # Sort children by position attribute.
        for nid in children_map:
            children_map[nid].sort(
                key=lambda n: int(n.attributes.get("position", 0))  # type: ignore[arg-type]
            )

        # Roots are nodes with no parent.
        root_nodes = [n for nid, n in all_nodes.items() if nid not in has_parent]
        root_nodes.sort(key=lambda n: int(n.attributes.get("position", 0)))  # type: ignore[arg-type]

        def build(node: Node, depth: int) -> OutlineTreeNode:
            tree_node = OutlineTreeNode(node=node, depth=depth)
            for child in children_map.get(node.id, []):
                tree_node.children.append(build(child, depth + 1))
            return tree_node

        return [build(r, 0) for r in root_nodes]

    def flat_list(self, roots: list[OutlineTreeNode]) -> list[OutlineTreeNode]:
        """Return a depth-first flat iteration of the tree.

        Args:
            roots: Output of :meth:`project`.

        Returns:
            Flat ordered list of all :class:`OutlineTreeNode` instances.
        """
        result: list[OutlineTreeNode] = []

        def walk(node: OutlineTreeNode) -> None:
            result.append(node)
            for child in node.children:
                walk(child)

        for root in roots:
            walk(root)
        return result

Functions

flat_list
flat_list(
    roots: list[OutlineTreeNode],
) -> list[OutlineTreeNode]

Return a depth-first flat iteration of the tree.

Parameters:

Name Type Description Default
roots list[OutlineTreeNode]

Output of :meth:project.

required

Returns:

Type Description
list[OutlineTreeNode]

Flat ordered list of all :class:OutlineTreeNode instances.

Source code in src/knowledge_platform/modules/outline/projection.py
def flat_list(self, roots: list[OutlineTreeNode]) -> list[OutlineTreeNode]:
    """Return a depth-first flat iteration of the tree.

    Args:
        roots: Output of :meth:`project`.

    Returns:
        Flat ordered list of all :class:`OutlineTreeNode` instances.
    """
    result: list[OutlineTreeNode] = []

    def walk(node: OutlineTreeNode) -> None:
        result.append(node)
        for child in node.children:
            walk(child)

    for root in roots:
        walk(root)
    return result
project
project(graph: Graph) -> list[OutlineTreeNode]

Build a forest of :class:OutlineTreeNode trees from graph.

Parameters:

Name Type Description Default
graph Graph

Outline graph to project.

required

Returns:

Type Description
list[OutlineTreeNode]

Ordered list of root :class:OutlineTreeNode instances (normally

list[OutlineTreeNode]

a single-element list for a well-formed outline).

Source code in src/knowledge_platform/modules/outline/projection.py
def project(self, graph: Graph) -> list[OutlineTreeNode]:
    """Build a forest of :class:`OutlineTreeNode` trees from *graph*.

    Args:
        graph: Outline graph to project.

    Returns:
        Ordered list of root :class:`OutlineTreeNode` instances (normally
        a single-element list for a well-formed outline).
    """
    # Identify all OutlineItem nodes.
    all_nodes = {n.id: n for n in graph.nodes(type_name="OutlineItem")}

    # Build parent→[child] mapping from ParentOf edges.
    children_map: dict[NodeId, list[Node]] = {nid: [] for nid in all_nodes}
    has_parent: set[NodeId] = set()

    for edge in graph.edges(type_name="ParentOf"):
        parent_id = edge.source_id
        child_id = edge.target_id
        if parent_id in all_nodes and child_id in all_nodes:
            children_map[parent_id].append(all_nodes[child_id])
            has_parent.add(child_id)

    # Sort children by position attribute.
    for nid in children_map:
        children_map[nid].sort(
            key=lambda n: int(n.attributes.get("position", 0))  # type: ignore[arg-type]
        )

    # Roots are nodes with no parent.
    root_nodes = [n for nid, n in all_nodes.items() if nid not in has_parent]
    root_nodes.sort(key=lambda n: int(n.attributes.get("position", 0)))  # type: ignore[arg-type]

    def build(node: Node, depth: int) -> OutlineTreeNode:
        tree_node = OutlineTreeNode(node=node, depth=depth)
        for child in children_map.get(node.id, []):
            tree_node.children.append(build(child, depth + 1))
        return tree_node

    return [build(r, 0) for r in root_nodes]

knowledge_platform.modules.outline.projection.OutlineTreeNode dataclass

A node in the projected tree view.

This is a view object – it holds a reference to the underlying domain :class:~knowledge_platform.core.node.Node and its resolved children, ordered by position attribute.

Attributes:

Name Type Description
node Node

The underlying domain node.

children list['OutlineTreeNode']

Ordered list of child :class:OutlineTreeNode instances.

depth int

Zero-based depth in the tree (0 = root).

Source code in src/knowledge_platform/modules/outline/projection.py
@dataclass
class OutlineTreeNode:
    """A node in the projected tree view.

    This is a *view* object – it holds a reference to the underlying domain
    :class:`~knowledge_platform.core.node.Node` and its resolved children,
    ordered by ``position`` attribute.

    Attributes:
        node: The underlying domain node.
        children: Ordered list of child :class:`OutlineTreeNode` instances.
        depth: Zero-based depth in the tree (0 = root).
    """

    node: Node
    children: list["OutlineTreeNode"] = field(default_factory=list)
    depth: int = 0

    @property
    def node_id(self) -> NodeId:
        """Convenience accessor for the underlying node ID."""
        return self.node.id

    @property
    def title(self) -> str:
        """Title attribute of the underlying node."""
        return str(self.node.attributes.get("title", ""))

    @property
    def content(self) -> str:
        """Content attribute of the underlying node."""
        return str(self.node.attributes.get("content", ""))

    @property
    def position(self) -> int:
        """Position attribute of the underlying node."""
        raw = self.node.attributes.get("position", 0)
        return int(raw) if isinstance(raw, (int, float)) else 0

Attributes

content property
content: str

Content attribute of the underlying node.

node_id property
node_id: NodeId

Convenience accessor for the underlying node ID.

position property
position: int

Position attribute of the underlying node.

title property
title: str

Title attribute of the underlying node.

knowledge_platform.modules.outline.module.OutlineModule

Plugin entry-point for the Outline module.

Registers the :class:OutlineGraphType with the platform's type registry and provides the factory for the module's primary UI widget.

Attributes:

Name Type Description
module_id str

"outline"

display_name str

"Outline"

graph_type str

:class:OutlineGraphType instance.

Source code in src/knowledge_platform/modules/outline/module.py
class OutlineModule:
    """Plugin entry-point for the Outline module.

    Registers the :class:`OutlineGraphType` with the platform's type registry
    and provides the factory for the module's primary UI widget.

    Attributes:
        module_id: ``"outline"``
        display_name: ``"Outline"``
        graph_type: :class:`OutlineGraphType` instance.
    """

    module_id: str = "outline"
    display_name: str = "Outline"
    document_label_singular: str = "Outline"
    document_label_plural: str = "Outlines"
    graph_types: tuple[OutlineGraphType, ...] = (OutlineGraphType(),)
    primary_graph_type: str = "outline"

    def __init__(self) -> None:
        self._settings: dict[str, Any] = {}

    def configure(self, settings: Mapping[str, Any]) -> None:
        """Store runtime module settings from ``modules.yaml``."""
        self._settings = dict(settings)

    def supports_graph_type(self, type_name: str) -> bool:
        """Return whether this module owns ``type_name``."""
        return any(graph_type.type_name == type_name for graph_type in self.graph_types)

    def create_widget(
        self,
        graph_service: IGraphService,
        parent: "QWidget | None" = None,
    ) -> "QWidget":
        """Construct and return the Outline module's main UI widget.

        Lazily imports PySide6 so the module can be used in headless contexts
        (e.g. unit tests) without a display.

        Args:
            graph_service: Application graph service passed from the platform.
            parent: Optional Qt parent widget.

        Returns:
            The :class:`~knowledge_platform.ui.outline.view.OutlineView` widget.
        """
        from knowledge_platform.ui.outline.view import OutlineView

        return OutlineView(graph_service=graph_service, parent=parent)

    def create_document(
        self,
        graph_service: IGraphService,
        workspace_id: WorkspaceId,
        name: str,
    ) -> Graph:
        """Create a new outline document."""
        return OutlineService(graph_service).create_outline(workspace_id, name)

    def list_documents(
        self,
        graph_service: IGraphService,
        workspace_id: WorkspaceId,
    ) -> list[Graph]:
        """List all outline documents in a workspace."""
        return OutlineService(graph_service).list_outlines(workspace_id)

Functions

configure
configure(settings: Mapping[str, Any]) -> None

Store runtime module settings from modules.yaml.

Source code in src/knowledge_platform/modules/outline/module.py
def configure(self, settings: Mapping[str, Any]) -> None:
    """Store runtime module settings from ``modules.yaml``."""
    self._settings = dict(settings)
create_document
create_document(
    graph_service: IGraphService,
    workspace_id: WorkspaceId,
    name: str,
) -> Graph

Create a new outline document.

Source code in src/knowledge_platform/modules/outline/module.py
def create_document(
    self,
    graph_service: IGraphService,
    workspace_id: WorkspaceId,
    name: str,
) -> Graph:
    """Create a new outline document."""
    return OutlineService(graph_service).create_outline(workspace_id, name)
create_widget
create_widget(
    graph_service: IGraphService,
    parent: "QWidget | None" = None,
) -> "QWidget"

Construct and return the Outline module's main UI widget.

Lazily imports PySide6 so the module can be used in headless contexts (e.g. unit tests) without a display.

Parameters:

Name Type Description Default
graph_service IGraphService

Application graph service passed from the platform.

required
parent 'QWidget | None'

Optional Qt parent widget.

None

Returns:

Name Type Description
The 'QWidget'

class:~knowledge_platform.ui.outline.view.OutlineView widget.

Source code in src/knowledge_platform/modules/outline/module.py
def create_widget(
    self,
    graph_service: IGraphService,
    parent: "QWidget | None" = None,
) -> "QWidget":
    """Construct and return the Outline module's main UI widget.

    Lazily imports PySide6 so the module can be used in headless contexts
    (e.g. unit tests) without a display.

    Args:
        graph_service: Application graph service passed from the platform.
        parent: Optional Qt parent widget.

    Returns:
        The :class:`~knowledge_platform.ui.outline.view.OutlineView` widget.
    """
    from knowledge_platform.ui.outline.view import OutlineView

    return OutlineView(graph_service=graph_service, parent=parent)
list_documents
list_documents(
    graph_service: IGraphService, workspace_id: WorkspaceId
) -> list[Graph]

List all outline documents in a workspace.

Source code in src/knowledge_platform/modules/outline/module.py
def list_documents(
    self,
    graph_service: IGraphService,
    workspace_id: WorkspaceId,
) -> list[Graph]:
    """List all outline documents in a workspace."""
    return OutlineService(graph_service).list_outlines(workspace_id)
supports_graph_type
supports_graph_type(type_name: str) -> bool

Return whether this module owns type_name.

Source code in src/knowledge_platform/modules/outline/module.py
def supports_graph_type(self, type_name: str) -> bool:
    """Return whether this module owns ``type_name``."""
    return any(graph_type.type_name == type_name for graph_type in self.graph_types)