<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Cradicle Explorer</title>
    <link href="/css/bootstrap/bootstrap.min.css" rel="stylesheet">
    <style>
      .form-control-dark::placeholder {
          color: #aaa;
          opacity: 1;
      }
    </style>
    <link rel="stylesheet" href="/assets/fontawesome/css/all.min.css">
    <link rel="icon" type="image/png" href="/favicon.png">


                <link href="/css/dashboard.css" rel="stylesheet">
                </head>
                <body>
                <header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
                  <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="/">Cradicle Explorer</a>
                  <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                  </button>
                  <form method="get" action="/cgi-bin/main" style="width:100%;"><input class="form-control form-control-dark w-100 rounded-0 border-0" type="text" name="q" placeholder="Search repos" aria-label="Search"></form>
                  <div class="navbar-nav flex-row">
                    <div class="nav-item text-nowrap">
                      <a class="nav-link px-3 active" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS">DreamTalk</a>
                    </div>
                  </div>
                </header>
                <div class="container-fluid">
                  <div class="row">
                    <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-dark sidebar collapse">
                      <div class="position-sticky pt-3 sidebar-sticky">
                        <ul class="nav flex-column">
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS">
                              <i class="align-text-bottom fa-solid fa-info"></i>
                              Info
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&issue=list">
                              <i class="align-text-bottom fa-solid fa-layer-group"></i>
                              Issues
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&patch=list">
                              <i class="align-text-bottom fa-solid fa-vest-patches"></i>
                              Patches
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&wallet=list">
                              <i class="align-text-bottom fa-solid fa-wallet"></i>
                              Wallets
                            </a>
                          </li>
                          <li class="nav-item">
                            <a class="nav-link active" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=.">
                              <i class="align-text-bottom fa-solid fa-code"></i>
                              Source
                            </a>
                          </li>
                        <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted text-uppercase">
                          <span></span>
                        </h6>
                        <ul class="nav flex-column mb-2">
                        
    <h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-1 mb-1 text-muted text-uppercase">
      <span>Source</span>
    </h6>
    <li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=.claude"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> .claude</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=animation"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> animation</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=assets"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> assets</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=docs"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> docs</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=introspection"><i class="fa-solid fa-folder-open" style="color:#f0c040;"></i> introspection</a></li><li><a class="nav-link py-0" style="padding-left:32px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=introspection%2F__init__.py"><i class="fa-solid fa-file" style="color:#888;"></i> __init__.py</a></li><li><a class="nav-link py-0 active" style="padding-left:32px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=introspection%2Fformatters.py"><i class="fa-solid fa-file" style="color:#888;"></i> formatters.py</a></li><li><a class="nav-link py-0" style="padding-left:32px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=introspection%2Fhierarchy.py"><i class="fa-solid fa-file" style="color:#888;"></i> hierarchy.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=legacy"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> legacy</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=mcp-servers"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> mcp-servers</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=objects"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> objects</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=scripts"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> scripts</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=templates"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> templates</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=test_scenes"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> test_scenes</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=tests"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> tests</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=xpresso"><i class="fa-solid fa-folder" style="color:#f0c040;"></i> xpresso</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=.gitignore"><i class="fa-solid fa-file" style="color:#888;"></i> .gitignore</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=.mcp.json.template"><i class="fa-solid fa-file" style="color:#888;"></i> .mcp.json.template</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=.udd"><i class="fa-solid fa-file" style="color:#888;"></i> .udd</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=CLAUDE.md"><i class="fa-solid fa-file" style="color:#888;"></i> CLAUDE.md</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=DreamTalk.gif"><i class="fa-solid fa-file" style="color:#888;"></i> DreamTalk.gif</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=DreamTalk.key"><i class="fa-solid fa-file" style="color:#888;"></i> DreamTalk.key</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=DreamTalk.png"><i class="fa-solid fa-file" style="color:#888;"></i> DreamTalk.png</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=DreamTalk.sublime-project"><i class="fa-solid fa-file" style="color:#888;"></i> DreamTalk.sublime-project</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=DreamTalk.sublime-workspace"><i class="fa-solid fa-file" style="color:#888;"></i> DreamTalk.sublime-workspace</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=LICENSE"><i class="fa-solid fa-file" style="color:#888;"></i> LICENSE</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=README.md"><i class="fa-solid fa-file" style="color:#888;"></i> README.md</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=__init__.py"><i class="fa-solid fa-file" style="color:#888;"></i> __init__.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=bootstrap.py"><i class="fa-solid fa-file" style="color:#888;"></i> bootstrap.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=constants.py"><i class="fa-solid fa-file" style="color:#888;"></i> constants.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=dreamtalk.py"><i class="fa-solid fa-file" style="color:#888;"></i> dreamtalk.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=dreamtalk_init.py"><i class="fa-solid fa-file" style="color:#888;"></i> dreamtalk_init.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=generator.py"><i class="fa-solid fa-file" style="color:#888;"></i> generator.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=imports.py"><i class="fa-solid fa-file" style="color:#888;"></i> imports.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=materials.py"><i class="fa-solid fa-file" style="color:#888;"></i> materials.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=pydeation.sublime-project"><i class="fa-solid fa-file" style="color:#888;"></i> pydeation.sublime-project</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=pydeation.sublime-workspace"><i class="fa-solid fa-file" style="color:#888;"></i> pydeation.sublime-workspace</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=scene.py"><i class="fa-solid fa-file" style="color:#888;"></i> scene.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=tags.py"><i class="fa-solid fa-file" style="color:#888;"></i> tags.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=test_binding_syntax.py"><i class="fa-solid fa-file" style="color:#888;"></i> test_binding_syntax.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=test_camera_generator.py"><i class="fa-solid fa-file" style="color:#888;"></i> test_camera_generator.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=test_foldable_generator.py"><i class="fa-solid fa-file" style="color:#888;"></i> test_foldable_generator.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=test_full_migration.py"><i class="fa-solid fa-file" style="color:#888;"></i> test_full_migration.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=test_mindvirus_infection.py"><i class="fa-solid fa-file" style="color:#888;"></i> test_mindvirus_infection.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=test_v2_architecture.py"><i class="fa-solid fa-file" style="color:#888;"></i> test_v2_architecture.py</a></li><li><a class="nav-link py-0" style="padding-left:16px;" href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&file=utils.py"><i class="fa-solid fa-file" style="color:#888;"></i> utils.py</a></li>
    
                        </ul>
                      </div>
                    </nav>
                <main class="col-md-9 ms-sm-auto col-lg-10">
                  <div class="container px-1 py-3">
        
<div class="mb-2" style="font-size:1.1rem;"><a href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=.">/</a> <a href="/cgi-bin/repo?id=zuPiXejbtFFCdDDs7usyJUSQvwaS&source=introspection">introspection</a> / formatters.py</div>
        <div class="list-group">
        <div class="list-group-item">
        <div class="mb-2" style="font-weight:bold;"><i class="fa-solid fa-file"></i> formatters.py</div>
        <pre style="margin:0; font-size:0.85rem; overflow-x:auto; color:#fafafa;"><span style="color:#666; user-select:none;">  1</span>  &quot;&quot;&quot;
<span style="color:#666; user-select:none;">  2</span>  Output Formatters for AI Consumption
<span style="color:#666; user-select:none;">  3</span>  
<span style="color:#666; user-select:none;">  4</span>  Transforms hierarchy descriptions into formats optimized for AI context windows.
<span style="color:#666; user-select:none;">  5</span>  &quot;&quot;&quot;
<span style="color:#666; user-select:none;">  6</span>  
<span style="color:#666; user-select:none;">  7</span>  import json
<span style="color:#666; user-select:none;">  8</span>  
<span style="color:#666; user-select:none;">  9</span>  
<span style="color:#666; user-select:none;"> 10</span>  def format_json(hierarchy_result):
<span style="color:#666; user-select:none;"> 11</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;"> 12</span>      Format hierarchy result as JSON string.
<span style="color:#666; user-select:none;"> 13</span>  
<span style="color:#666; user-select:none;"> 14</span>      Args:
<span style="color:#666; user-select:none;"> 15</span>          hierarchy_result: dict from describe_hierarchy()
<span style="color:#666; user-select:none;"> 16</span>  
<span style="color:#666; user-select:none;"> 17</span>      Returns:
<span style="color:#666; user-select:none;"> 18</span>          JSON string
<span style="color:#666; user-select:none;"> 19</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;"> 20</span>      return json.dumps(hierarchy_result, indent=2)
<span style="color:#666; user-select:none;"> 21</span>  
<span style="color:#666; user-select:none;"> 22</span>  
<span style="color:#666; user-select:none;"> 23</span>  def format_markdown(hierarchy_result):
<span style="color:#666; user-select:none;"> 24</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;"> 25</span>      Format hierarchy result as markdown for AI context.
<span style="color:#666; user-select:none;"> 26</span>  
<span style="color:#666; user-select:none;"> 27</span>      Produces a tree-like structure that&#x27;s easy to scan and understand.
<span style="color:#666; user-select:none;"> 28</span>  
<span style="color:#666; user-select:none;"> 29</span>      Args:
<span style="color:#666; user-select:none;"> 30</span>          hierarchy_result: dict from describe_hierarchy()
<span style="color:#666; user-select:none;"> 31</span>  
<span style="color:#666; user-select:none;"> 32</span>      Returns:
<span style="color:#666; user-select:none;"> 33</span>          Markdown string
<span style="color:#666; user-select:none;"> 34</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;"> 35</span>      lines = []
<span style="color:#666; user-select:none;"> 36</span>  
<span style="color:#666; user-select:none;"> 37</span>      # Header
<span style="color:#666; user-select:none;"> 38</span>      doc_name = hierarchy_result.get(&quot;document_name&quot;, &quot;Untitled&quot;)
<span style="color:#666; user-select:none;"> 39</span>      lines.append(f&quot;# Scene: {doc_name}&quot;)
<span style="color:#666; user-select:none;"> 40</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;"> 41</span>  
<span style="color:#666; user-select:none;"> 42</span>      # Summary
<span style="color:#666; user-select:none;"> 43</span>      lines.append(f&quot;**{hierarchy_result.get(&#x27;summary&#x27;, &#x27;No objects&#x27;)}**&quot;)
<span style="color:#666; user-select:none;"> 44</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;"> 45</span>  
<span style="color:#666; user-select:none;"> 46</span>      # Object tree
<span style="color:#666; user-select:none;"> 47</span>      if hierarchy_result.get(&quot;objects&quot;):
<span style="color:#666; user-select:none;"> 48</span>          lines.append(&quot;## Hierarchy&quot;)
<span style="color:#666; user-select:none;"> 49</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;"> 50</span>          lines.append(&quot;```&quot;)
<span style="color:#666; user-select:none;"> 51</span>  
<span style="color:#666; user-select:none;"> 52</span>          for obj in hierarchy_result[&quot;objects&quot;]:
<span style="color:#666; user-select:none;"> 53</span>              lines.extend(_format_object_tree(obj, prefix=&quot;&quot;, is_last=True))
<span style="color:#666; user-select:none;"> 54</span>  
<span style="color:#666; user-select:none;"> 55</span>          lines.append(&quot;```&quot;)
<span style="color:#666; user-select:none;"> 56</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;"> 57</span>  
<span style="color:#666; user-select:none;"> 58</span>      # Stats
<span style="color:#666; user-select:none;"> 59</span>      stats = hierarchy_result.get(&quot;stats&quot;, {})
<span style="color:#666; user-select:none;"> 60</span>      if stats:
<span style="color:#666; user-select:none;"> 61</span>          lines.append(&quot;## Stats&quot;)
<span style="color:#666; user-select:none;"> 62</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;"> 63</span>          lines.append(f&quot;- Total objects: {stats.get(&#x27;total_objects&#x27;, 0)}&quot;)
<span style="color:#666; user-select:none;"> 64</span>          if stats.get(&quot;custom_objects&quot;):
<span style="color:#666; user-select:none;"> 65</span>              lines.append(f&quot;- CustomObjects: {stats[&#x27;custom_objects&#x27;]}&quot;)
<span style="color:#666; user-select:none;"> 66</span>          if stats.get(&quot;line_objects&quot;):
<span style="color:#666; user-select:none;"> 67</span>              lines.append(f&quot;- LineObjects: {stats[&#x27;line_objects&#x27;]}&quot;)
<span style="color:#666; user-select:none;"> 68</span>          if stats.get(&quot;solid_objects&quot;):
<span style="color:#666; user-select:none;"> 69</span>              lines.append(f&quot;- SolidObjects: {stats[&#x27;solid_objects&#x27;]}&quot;)
<span style="color:#666; user-select:none;"> 70</span>          lines.append(f&quot;- Max depth: {stats.get(&#x27;max_depth&#x27;, 0)}&quot;)
<span style="color:#666; user-select:none;"> 71</span>  
<span style="color:#666; user-select:none;"> 72</span>      return &quot;\n&quot;.join(lines)
<span style="color:#666; user-select:none;"> 73</span>  
<span style="color:#666; user-select:none;"> 74</span>  
<span style="color:#666; user-select:none;"> 75</span>  def _format_object_tree(obj, prefix=&quot;&quot;, is_last=True):
<span style="color:#666; user-select:none;"> 76</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;"> 77</span>      Recursively format an object and its children as a tree.
<span style="color:#666; user-select:none;"> 78</span>  
<span style="color:#666; user-select:none;"> 79</span>      Args:
<span style="color:#666; user-select:none;"> 80</span>          obj: object description dict
<span style="color:#666; user-select:none;"> 81</span>          prefix: current line prefix for indentation
<span style="color:#666; user-select:none;"> 82</span>          is_last: whether this is the last sibling
<span style="color:#666; user-select:none;"> 83</span>  
<span style="color:#666; user-select:none;"> 84</span>      Returns:
<span style="color:#666; user-select:none;"> 85</span>          list of formatted lines
<span style="color:#666; user-select:none;"> 86</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;"> 87</span>      lines = []
<span style="color:#666; user-select:none;"> 88</span>  
<span style="color:#666; user-select:none;"> 89</span>      # Build the connector
<span style="color:#666; user-select:none;"> 90</span>      connector = &quot;└── &quot; if is_last else &quot;├── &quot;
<span style="color:#666; user-select:none;"> 91</span>  
<span style="color:#666; user-select:none;"> 92</span>      # Build object description line
<span style="color:#666; user-select:none;"> 93</span>      name = obj.get(&quot;name&quot;, &quot;Unknown&quot;)
<span style="color:#666; user-select:none;"> 94</span>      obj_type = obj.get(&quot;type&quot;, &quot;Unknown&quot;)
<span style="color:#666; user-select:none;"> 95</span>  
<span style="color:#666; user-select:none;"> 96</span>      # Start with name and type
<span style="color:#666; user-select:none;"> 97</span>      desc_parts = [f&quot;{name} ({obj_type})&quot;]
<span style="color:#666; user-select:none;"> 98</span>  
<span style="color:#666; user-select:none;"> 99</span>      # Add position if not at origin
<span style="color:#666; user-select:none;">100</span>      pos = obj.get(&quot;position&quot;, {})
<span style="color:#666; user-select:none;">101</span>      if pos.get(&quot;x&quot;, 0) != 0 or pos.get(&quot;y&quot;, 0) != 0 or pos.get(&quot;z&quot;, 0) != 0:
<span style="color:#666; user-select:none;">102</span>          desc_parts.append(f&quot;@ ({pos.get(&#x27;x&#x27;, 0)}, {pos.get(&#x27;y&#x27;, 0)}, {pos.get(&#x27;z&#x27;, 0)})&quot;)
<span style="color:#666; user-select:none;">103</span>  
<span style="color:#666; user-select:none;">104</span>      # Add color
<span style="color:#666; user-select:none;">105</span>      if obj.get(&quot;color&quot;):
<span style="color:#666; user-select:none;">106</span>          desc_parts.append(f&quot;[{obj[&#x27;color&#x27;]}]&quot;)
<span style="color:#666; user-select:none;">107</span>  
<span style="color:#666; user-select:none;">108</span>      # Add creation progress
<span style="color:#666; user-select:none;">109</span>      if obj.get(&quot;creation&quot;) is not None:
<span style="color:#666; user-select:none;">110</span>          desc_parts.append(f&quot;creation:{obj[&#x27;creation&#x27;]}%&quot;)
<span style="color:#666; user-select:none;">111</span>  
<span style="color:#666; user-select:none;">112</span>      # Add draw progress
<span style="color:#666; user-select:none;">113</span>      if obj.get(&quot;draw&quot;) is not None and obj.get(&quot;draw&quot;) != 100:
<span style="color:#666; user-select:none;">114</span>          desc_parts.append(f&quot;draw:{obj[&#x27;draw&#x27;]}%&quot;)
<span style="color:#666; user-select:none;">115</span>  
<span style="color:#666; user-select:none;">116</span>      # Add scale if non-default
<span style="color:#666; user-select:none;">117</span>      if obj.get(&quot;scale&quot;) and obj[&quot;scale&quot;] != 1:
<span style="color:#666; user-select:none;">118</span>          desc_parts.append(f&quot;scale:{obj[&#x27;scale&#x27;]}&quot;)
<span style="color:#666; user-select:none;">119</span>  
<span style="color:#666; user-select:none;">120</span>      line = prefix + connector + &quot; &quot;.join(desc_parts)
<span style="color:#666; user-select:none;">121</span>      lines.append(line)
<span style="color:#666; user-select:none;">122</span>  
<span style="color:#666; user-select:none;">123</span>      # Process children
<span style="color:#666; user-select:none;">124</span>      children = obj.get(&quot;children&quot;, [])
<span style="color:#666; user-select:none;">125</span>      if children:
<span style="color:#666; user-select:none;">126</span>          # Update prefix for children
<span style="color:#666; user-select:none;">127</span>          child_prefix = prefix + (&quot;    &quot; if is_last else &quot;│   &quot;)
<span style="color:#666; user-select:none;">128</span>  
<span style="color:#666; user-select:none;">129</span>          for i, child in enumerate(children):
<span style="color:#666; user-select:none;">130</span>              child_is_last = (i == len(children) - 1)
<span style="color:#666; user-select:none;">131</span>              lines.extend(_format_object_tree(child, child_prefix, child_is_last))
<span style="color:#666; user-select:none;">132</span>  
<span style="color:#666; user-select:none;">133</span>      return lines
<span style="color:#666; user-select:none;">134</span>  
<span style="color:#666; user-select:none;">135</span>  
<span style="color:#666; user-select:none;">136</span>  def format_for_ai(hierarchy_result, format_type=&quot;markdown&quot;):
<span style="color:#666; user-select:none;">137</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">138</span>      Format hierarchy result for AI consumption.
<span style="color:#666; user-select:none;">139</span>  
<span style="color:#666; user-select:none;">140</span>      Args:
<span style="color:#666; user-select:none;">141</span>          hierarchy_result: dict from describe_hierarchy()
<span style="color:#666; user-select:none;">142</span>          format_type: &quot;markdown&quot; or &quot;json&quot;
<span style="color:#666; user-select:none;">143</span>  
<span style="color:#666; user-select:none;">144</span>      Returns:
<span style="color:#666; user-select:none;">145</span>          Formatted string
<span style="color:#666; user-select:none;">146</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">147</span>      if format_type == &quot;json&quot;:
<span style="color:#666; user-select:none;">148</span>          return format_json(hierarchy_result)
<span style="color:#666; user-select:none;">149</span>      else:
<span style="color:#666; user-select:none;">150</span>          return format_markdown(hierarchy_result)
<span style="color:#666; user-select:none;">151</span>  
<span style="color:#666; user-select:none;">152</span>  
<span style="color:#666; user-select:none;">153</span>  def format_compact(hierarchy_result):
<span style="color:#666; user-select:none;">154</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">155</span>      Format as a compact single-line summary.
<span style="color:#666; user-select:none;">156</span>  
<span style="color:#666; user-select:none;">157</span>      Useful for quick status checks.
<span style="color:#666; user-select:none;">158</span>  
<span style="color:#666; user-select:none;">159</span>      Args:
<span style="color:#666; user-select:none;">160</span>          hierarchy_result: dict from describe_hierarchy()
<span style="color:#666; user-select:none;">161</span>  
<span style="color:#666; user-select:none;">162</span>      Returns:
<span style="color:#666; user-select:none;">163</span>          Single line summary string
<span style="color:#666; user-select:none;">164</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">165</span>      stats = hierarchy_result.get(&quot;stats&quot;, {})
<span style="color:#666; user-select:none;">166</span>      doc_name = hierarchy_result.get(&quot;document_name&quot;, &quot;Untitled&quot;)
<span style="color:#666; user-select:none;">167</span>  
<span style="color:#666; user-select:none;">168</span>      parts = [f&quot;Scene: {doc_name}&quot;]
<span style="color:#666; user-select:none;">169</span>      parts.append(f&quot;{stats.get(&#x27;total_objects&#x27;, 0)} objects&quot;)
<span style="color:#666; user-select:none;">170</span>  
<span style="color:#666; user-select:none;">171</span>      type_counts = []
<span style="color:#666; user-select:none;">172</span>      if stats.get(&quot;custom_objects&quot;):
<span style="color:#666; user-select:none;">173</span>          type_counts.append(f&quot;{stats[&#x27;custom_objects&#x27;]}C&quot;)
<span style="color:#666; user-select:none;">174</span>      if stats.get(&quot;line_objects&quot;):
<span style="color:#666; user-select:none;">175</span>          type_counts.append(f&quot;{stats[&#x27;line_objects&#x27;]}L&quot;)
<span style="color:#666; user-select:none;">176</span>      if stats.get(&quot;solid_objects&quot;):
<span style="color:#666; user-select:none;">177</span>          type_counts.append(f&quot;{stats[&#x27;solid_objects&#x27;]}S&quot;)
<span style="color:#666; user-select:none;">178</span>  
<span style="color:#666; user-select:none;">179</span>      if type_counts:
<span style="color:#666; user-select:none;">180</span>          parts.append(f&quot;({&#x27;/&#x27;.join(type_counts)})&quot;)
<span style="color:#666; user-select:none;">181</span>  
<span style="color:#666; user-select:none;">182</span>      return &quot; | &quot;.join(parts)
<span style="color:#666; user-select:none;">183</span>  
<span style="color:#666; user-select:none;">184</span>  
<span style="color:#666; user-select:none;">185</span>  def format_inspect_object(result):
<span style="color:#666; user-select:none;">186</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">187</span>      Format inspect_object result as markdown.
<span style="color:#666; user-select:none;">188</span>  
<span style="color:#666; user-select:none;">189</span>      Args:
<span style="color:#666; user-select:none;">190</span>          result: dict from inspect_object()
<span style="color:#666; user-select:none;">191</span>  
<span style="color:#666; user-select:none;">192</span>      Returns:
<span style="color:#666; user-select:none;">193</span>          Markdown string
<span style="color:#666; user-select:none;">194</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">195</span>      if &quot;error&quot; in result:
<span style="color:#666; user-select:none;">196</span>          return f&quot;**Error:** {result[&#x27;error&#x27;]}&quot;
<span style="color:#666; user-select:none;">197</span>  
<span style="color:#666; user-select:none;">198</span>      lines = []
<span style="color:#666; user-select:none;">199</span>      lines.append(f&quot;# Object: {result[&#x27;name&#x27;]}&quot;)
<span style="color:#666; user-select:none;">200</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">201</span>      lines.append(f&quot;**Type:** {result[&#x27;type&#x27;]} ({result.get(&#x27;c4d_type&#x27;, &#x27;Unknown&#x27;)})&quot;)
<span style="color:#666; user-select:none;">202</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">203</span>  
<span style="color:#666; user-select:none;">204</span>      # Transform
<span style="color:#666; user-select:none;">205</span>      t = result.get(&quot;transform&quot;, {})
<span style="color:#666; user-select:none;">206</span>      pos = t.get(&quot;position&quot;, {})
<span style="color:#666; user-select:none;">207</span>      rot = t.get(&quot;rotation&quot;, {})
<span style="color:#666; user-select:none;">208</span>      scale = t.get(&quot;scale&quot;, {})
<span style="color:#666; user-select:none;">209</span>      lines.append(&quot;## Transform&quot;)
<span style="color:#666; user-select:none;">210</span>      lines.append(f&quot;- Position: ({pos.get(&#x27;x&#x27;, 0)}, {pos.get(&#x27;y&#x27;, 0)}, {pos.get(&#x27;z&#x27;, 0)})&quot;)
<span style="color:#666; user-select:none;">211</span>      lines.append(f&quot;- Rotation: ({rot.get(&#x27;h&#x27;, 0)}°, {rot.get(&#x27;p&#x27;, 0)}°, {rot.get(&#x27;b&#x27;, 0)}°)&quot;)
<span style="color:#666; user-select:none;">212</span>      lines.append(f&quot;- Scale: ({scale.get(&#x27;x&#x27;, 1)}, {scale.get(&#x27;y&#x27;, 1)}, {scale.get(&#x27;z&#x27;, 1)})&quot;)
<span style="color:#666; user-select:none;">213</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">214</span>  
<span style="color:#666; user-select:none;">215</span>      # Hierarchy
<span style="color:#666; user-select:none;">216</span>      if result.get(&quot;parent&quot;) or result.get(&quot;children&quot;):
<span style="color:#666; user-select:none;">217</span>          lines.append(&quot;## Hierarchy&quot;)
<span style="color:#666; user-select:none;">218</span>          if result.get(&quot;parent&quot;):
<span style="color:#666; user-select:none;">219</span>              lines.append(f&quot;- Parent: {result[&#x27;parent&#x27;]}&quot;)
<span style="color:#666; user-select:none;">220</span>          if result.get(&quot;children&quot;):
<span style="color:#666; user-select:none;">221</span>              lines.append(f&quot;- Children: {&#x27;, &#x27;.join(result[&#x27;children&#x27;])}&quot;)
<span style="color:#666; user-select:none;">222</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">223</span>  
<span style="color:#666; user-select:none;">224</span>      # Color
<span style="color:#666; user-select:none;">225</span>      if result.get(&quot;color&quot;):
<span style="color:#666; user-select:none;">226</span>          c = result[&quot;color&quot;]
<span style="color:#666; user-select:none;">227</span>          lines.append(&quot;## Color&quot;)
<span style="color:#666; user-select:none;">228</span>          lines.append(f&quot;- Name: {c.get(&#x27;name&#x27;, &#x27;Unknown&#x27;)}&quot;)
<span style="color:#666; user-select:none;">229</span>          rgb = c.get(&quot;rgb&quot;, {})
<span style="color:#666; user-select:none;">230</span>          lines.append(f&quot;- RGB: ({rgb.get(&#x27;r&#x27;, 0)}, {rgb.get(&#x27;g&#x27;, 0)}, {rgb.get(&#x27;b&#x27;, 0)})&quot;)
<span style="color:#666; user-select:none;">231</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">232</span>  
<span style="color:#666; user-select:none;">233</span>      # Userdata
<span style="color:#666; user-select:none;">234</span>      if result.get(&quot;userdata&quot;):
<span style="color:#666; user-select:none;">235</span>          lines.append(&quot;## Parameters&quot;)
<span style="color:#666; user-select:none;">236</span>          for group, params in result[&quot;userdata&quot;].items():
<span style="color:#666; user-select:none;">237</span>              lines.append(f&quot;### {group}&quot;)
<span style="color:#666; user-select:none;">238</span>              for name, value in params.items():
<span style="color:#666; user-select:none;">239</span>                  if isinstance(value, dict):
<span style="color:#666; user-select:none;">240</span>                      value = f&quot;({value.get(&#x27;x&#x27;, 0)}, {value.get(&#x27;y&#x27;, 0)}, {value.get(&#x27;z&#x27;, 0)})&quot;
<span style="color:#666; user-select:none;">241</span>                  elif isinstance(value, float):
<span style="color:#666; user-select:none;">242</span>                      value = round(value, 3)
<span style="color:#666; user-select:none;">243</span>                  lines.append(f&quot;- {name}: {value}&quot;)
<span style="color:#666; user-select:none;">244</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">245</span>  
<span style="color:#666; user-select:none;">246</span>      # Tags
<span style="color:#666; user-select:none;">247</span>      if result.get(&quot;tags&quot;):
<span style="color:#666; user-select:none;">248</span>          lines.append(&quot;## Tags&quot;)
<span style="color:#666; user-select:none;">249</span>          for tag in result[&quot;tags&quot;]:
<span style="color:#666; user-select:none;">250</span>              tag_line = f&quot;- {tag[&#x27;type&#x27;]}&quot;
<span style="color:#666; user-select:none;">251</span>              if tag.get(&quot;material&quot;):
<span style="color:#666; user-select:none;">252</span>                  tag_line += f&quot; → {tag[&#x27;material&#x27;]}&quot;
<span style="color:#666; user-select:none;">253</span>              lines.append(tag_line)
<span style="color:#666; user-select:none;">254</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">255</span>  
<span style="color:#666; user-select:none;">256</span>      # Bounding box
<span style="color:#666; user-select:none;">257</span>      if result.get(&quot;bounding_box&quot;):
<span style="color:#666; user-select:none;">258</span>          bb = result[&quot;bounding_box&quot;]
<span style="color:#666; user-select:none;">259</span>          lines.append(&quot;## Bounding Box&quot;)
<span style="color:#666; user-select:none;">260</span>          lines.append(f&quot;- Size: {bb.get(&#x27;width&#x27;, 0)} × {bb.get(&#x27;height&#x27;, 0)} × {bb.get(&#x27;depth&#x27;, 0)}&quot;)
<span style="color:#666; user-select:none;">261</span>  
<span style="color:#666; user-select:none;">262</span>      return &quot;\n&quot;.join(lines)
<span style="color:#666; user-select:none;">263</span>  
<span style="color:#666; user-select:none;">264</span>  
<span style="color:#666; user-select:none;">265</span>  def format_inspect_materials(result):
<span style="color:#666; user-select:none;">266</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">267</span>      Format inspect_materials result as markdown.
<span style="color:#666; user-select:none;">268</span>  
<span style="color:#666; user-select:none;">269</span>      Args:
<span style="color:#666; user-select:none;">270</span>          result: dict from inspect_materials()
<span style="color:#666; user-select:none;">271</span>  
<span style="color:#666; user-select:none;">272</span>      Returns:
<span style="color:#666; user-select:none;">273</span>          Markdown string
<span style="color:#666; user-select:none;">274</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">275</span>      lines = []
<span style="color:#666; user-select:none;">276</span>      lines.append(f&quot;# Materials ({result.get(&#x27;count&#x27;, 0)})&quot;)
<span style="color:#666; user-select:none;">277</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">278</span>      lines.append(f&quot;**{result.get(&#x27;summary&#x27;, &#x27;No materials&#x27;)}**&quot;)
<span style="color:#666; user-select:none;">279</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">280</span>  
<span style="color:#666; user-select:none;">281</span>      for mat in result.get(&quot;materials&quot;, []):
<span style="color:#666; user-select:none;">282</span>          lines.append(f&quot;## {mat[&#x27;name&#x27;]}&quot;)
<span style="color:#666; user-select:none;">283</span>          lines.append(f&quot;- Type: {mat.get(&#x27;type&#x27;, &#x27;Unknown&#x27;)}&quot;)
<span style="color:#666; user-select:none;">284</span>  
<span style="color:#666; user-select:none;">285</span>          if mat.get(&quot;color&quot;):
<span style="color:#666; user-select:none;">286</span>              c = mat[&quot;color&quot;]
<span style="color:#666; user-select:none;">287</span>              lines.append(f&quot;- Color: {c.get(&#x27;name&#x27;, &#x27;Unknown&#x27;)}&quot;)
<span style="color:#666; user-select:none;">288</span>  
<span style="color:#666; user-select:none;">289</span>          if mat.get(&quot;has_transparency&quot;):
<span style="color:#666; user-select:none;">290</span>              lines.append(&quot;- Has transparency&quot;)
<span style="color:#666; user-select:none;">291</span>          if mat.get(&quot;has_luminance&quot;):
<span style="color:#666; user-select:none;">292</span>              lines.append(&quot;- Has luminance/glow&quot;)
<span style="color:#666; user-select:none;">293</span>  
<span style="color:#666; user-select:none;">294</span>          if mat.get(&quot;used_by&quot;):
<span style="color:#666; user-select:none;">295</span>              lines.append(f&quot;- Used by: {&#x27;, &#x27;.join(mat[&#x27;used_by&#x27;])}&quot;)
<span style="color:#666; user-select:none;">296</span>          else:
<span style="color:#666; user-select:none;">297</span>              lines.append(&quot;- **Not used**&quot;)
<span style="color:#666; user-select:none;">298</span>  
<span style="color:#666; user-select:none;">299</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">300</span>  
<span style="color:#666; user-select:none;">301</span>      return &quot;\n&quot;.join(lines)
<span style="color:#666; user-select:none;">302</span>  
<span style="color:#666; user-select:none;">303</span>  
<span style="color:#666; user-select:none;">304</span>  def format_inspect_animation(result):
<span style="color:#666; user-select:none;">305</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">306</span>      Format inspect_animation result as markdown.
<span style="color:#666; user-select:none;">307</span>  
<span style="color:#666; user-select:none;">308</span>      Args:
<span style="color:#666; user-select:none;">309</span>          result: dict from inspect_animation()
<span style="color:#666; user-select:none;">310</span>  
<span style="color:#666; user-select:none;">311</span>      Returns:
<span style="color:#666; user-select:none;">312</span>          Markdown string
<span style="color:#666; user-select:none;">313</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">314</span>      lines = []
<span style="color:#666; user-select:none;">315</span>      fr = result.get(&quot;frame_range&quot;, {})
<span style="color:#666; user-select:none;">316</span>      lines.append(f&quot;# Animation: Frames {fr.get(&#x27;start&#x27;, 0)} - {fr.get(&#x27;end&#x27;, 0)}&quot;)
<span style="color:#666; user-select:none;">317</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">318</span>      lines.append(f&quot;**{result.get(&#x27;summary&#x27;, &#x27;No animation&#x27;)}**&quot;)
<span style="color:#666; user-select:none;">319</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">320</span>      lines.append(f&quot;- FPS: {result.get(&#x27;fps&#x27;, 30)}&quot;)
<span style="color:#666; user-select:none;">321</span>      lines.append(f&quot;- Duration: {result.get(&#x27;duration_seconds&#x27;, 0)}s&quot;)
<span style="color:#666; user-select:none;">322</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">323</span>  
<span style="color:#666; user-select:none;">324</span>      for obj in result.get(&quot;animated_objects&quot;, []):
<span style="color:#666; user-select:none;">325</span>          lines.append(f&quot;## {obj[&#x27;name&#x27;]} ({obj.get(&#x27;type&#x27;, &#x27;Unknown&#x27;)})&quot;)
<span style="color:#666; user-select:none;">326</span>          for track in obj.get(&quot;tracks&quot;, []):
<span style="color:#666; user-select:none;">327</span>              param = track.get(&quot;parameter&quot;, &quot;Unknown&quot;)
<span style="color:#666; user-select:none;">328</span>              keyframes = track.get(&quot;keyframes&quot;, [])
<span style="color:#666; user-select:none;">329</span>              if keyframes:
<span style="color:#666; user-select:none;">330</span>                  kf_summary = &quot;, &quot;.join([f&quot;f{kf[&#x27;frame&#x27;]}={kf[&#x27;value&#x27;]}&quot; for kf in keyframes[:5]])
<span style="color:#666; user-select:none;">331</span>                  if len(keyframes) &gt; 5:
<span style="color:#666; user-select:none;">332</span>                      kf_summary += f&quot; ... (+{len(keyframes) - 5} more)&quot;
<span style="color:#666; user-select:none;">333</span>                  lines.append(f&quot;- {param}: {kf_summary}&quot;)
<span style="color:#666; user-select:none;">334</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">335</span>  
<span style="color:#666; user-select:none;">336</span>      return &quot;\n&quot;.join(lines)
<span style="color:#666; user-select:none;">337</span>  
<span style="color:#666; user-select:none;">338</span>  
<span style="color:#666; user-select:none;">339</span>  def format_validate_scene(result):
<span style="color:#666; user-select:none;">340</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">341</span>      Format validate_scene result as markdown.
<span style="color:#666; user-select:none;">342</span>  
<span style="color:#666; user-select:none;">343</span>      Args:
<span style="color:#666; user-select:none;">344</span>          result: dict from validate_scene()
<span style="color:#666; user-select:none;">345</span>  
<span style="color:#666; user-select:none;">346</span>      Returns:
<span style="color:#666; user-select:none;">347</span>          Markdown string
<span style="color:#666; user-select:none;">348</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">349</span>      lines = []
<span style="color:#666; user-select:none;">350</span>      status = &quot;✅ PASSED&quot; if result.get(&quot;valid&quot;) else &quot;❌ FAILED&quot;
<span style="color:#666; user-select:none;">351</span>      lines.append(f&quot;# Scene Validation: {status}&quot;)
<span style="color:#666; user-select:none;">352</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">353</span>      lines.append(f&quot;**{result.get(&#x27;summary&#x27;, &#x27;No validation&#x27;)}**&quot;)
<span style="color:#666; user-select:none;">354</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">355</span>  
<span style="color:#666; user-select:none;">356</span>      if result.get(&quot;issues&quot;):
<span style="color:#666; user-select:none;">357</span>          lines.append(&quot;## Issues (must fix)&quot;)
<span style="color:#666; user-select:none;">358</span>          for issue in result[&quot;issues&quot;]:
<span style="color:#666; user-select:none;">359</span>              lines.append(f&quot;- ❌ {issue}&quot;)
<span style="color:#666; user-select:none;">360</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">361</span>  
<span style="color:#666; user-select:none;">362</span>      if result.get(&quot;warnings&quot;):
<span style="color:#666; user-select:none;">363</span>          lines.append(&quot;## Warnings&quot;)
<span style="color:#666; user-select:none;">364</span>          for warning in result[&quot;warnings&quot;]:
<span style="color:#666; user-select:none;">365</span>              lines.append(f&quot;- ⚠️ {warning}&quot;)
<span style="color:#666; user-select:none;">366</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">367</span>  
<span style="color:#666; user-select:none;">368</span>      if result.get(&quot;info&quot;):
<span style="color:#666; user-select:none;">369</span>          lines.append(&quot;## Info&quot;)
<span style="color:#666; user-select:none;">370</span>          for info in result[&quot;info&quot;]:
<span style="color:#666; user-select:none;">371</span>              lines.append(f&quot;- ℹ️ {info}&quot;)
<span style="color:#666; user-select:none;">372</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">373</span>  
<span style="color:#666; user-select:none;">374</span>      stats = result.get(&quot;stats&quot;, {})
<span style="color:#666; user-select:none;">375</span>      if stats:
<span style="color:#666; user-select:none;">376</span>          lines.append(&quot;## Stats&quot;)
<span style="color:#666; user-select:none;">377</span>          lines.append(f&quot;- Materials: {stats.get(&#x27;material_count&#x27;, 0)}&quot;)
<span style="color:#666; user-select:none;">378</span>          if stats.get(&quot;unused_materials&quot;):
<span style="color:#666; user-select:none;">379</span>              lines.append(f&quot;- Unused materials: {stats[&#x27;unused_materials&#x27;]}&quot;)
<span style="color:#666; user-select:none;">380</span>  
<span style="color:#666; user-select:none;">381</span>      return &quot;\n&quot;.join(lines)
<span style="color:#666; user-select:none;">382</span>  
<span style="color:#666; user-select:none;">383</span>  
<span style="color:#666; user-select:none;">384</span>  def format_describe_scene(result):
<span style="color:#666; user-select:none;">385</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">386</span>      Format describe_scene result as markdown.
<span style="color:#666; user-select:none;">387</span>  
<span style="color:#666; user-select:none;">388</span>      Universal formatter for the consolidated introspection tool.
<span style="color:#666; user-select:none;">389</span>  
<span style="color:#666; user-select:none;">390</span>      Args:
<span style="color:#666; user-select:none;">391</span>          result: dict from describe_scene()
<span style="color:#666; user-select:none;">392</span>  
<span style="color:#666; user-select:none;">393</span>      Returns:
<span style="color:#666; user-select:none;">394</span>          Markdown string
<span style="color:#666; user-select:none;">395</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">396</span>      lines = []
<span style="color:#666; user-select:none;">397</span>  
<span style="color:#666; user-select:none;">398</span>      # Header with scene name and frame info
<span style="color:#666; user-select:none;">399</span>      doc_name = result.get(&quot;document_name&quot;, &quot;Untitled&quot;)
<span style="color:#666; user-select:none;">400</span>      frame = result.get(&quot;frame&quot;, {})
<span style="color:#666; user-select:none;">401</span>      lines.append(f&quot;# Scene: {doc_name}&quot;)
<span style="color:#666; user-select:none;">402</span>      lines.append(f&quot;Frame {frame.get(&#x27;current&#x27;, 0)}/{frame.get(&#x27;end&#x27;, 0)} @ {frame.get(&#x27;fps&#x27;, 30)}fps&quot;)
<span style="color:#666; user-select:none;">403</span>      lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">404</span>  
<span style="color:#666; user-select:none;">405</span>      # Changes section (most important - at top)
<span style="color:#666; user-select:none;">406</span>      changes = result.get(&quot;changes&quot;)
<span style="color:#666; user-select:none;">407</span>      if changes:
<span style="color:#666; user-select:none;">408</span>          lines.append(&quot;## Changes Detected&quot;)
<span style="color:#666; user-select:none;">409</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">410</span>          _format_changes(changes, lines)
<span style="color:#666; user-select:none;">411</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">412</span>      elif result.get(&quot;changes&quot;) is None:
<span style="color:#666; user-select:none;">413</span>          lines.append(&quot;*First inspection - baseline captured for change detection*&quot;)
<span style="color:#666; user-select:none;">414</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">415</span>  
<span style="color:#666; user-select:none;">416</span>      # Hierarchy
<span style="color:#666; user-select:none;">417</span>      hierarchy = result.get(&quot;hierarchy&quot;, {})
<span style="color:#666; user-select:none;">418</span>      if hierarchy.get(&quot;objects&quot;):
<span style="color:#666; user-select:none;">419</span>          lines.append(&quot;## Hierarchy&quot;)
<span style="color:#666; user-select:none;">420</span>          lines.append(f&quot;*{hierarchy.get(&#x27;summary&#x27;, &#x27;&#x27;)}*&quot;)
<span style="color:#666; user-select:none;">421</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">422</span>          for obj in hierarchy[&quot;objects&quot;]:
<span style="color:#666; user-select:none;">423</span>              _format_object_compact(obj, lines, indent=0)
<span style="color:#666; user-select:none;">424</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">425</span>  
<span style="color:#666; user-select:none;">426</span>      # Materials (compact)
<span style="color:#666; user-select:none;">427</span>      materials = result.get(&quot;materials&quot;, {})
<span style="color:#666; user-select:none;">428</span>      mat_list = materials.get(&quot;materials&quot;, [])
<span style="color:#666; user-select:none;">429</span>      if mat_list:
<span style="color:#666; user-select:none;">430</span>          lines.append(f&quot;## Materials ({len(mat_list)})&quot;)
<span style="color:#666; user-select:none;">431</span>          for mat in mat_list:
<span style="color:#666; user-select:none;">432</span>              mat_line = f&quot;- **{mat[&#x27;name&#x27;]}**&quot;
<span style="color:#666; user-select:none;">433</span>              if mat.get(&quot;color&quot;):
<span style="color:#666; user-select:none;">434</span>                  mat_line += f&quot; [{mat[&#x27;color&#x27;].get(&#x27;name&#x27;, &#x27;&#x27;)}]&quot;
<span style="color:#666; user-select:none;">435</span>              if mat.get(&quot;used_by&quot;):
<span style="color:#666; user-select:none;">436</span>                  mat_line += f&quot; → {&#x27;, &#x27;.join(mat[&#x27;used_by&#x27;])}&quot;
<span style="color:#666; user-select:none;">437</span>              else:
<span style="color:#666; user-select:none;">438</span>                  mat_line += &quot; (unused)&quot;
<span style="color:#666; user-select:none;">439</span>              lines.append(mat_line)
<span style="color:#666; user-select:none;">440</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">441</span>  
<span style="color:#666; user-select:none;">442</span>      # Animation (compact)
<span style="color:#666; user-select:none;">443</span>      animation = result.get(&quot;animation&quot;, {})
<span style="color:#666; user-select:none;">444</span>      animated = animation.get(&quot;animated_objects&quot;, [])
<span style="color:#666; user-select:none;">445</span>      if animated:
<span style="color:#666; user-select:none;">446</span>          lines.append(f&quot;## Animation&quot;)
<span style="color:#666; user-select:none;">447</span>          lines.append(f&quot;*{animation.get(&#x27;summary&#x27;, &#x27;&#x27;)}*&quot;)
<span style="color:#666; user-select:none;">448</span>          for obj in animated:
<span style="color:#666; user-select:none;">449</span>              tracks_summary = &quot;, &quot;.join([t.get(&quot;parameter&quot;, &quot;?&quot;) for t in obj.get(&quot;tracks&quot;, [])])
<span style="color:#666; user-select:none;">450</span>              lines.append(f&quot;- {obj[&#x27;name&#x27;]}: {tracks_summary}&quot;)
<span style="color:#666; user-select:none;">451</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">452</span>  
<span style="color:#666; user-select:none;">453</span>      # Validation warnings (compact)
<span style="color:#666; user-select:none;">454</span>      validation = result.get(&quot;validation&quot;, {})
<span style="color:#666; user-select:none;">455</span>      warnings = validation.get(&quot;warnings&quot;, [])
<span style="color:#666; user-select:none;">456</span>      issues = validation.get(&quot;issues&quot;, [])
<span style="color:#666; user-select:none;">457</span>      if warnings or issues:
<span style="color:#666; user-select:none;">458</span>          lines.append(&quot;## Validation&quot;)
<span style="color:#666; user-select:none;">459</span>          for issue in issues:
<span style="color:#666; user-select:none;">460</span>              lines.append(f&quot;- ❌ {issue}&quot;)
<span style="color:#666; user-select:none;">461</span>          for warning in warnings:
<span style="color:#666; user-select:none;">462</span>              lines.append(f&quot;- ⚠️ {warning}&quot;)
<span style="color:#666; user-select:none;">463</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">464</span>  
<span style="color:#666; user-select:none;">465</span>      # Console output delta (only if there are new messages)
<span style="color:#666; user-select:none;">466</span>      console = result.get(&quot;console&quot;, {})
<span style="color:#666; user-select:none;">467</span>      if console.get(&quot;has_new&quot;):
<span style="color:#666; user-select:none;">468</span>          lines.append(&quot;## Console Output&quot;)
<span style="color:#666; user-select:none;">469</span>          if console.get(&quot;truncated&quot;):
<span style="color:#666; user-select:none;">470</span>              lines.append(&quot;*Output truncated for safety*&quot;)
<span style="color:#666; user-select:none;">471</span>          for msg in console.get(&quot;messages&quot;, []):
<span style="color:#666; user-select:none;">472</span>              text = msg.get(&quot;text&quot;, &quot;&quot;)
<span style="color:#666; user-select:none;">473</span>              count = msg.get(&quot;count&quot;, 1)
<span style="color:#666; user-select:none;">474</span>              # Format with repetition count if repeated
<span style="color:#666; user-select:none;">475</span>              if count &gt; 1:
<span style="color:#666; user-select:none;">476</span>                  lines.append(f&quot;- `{text}` (×{count})&quot;)
<span style="color:#666; user-select:none;">477</span>              else:
<span style="color:#666; user-select:none;">478</span>                  lines.append(f&quot;- `{text}`&quot;)
<span style="color:#666; user-select:none;">479</span>          lines.append(&quot;&quot;)
<span style="color:#666; user-select:none;">480</span>  
<span style="color:#666; user-select:none;">481</span>      return &quot;\n&quot;.join(lines)
<span style="color:#666; user-select:none;">482</span>  
<span style="color:#666; user-select:none;">483</span>  
<span style="color:#666; user-select:none;">484</span>  def _format_changes(changes_result, lines):
<span style="color:#666; user-select:none;">485</span>      &quot;&quot;&quot;Format change detection results.
<span style="color:#666; user-select:none;">486</span>  
<span style="color:#666; user-select:none;">487</span>      Handles both DreamTalk param changes and native C4D param changes.
<span style="color:#666; user-select:none;">488</span>      Native changes include the DescID for direct use in code.
<span style="color:#666; user-select:none;">489</span>      &quot;&quot;&quot;
<span style="color:#666; user-select:none;">490</span>      changes = changes_result.get(&quot;changes&quot;, {})
<span style="color:#666; user-select:none;">491</span>  
<span style="color:#666; user-select:none;">492</span>      # Object changes
<span style="color:#666; user-select:none;">493</span>      obj_changes = changes.get(&quot;objects&quot;, {})
<span style="color:#666; user-select:none;">494</span>  
<span style="color:#666; user-select:none;">495</span>      # DreamTalk param changes (userdata, transforms, etc.)
<span style="color:#666; user-select:none;">496</span>      if obj_changes.get(&quot;dreamtalk_modified&quot;):
<span style="color:#666; user-select:none;">497</span>          for obj_name, params in obj_changes[&quot;dreamtalk_modified&quot;].items():
<span style="color:#666; user-select:none;">498</span>              for param, vals in params.items():
<span style="color:#666; user-select:none;">499</span>                  old_val = vals.get(&#x27;old&#x27;)
<span style="color:#666; user-select:none;">500</span>                  new_val = vals.get(&#x27;new&#x27;)
<span style="color:#666; user-select:none;">501</span>                  # Format values nicely
<span style="color:#666; user-select:none;">502</span>                  if isinstance(old_val, float):
<span style="color:#666; user-select:none;">503</span>                      old_val = round(old_val, 3)
<span style="color:#666; user-select:none;">504</span>                  if isinstance(new_val, float):
<span style="color:#666; user-select:none;">505</span>                      new_val = round(new_val, 3)
<span style="color:#666; user-select:none;">506</span>                  lines.append(f&quot;- **{obj_name}**.{param}: `{old_val}` → `{new_val}`&quot;)
<span style="color:#666; user-select:none;">507</span>  
<span style="color:#666; user-select:none;">508</span>      # Native C4D param changes (only shown when changed)
<span style="color:#666; user-select:none;">509</span>      if obj_changes.get(&quot;native_modified&quot;):
<span style="color:#666; user-select:none;">510</span>          for obj_name, params in obj_changes[&quot;native_modified&quot;].items():
<span style="color:#666; user-select:none;">511</span>              for desc_id_str, vals in params.items():
<span style="color:#666; user-select:none;">512</span>                  old_val = vals.get(&#x27;old&#x27;)
<span style="color:#666; user-select:none;">513</span>                  new_val = vals.get(&#x27;new&#x27;)
<span style="color:#666; user-select:none;">514</span>                  param_name = vals.get(&#x27;name&#x27;, &#x27;&#x27;)
<span style="color:#666; user-select:none;">515</span>                  ident = vals.get(&#x27;ident&#x27;, &#x27;&#x27;)
<span style="color:#666; user-select:none;">516</span>                  old_label = vals.get(&#x27;old_label&#x27;)
<span style="color:#666; user-select:none;">517</span>                  new_label = vals.get(&#x27;new_label&#x27;)
<span style="color:#666; user-select:none;">518</span>  
<span style="color:#666; user-select:none;">519</span>                  # Format values nicely
<span style="color:#666; user-select:none;">520</span>                  if isinstance(old_val, float):
<span style="color:#666; user-select:none;">521</span>                      old_val = round(old_val, 3)
<span style="color:#666; user-select:none;">522</span>                  if isinstance(new_val, float):
<span style="color:#666; user-select:none;">523</span>                      new_val = round(new_val, 3)
<span style="color:#666; user-select:none;">524</span>  
<span style="color:#666; user-select:none;">525</span>                  # Build the change description
<span style="color:#666; user-select:none;">526</span>                  # If we have labels (dropdown/enum), show &quot;Label (value)&quot;
<span style="color:#666; user-select:none;">527</span>                  if old_label and new_label:
<span style="color:#666; user-select:none;">528</span>                      old_display = f&quot;{old_label} ({old_val})&quot;
<span style="color:#666; user-select:none;">529</span>                      new_display = f&quot;{new_label} ({new_val})&quot;
<span style="color:#666; user-select:none;">530</span>                  else:
<span style="color:#666; user-select:none;">531</span>                      old_display = str(old_val)
<span style="color:#666; user-select:none;">532</span>                      new_display = str(new_val)
<span style="color:#666; user-select:none;">533</span>  
<span style="color:#666; user-select:none;">534</span>                  # Build parameter identifier: &quot;Name&quot; or &quot;Name (ID_CONSTANT)&quot;
<span style="color:#666; user-select:none;">535</span>                  if param_name and ident:
<span style="color:#666; user-select:none;">536</span>                      param_display = f&quot;{param_name} ({ident})&quot;
<span style="color:#666; user-select:none;">537</span>                  elif param_name:
<span style="color:#666; user-select:none;">538</span>                      param_display = param_name
<span style="color:#666; user-select:none;">539</span>                  elif ident:
<span style="color:#666; user-select:none;">540</span>                      param_display = ident
<span style="color:#666; user-select:none;">541</span>                  else:
<span style="color:#666; user-select:none;">542</span>                      param_display = f&quot;DescID {desc_id_str}&quot;
<span style="color:#666; user-select:none;">543</span>  
<span style="color:#666; user-select:none;">544</span>                  lines.append(f&quot;- **{obj_name}**.{param_display}: `{old_display}` → `{new_display}`&quot;)
<span style="color:#666; user-select:none;">545</span>  
<span style="color:#666; user-select:none;">546</span>      # Tag param changes
<span style="color:#666; user-select:none;">547</span>      if obj_changes.get(&quot;tags_modified&quot;):
<span style="color:#666; user-select:none;">548</span>          for obj_name, params in obj_changes[&quot;tags_modified&quot;].items():
<span style="color:#666; user-select:none;">549</span>              for param_key, vals in params.items():
<span style="color:#666; user-select:none;">550</span>                  old_val = vals.get(&#x27;old&#x27;)
<span style="color:#666; user-select:none;">551</span>                  new_val = vals.get(&#x27;new&#x27;)
<span style="color:#666; user-select:none;">552</span>                  tag_name = vals.get(&#x27;tag&#x27;, &#x27;&#x27;)
<span style="color:#666; user-select:none;">553</span>                  param_name = vals.get(&#x27;name&#x27;, &#x27;&#x27;)
<span style="color:#666; user-select:none;">554</span>                  ident = vals.get(&#x27;ident&#x27;, &#x27;&#x27;)
<span style="color:#666; user-select:none;">555</span>                  old_label = vals.get(&#x27;old_label&#x27;)
<span style="color:#666; user-select:none;">556</span>                  new_label = vals.get(&#x27;new_label&#x27;)
<span style="color:#666; user-select:none;">557</span>  
<span style="color:#666; user-select:none;">558</span>                  if isinstance(old_val, float):
<span style="color:#666; user-select:none;">559</span>                      old_val = round(old_val, 3)
<span style="color:#666; user-select:none;">560</span>                  if isinstance(new_val, float):
<span style="color:#666; user-select:none;">561</span>                      new_val = round(new_val, 3)
<span style="color:#666; user-select:none;">562</span>  
<span style="color:#666; user-select:none;">563</span>                  if old_label and new_label:
<span style="color:#666; user-select:none;">564</span>                      old_display = f&quot;{old_label} ({old_val})&quot;
<span style="color:#666; user-select:none;">565</span>                      new_display = f&quot;{new_label} ({new_val})&quot;
<span style="color:#666; user-select:none;">566</span>                  else:
<span style="color:#666; user-select:none;">567</span>                      old_display = str(old_val)
<span style="color:#666; user-select:none;">568</span>                      new_display = str(new_val)
<span style="color:#666; user-select:none;">569</span>  
<span style="color:#666; user-select:none;">570</span>                  if param_name and ident:
<span style="color:#666; user-select:none;">571</span>                      param_display = f&quot;{param_name} ({ident})&quot;
<span style="color:#666; user-select:none;">572</span>                  elif param_name:
<span style="color:#666; user-select:none;">573</span>                      param_display = param_name
<span style="color:#666; user-select:none;">574</span>                  elif ident:
<span style="color:#666; user-select:none;">575</span>                      param_display = ident
<span style="color:#666; user-select:none;">576</span>                  else:
<span style="color:#666; user-select:none;">577</span>                      param_display = param_key
<span style="color:#666; user-select:none;">578</span>  
<span style="color:#666; user-select:none;">579</span>                  lines.append(f&quot;- **{obj_name}** tag **{tag_name}**.{param_display}: `{old_display}` → `{new_display}`&quot;)
<span style="color:#666; user-select:none;">580</span>  
<span style="color:#666; user-select:none;">581</span>      # Added/removed objects
<span style="color:#666; user-select:none;">582</span>      if obj_changes.get(&quot;added&quot;):
<span style="color:#666; user-select:none;">583</span>          for name in obj_changes[&quot;added&quot;]:
<span style="color:#666; user-select:none;">584</span>              lines.append(f&quot;- **+ Added**: {name}&quot;)
<span style="color:#666; user-select:none;">585</span>  
<span style="color:#666; user-select:none;">586</span>      if obj_changes.get(&quot;removed&quot;):
<span style="color:#666; user-select:none;">587</span>          for name in obj_changes[&quot;removed&quot;]:
<span style="color:#666; user-select:none;">588</span>              lines.append(f&quot;- **- Removed**: {name}&quot;)
<span style="color:#666; user-select:none;">589</span>  
<span style="color:#666; user-select:none;">590</span>      # Material changes
<span style="color:#666; user-select:none;">591</span>      mat_changes = changes.get(&quot;materials&quot;, {})
<span style="color:#666; user-select:none;">592</span>      if mat_changes.get(&quot;modified&quot;):
<span style="color:#666; user-select:none;">593</span>          for mat_name, params in mat_changes[&quot;modified&quot;].items():
<span style="color:#666; user-select:none;">594</span>              for param, vals in params.items():
<span style="color:#666; user-select:none;">595</span>                  lines.append(f&quot;- Material **{mat_name}**.{param}: `{vals.get(&#x27;old&#x27;)}` → `{vals.get(&#x27;new&#x27;)}`&quot;)
<span style="color:#666; user-select:none;">596</span>  
<span style="color:#666; user-select:none;">597</span>      if mat_changes.get(&quot;added&quot;):
<span style="color:#666; user-select:none;">598</span>          for name in mat_changes[&quot;added&quot;]:
<span style="color:#666; user-select:none;">599</span>              lines.append(f&quot;- **+ Added Material**: {name}&quot;)
<span style="color:#666; user-select:none;">600</span>  
<span style="color:#666; user-select:none;">601</span>      if mat_changes.get(&quot;removed&quot;):
<span style="color:#666; user-select:none;">602</span>          for name in mat_changes[&quot;removed&quot;]:
<span style="color:#666; user-select:none;">603</span>              lines.append(f&quot;- **- Removed Material**: {name}&quot;)
<span style="color:#666; user-select:none;">604</span>  
<span style="color:#666; user-select:none;">605</span>  
<span style="color:#666; user-select:none;">606</span>  def _format_object_compact(obj, lines, indent=0):
<span style="color:#666; user-select:none;">607</span>      &quot;&quot;&quot;Format a single object compactly with its key parameters.&quot;&quot;&quot;
<span style="color:#666; user-select:none;">608</span>      prefix = &quot;  &quot; * indent
<span style="color:#666; user-select:none;">609</span>  
<span style="color:#666; user-select:none;">610</span>      # Build object line
<span style="color:#666; user-select:none;">611</span>      name = obj.get(&quot;name&quot;, &quot;Unknown&quot;)
<span style="color:#666; user-select:none;">612</span>      obj_type = obj.get(&quot;type&quot;, &quot;Unknown&quot;)
<span style="color:#666; user-select:none;">613</span>  
<span style="color:#666; user-select:none;">614</span>      parts = [f&quot;{name} ({obj_type})&quot;]
<span style="color:#666; user-select:none;">615</span>  
<span style="color:#666; user-select:none;">616</span>      # Position (only if non-zero)
<span style="color:#666; user-select:none;">617</span>      pos = obj.get(&quot;position&quot;, {})
<span style="color:#666; user-select:none;">618</span>      if pos.get(&quot;x&quot;, 0) != 0 or pos.get(&quot;y&quot;, 0) != 0 or pos.get(&quot;z&quot;, 0) != 0:
<span style="color:#666; user-select:none;">619</span>          parts.append(f&quot;@ ({pos.get(&#x27;x&#x27;, 0)}, {pos.get(&#x27;y&#x27;, 0)}, {pos.get(&#x27;z&#x27;, 0)})&quot;)
<span style="color:#666; user-select:none;">620</span>  
<span style="color:#666; user-select:none;">621</span>      # Key DreamTalk params
<span style="color:#666; user-select:none;">622</span>      if obj.get(&quot;creation&quot;) is not None:
<span style="color:#666; user-select:none;">623</span>          parts.append(f&quot;creation:{obj[&#x27;creation&#x27;]}%&quot;)
<span style="color:#666; user-select:none;">624</span>      if obj.get(&quot;draw&quot;) is not None and obj.get(&quot;draw&quot;) != 100:
<span style="color:#666; user-select:none;">625</span>          parts.append(f&quot;draw:{obj[&#x27;draw&#x27;]}%&quot;)
<span style="color:#666; user-select:none;">626</span>      if obj.get(&quot;color&quot;):
<span style="color:#666; user-select:none;">627</span>          parts.append(f&quot;[{obj[&#x27;color&#x27;]}]&quot;)
<span style="color:#666; user-select:none;">628</span>  
<span style="color:#666; user-select:none;">629</span>      lines.append(f&quot;{prefix}- {&#x27; &#x27;.join(parts)}&quot;)
<span style="color:#666; user-select:none;">630</span>  
<span style="color:#666; user-select:none;">631</span>      # Recurse children
<span style="color:#666; user-select:none;">632</span>      for child in obj.get(&quot;children&quot;, []):
<span style="color:#666; user-select:none;">633</span>          _format_object_compact(child, lines, indent + 1)
</pre>
        </div>
        </div>

</div>
</main>
</div>
</div>


</body>
</html>

