```
# tree_app.py
# Python 3.12+ / Flet 0.28.3
from __future__ import annotations

from typing import List, Optional

import flet as ft
from pydantic import BaseModel, Field


# ========== Domain layer ======================================================
class TreeNode(BaseModel):
    """reasoning / response を持ち、再帰的に子ノードを保持できるノード。"""
    role: str
    reasoning: str
    response: str
    children: List["TreeNode"] = Field(default_factory=list)
    active_index: int = 0

    # ----- public api ----------------------------------------------------------
    def add_child(self, child: "TreeNode") -> None:
        self.children.append(child)

    def get_active_child(self) -> Optional["TreeNode"]:
        """active_index を補正しつつ現在アクティブな子ノードを返す。"""
        if not self.children:
            self.active_index = 0
            return None

        if self.active_index < 0 or self.active_index >= len(self.children):
            self.active_index = 0

        return self.children[self.active_index]


class Tree(BaseModel):
    root_nodes: List[TreeNode] = Field(default_factory=list)
    active_tree_index: int = 0

    # ----- public api ----------------------------------------------------------
    def add_root(self, root: TreeNode) -> None:
        self.root_nodes.append(root)

    def get_active_nodes(self) -> List[TreeNode]:
        """
        root → active_child → … と辿った際の「アクティブなノード集合」を返す。
        """
        if not self.root_nodes:
            self.active_tree_index = 0
            return []

        if (self.active_tree_index < 0
                or self.active_tree_index >= len(self.root_nodes)):
            self.active_tree_index = 0

        path: List[TreeNode] = []
        current: Optional[TreeNode] = self.root_nodes[self.active_tree_index]

        while current:
            path.append(current)
            current = current.get_active_child()

        return path


# ========== UI layer ==========================================================
class ChatCard(ft.Card):
    """単一の TreeNode ↔ 入力フォーム ↔ 更新ボタン"""

    def __init__(
            self,
            node: TreeNode,
            page: ft.Page,
            refresh_callback,  # 再描画用
            *args,
            **kwargs,
    ):
        # フィールド
        self.node = node
        self.page = page
        self.refresh_callback = refresh_callback

        # --------------- widgets ----------------------------------------------
        self.role_text = ft.Text(value=node.role, style=ft.TextThemeStyle.LABEL_SMALL)
        self.reasoning_tf = ft.TextField(
            value=node.reasoning,
            label="Reasoning",
            multiline=True,
            expand=True,
        )
        self.response_tf = ft.TextField(
            value=node.response,
            label="Response",
            multiline=True,
            expand=True,
        )
        update_btn = ft.OutlinedButton(text="更新", on_click=self._on_update)

        body = ft.Column(
            [
                self.role_text,
                self.reasoning_tf,
                self.response_tf,
                update_btn,
            ],
            tight=True,
        )
        super().__init__(content=body, *args, **kwargs)

    # --------------- event handler --------------------------------------------
    def _on_update(self, _):
        """TextField の内容を TreeNode に反映"""
        self.node.reasoning = self.reasoning_tf.value
        self.node.response = self.response_tf.value

        # UI フィードバック
        self.page.snack_bar = ft.SnackBar(
            content=ft.Text("ノードを更新しました！"),
            open=True,
        )
        self.page.update()

        # 必要なら親を再描画
        self.refresh_callback()


class NodeContainer(ft.Container):
    """
    TreeNode に対応するコンテナ。
    将来的に 1 ノード = 複数 ChatCard となってもここでまとめれば良い。
    今回は 1:1 なので単に ChatCard をラップしているだけ。
    """

    def __init__(
            self,
            node: TreeNode,
            page: ft.Page,
            refresh_callback,
            depth: int,
    ):
        chat = ChatCard(node, page, refresh_callback)
        super().__init__(
            content=chat,
            padding=10,
            bgcolor=ft.Colors.with_opacity(
                0.05 + depth * 0.05, ft.Colors.BLUE_500
            ),  # 深さに応じて色を少しずつ変える
            border_radius=8,
            expand=True,
        )


# ========== Flet main =========================================================
def main(page: ft.Page):
    page.title = "TreeNode Active Path Demo"
    page.scroll = ft.ScrollMode.AUTO

    # -------------- デモ用ツリー ----------------------------------------------
    root1 = TreeNode(role="system", reasoning="root 1", response="resp 1")
    child_a = TreeNode(role="user", reasoning="A", response="a")
    child_b = TreeNode(role="assistant", reasoning="B", response="b", active_index=0)
    child_b1 = TreeNode(role="user", reasoning="B1", response="b1")
    child_b.children = [child_b1]
    root1.children = [child_a, child_b]
    root1.active_index = 1

    demo_tree = Tree(root_nodes=[root1], active_tree_index=0)

    # -------------- 描画関数 --------------------------------------------------
    body_column = ft.Column(spacing=12)

    def rebuild_body():
        """現在のアクティブパスに基づき UI を再構築"""
        body_column.controls.clear()

        for depth, node in enumerate(demo_tree.get_active_nodes()):
            body_column.controls.append(
                NodeContainer(
                    node=node,
                    page=page,
                    refresh_callback=rebuild_body,
                    depth=depth,
                )
            )

        page.update()

    rebuild_body()  # 初回描画
    page.add(body_column)  # ルートに配置


# -----------------------------------------------------------------------------
# flet run tree_app.py
# -----------------------------------------------------------------------------
if __name__ == "__main__":
    ft.app(target=main)
```

Python 3.12-13, Flet 0.28.3.
各ノードが、自身が有効な子ノードのうち１つである場合、左右のボタンで親ノードの有効な子ノードのインデックスを-/+1できるようにして。