```
# src/models/markdown_model.py
from typing import Optional

import flet as ft
from pydantic import BaseModel, Field
from uuid6 import uuid7


class Section(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid7()))
    title: str
    content: str = ""
    sub_sections: list['Section'] = Field(default_factory=list)

    def find(self, title: str) -> Optional['Section']:
        """
        自分自身または子孫セクションの中から、title が一致する Section を返す。
        見つからなければ None。
        """
        if self.title == title:
            return self
        for sub in self.sub_sections:
            found = sub.find(title)
            if found:
                return found
        return None

    def find_by_id(self, section_id: str) -> Optional['Section']:
        """IDでセクションを検索"""
        if self.id == section_id:
            return self
        for sub in self.sub_sections:
            found = sub.find_by_id(section_id)
            if found:
                return found
        return None

    def remove_by_id(self, section_id: str) -> bool:
        """IDでセクションを削除"""
        for i, sub in enumerate(self.sub_sections):
            if sub.id == section_id:
                self.sub_sections.pop(i)
                return True
            if sub.remove_by_id(section_id):
                return True
        return False

    def to_markdown(self, level: int = 1, auto_format: bool = True) -> str:
        """
        このセクションとサブセクションを再帰的に Markdown 形式で返す。
        level は見出しレベル（# の数）を指定。
        """
        title = self.title
        if auto_format:
            if level <= 2:
                title = title.title()  # H1, H2: 各単語の先頭を大文字
            else:
                words = title.lower().split()
                if words:
                    words[0] = words[0].capitalize()  # H3以下: 先頭の単語だけ大文字
                    title = ' '.join(words)
        md = [f"{'#' * level} {title}"]
        if self.content:
            md.append(f"{self.content}")
        for sub in self.sub_sections:
            md.append("\n" + sub.to_markdown(level + 1, auto_format))
        return "\n".join(md)


class SectionEditor:
    def __init__(self, page: ft.Page):
        self.page = page
        self.root_section = Section(title="Root", content="")
        self.selected_section: Optional[Section] = None

        # サンプルデータ
        self._init_sample_data()

    def _init_sample_data(self):
        """サンプルデータの初期化"""
        intro = Section(title="introduction", content="This is the introduction.")
        intro.sub_sections.append(Section(title="background", content="Background information."))
        intro.sub_sections.append(Section(title="motivation", content="Why this matters."))

        methods = Section(title="methods", content="Our methodology.")
        methods.sub_sections.append(Section(title="data collection", content="How we collected data."))
        methods.sub_sections.append(Section(title="analysis", content="How we analyzed data."))

        self.root_section.sub_sections.extend([intro, methods])

    def create_section_tile(self, section: Section, level: int = 0) -> ft.Container:
        """セクションをドラッグ可能なタイルとして作成"""

        def on_drag_accept(e):
            # ドラッグされたセクションのIDを取得
            dragged_id = e.src_id
            target_id = e.control.data

            if dragged_id == target_id:
                return

            # ドラッグされたセクションを探す
            dragged_section = self.root_section.find_by_id(dragged_id)
            if not dragged_section:
                return

            # 元の場所から削除
            self.root_section.remove_by_id(dragged_id)

            # ターゲットセクションに追加
            if target_id == "root":
                self.root_section.sub_sections.append(dragged_section)
            else:
                target_section = self.root_section.find_by_id(target_id)
                if target_section:
                    target_section.sub_sections.append(dragged_section)

            self.update_tree()
            self.update_preview()

        def on_click(e):
            self.selected_section = section
            self.update_editor()

        # セクションタイトル
        title_row = ft.Row([
            ft.Icon(ft.Icons.FOLDER_OPEN if section.sub_sections else ft.Icons.ARTICLE, size=16),
            ft.Text(section.title, size=14, weight=ft.FontWeight.W_500),
        ], spacing=5)

        # ドラッグ可能なコンテナ
        draggable = ft.Draggable(
            group="section",
            content=ft.Container(
                content=title_row,
                border_radius=5,
                padding=ft.padding.all(8),
                on_click=on_click,
            ),
            content_feedback=ft.Container(
                content=ft.Text(section.title, size=12),
                border_radius=5,
                padding=ft.padding.all(5),
            ),
        )

        # ドロップターゲット
        drag_target = ft.DragTarget(
            group="section",
            content=draggable,
            on_accept=on_drag_accept,
            data=section.id,
        )

        # サブセクションを含むコンテナ
        items = [drag_target]
        for sub in section.sub_sections:
            items.append(self.create_section_tile(sub, level + 1))

        return ft.Container(
            content=ft.Column(items, spacing=2),
            padding=ft.padding.only(left=level * 20),
        )

    def update_tree(self):
        """ツリービューを更新"""
        self.tree_container.content = ft.Column(
            [self.create_section_tile(sub) for sub in self.root_section.sub_sections],
            spacing=5,
            scroll=ft.ScrollMode.AUTO,
        )
        self.page.update()

    def update_editor(self):
        """選択されたセクションの編集フォームを更新"""
        if not self.selected_section:
            self.editor_container.content = ft.Text("セクションを選択してください")
        else:
            self.title_field.value = self.selected_section.title
            self.content_field.value = self.selected_section.content
            self.editor_container.content = self.editor_form
        self.page.update()

    def update_preview(self):
        """Markdownプレビューを更新"""
        markdown_text = ""
        for section in self.root_section.sub_sections:
            markdown_text += section.to_markdown() + "\n\n"
        self.preview_text.value = markdown_text
        self.page.update()

    def save_changes(self, e):
        """変更を保存"""
        if self.selected_section:
            self.selected_section.title = self.title_field.value
            self.selected_section.content = self.content_field.value
            self.update_tree()
            self.update_preview()

    def add_section(self, e):
        """新しいセクションを追加"""
        new_section = Section(title="New Section", content="")
        if self.selected_section:
            self.selected_section.sub_sections.append(new_section)
        else:
            self.root_section.sub_sections.append(new_section)
        self.update_tree()
        self.update_preview()

    def delete_section(self, e):
        """選択されたセクションを削除"""
        if self.selected_section and self.selected_section.id != self.root_section.id:
            self.root_section.remove_by_id(self.selected_section.id)
            self.selected_section = None
            self.update_tree()
            self.update_editor()
            self.update_preview()

    def build(self):
        """UIを構築"""
        # 編集フォーム
        self.title_field = ft.TextField(label="タイトル", expand=True)
        self.content_field = ft.TextField(
            label="コンテンツ",
            multiline=True,
            min_lines=3,
            expand=True
        )

        self.editor_form = ft.Column([
            self.title_field,
            self.content_field,
            ft.Row([
                ft.ElevatedButton("保存", on_click=self.save_changes, icon=ft.Icons.SAVE),
                ft.ElevatedButton("削除", on_click=self.delete_section, icon=ft.Icons.DELETE, color=ft.Colors.RED),
            ], spacing=10),
        ], spacing=10)

        # ツリービュー
        self.tree_container = ft.Container(
            expand=True,
            border_radius=10,
            padding=10,
        )

        # エディターコンテナ
        self.editor_container = ft.Container(
            expand=True,
            border_radius=10,
            padding=20,
        )

        # プレビュー
        self.preview_text = ft.Text(
            selectable=True,
            expand=True,
        )

        # レイアウト
        left_panel = ft.Container(
            content=ft.Column([
                ft.Row([
                    ft.Text("セクション構造", size=18, weight=ft.FontWeight.BOLD),
                    ft.IconButton(ft.Icons.ADD_CIRCLE, on_click=self.add_section, tooltip="新規セクション追加"),
                ]),
                self.tree_container,
            ], expand=True),
            width=300,
            padding=10,
        )

        center_panel = ft.Container(
            content=ft.Column([
                ft.Text("セクション編集", size=18, weight=ft.FontWeight.BOLD),
                self.editor_container,
            ], expand=True),
            expand=True,
            padding=10,
        )

        right_panel = ft.Container(
            content=ft.Column([
                ft.Text("Markdownプレビュー", size=18, weight=ft.FontWeight.BOLD),
                ft.Container(
                    content=self.preview_text,
                    border_radius=10,
                    padding=15,
                    expand=True,
                ),
            ], expand=True, scroll=ft.ScrollMode.AUTO),
            width=400,
            padding=10,
        )

        # 初期化
        self.update_tree()
        self.update_editor()
        self.update_preview()

        return ft.Row([
            left_panel,
            ft.VerticalDivider(width=1),
            center_panel,
            ft.VerticalDivider(width=1),
            right_panel,
        ], expand=True)


def main(page: ft.Page):
    page.title = "Section Editor - Drag & Drop"
    page.window_width = 1200
    page.window_height = 700

    editor = SectionEditor(page)
    page.add(editor.build())


if __name__ == "__main__":
    ft.app(target=main)
```

Python 3.12+, Flet 0.28.3.
GUIによる編集が、D&Dのみうまく機能しない。ドラッグすることのみ可能で、ドロップできない。
ドラッグ中にリアルタイムで移動先を半透明表示し、ドロップで確定できるように変更して。