{
  "$schema": "https://pi-tui.ratstack.sh/schema/pattern.schema.json",
  "aka": [
    "Section composition"
  ],
  "animated": false,
  "apis": [
    {
      "name": "Component",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "name": "truncateToWidth",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    }
  ],
  "applicability": [
    "Use this when several sections share formatting without sharing interaction state."
  ],
  "category": "layout",
  "categoryLabel": "Layout",
  "consequences": [
    "Sections share formatting without one large renderer.",
    "The composer still owns the combined row and column budget."
  ],
  "family": "structural",
  "familyLabel": "Structural",
  "id": "section-loom",
  "implementation": [
    "Pure formatting does not mount or refresh a view.",
    "Budget width and height where sections are combined."
  ],
  "intent": "Compose a dashboard from focused render helpers under one layout budget.",
  "jsonUrl": "https://pi-tui.ratstack.sh/patterns/section-loom.json",
  "kind": "component",
  "knownUses": [
    {
      "example": "display-formatting-helpers",
      "moreRefs": 0,
      "refs": [
        {
          "file": "coordinate/render-utils.ts",
          "lines": "1-65",
          "sha": "7f32f5d9",
          "url": "https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/render-utils.ts#L1-L65"
        }
      ],
      "repo": "pi-coordination",
      "repoUrl": "https://github.com/nicobailon/pi-coordination",
      "title": "Centralize status and section formatting"
    },
    {
      "example": "display-formatting-helpers",
      "moreRefs": 0,
      "refs": [
        {
          "file": "overlay-render.ts",
          "lines": "1-55",
          "sha": "09937ed6",
          "url": "https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay-render.ts#L1-L55"
        }
      ],
      "repo": "pi-messenger",
      "repoUrl": "https://github.com/nicobailon/pi-messenger",
      "title": "Centralize status and section formatting"
    },
    {
      "example": "messenger-overlay-render",
      "moreRefs": 0,
      "refs": [
        {
          "file": "overlay.ts",
          "lines": "576-635",
          "sha": "09937ed6",
          "url": "https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay.ts#L576-L635"
        }
      ],
      "repo": "pi-messenger",
      "repoUrl": "https://github.com/nicobailon/pi-messenger",
      "title": "Messenger overlay composed by render helpers"
    }
  ],
  "linkedFrom": [],
  "links": {
    "agentGuide": "https://pi-tui.ratstack.sh/llms.txt",
    "index": "https://pi-tui.ratstack.sh/patterns.md",
    "skill": "https://pi-tui.ratstack.sh/skills/build-pi-tui-component.md"
  },
  "mdUrl": "https://pi-tui.ratstack.sh/patterns/section-loom.md",
  "motivation": "Messenger's activity overlay combines status, workers, tasks, feed and detail from separate render helpers.",
  "name": "Section Loom",
  "participants": [
    {
      "name": "Component",
      "role": "Renders width-bounded lines and invalidates cached output.",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "name": "truncateToWidth",
      "role": "Clips text to its allotted columns and can pad the result.",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "name": "Section helpers",
      "role": "Return focused sections to one budget-owning composer.",
      "url": null
    }
  ],
  "pi": "1.0.3",
  "related": [
    {
      "how": "chooses the arrangement",
      "id": "hinge-panel",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/hinge-panel.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/hinge-panel.md",
      "name": "Hinge Panel",
      "url": "https://pi-tui.ratstack.sh/patterns/hinge-panel/"
    }
  ],
  "sampleCode": {
    "code": "// Section Loom: combine focused section renderers under one layout budget.\nimport { truncateToWidth, type Component } from \"@earendil-works/pi-tui\";\nimport type { PatternStory } from \"../../../src/pattern.ts\";\n\nexport const story: PatternStory = {\n  id: \"section-loom\", title: \"Section Loom\", kind: \"component\",\n  apis: [\"Component\", \"truncateToWidth\"], rows: 14,\n  states: [\n    { id: \"default\", label: \"Sections\" },\n    { id: \"compact\", label: \"Compact sections\", steps: [{ type: \"action\", name: \"compact\" }] },\n  ],\n  setup({ theme, action, tui }) {\n    const snapshot = {\n      status: \"Preview ready · 2 checks passed\",\n      tasks: [\"✓ Measure columns\", \"> Review light theme\", \"· Publish sample\"],\n      feed: [\"14:02 Width check passed\", \"14:03 Preview rendered\", \"14:04 Review requested\"],\n    };\n    let compact = false;\n    action(\"compact\", () => { compact = true; tui.requestRender(); });\n    const section = (title: string, rows: readonly string[], width: number) => [\n      theme.fg(\"accent\", truncateToWidth(title, width)),\n      ...rows.map(row => theme.fg(\"text\", truncateToWidth(\"  \" + row, width))),\n    ];\n    const renderStatus = (width: number) => section(\"STATUS\", [snapshot.status], width);\n    const renderTasks = (width: number) => section(\"TASKS\", snapshot.tasks, width);\n    const renderFeed = (width: number) => section(\"FEED\", compact\n      ? [snapshot.feed.at(-1)! + \" · 2 earlier\"]\n      : snapshot.feed, width);\n    const component: Component = {\n      invalidate() {},\n      render(width) {\n        const sections = [renderStatus(width), renderTasks(width), renderFeed(width)];\n        const availableRows = tui.terminal.rows - 2;\n        const body = sections.flatMap((lines, i) => i === 0 ? lines : [\"\", ...lines]);\n        return [\n          theme.fg(\"accent\", \"Section Loom · \" + (compact ? \"compact\" : \"dashboard\")),\n          ...body.slice(0, availableRows),\n          theme.fg(\"muted\", truncateToWidth(\"One snapshot · section helpers do not mount UI\", width)),\n        ];\n      },\n    };\n    return component;\n  },\n};\n",
    "lang": "ts",
    "path": "stories/patterns/structural/section-loom.ts"
  },
  "states": [
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Section Loom, Sections state, 40 columns, dark theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-40-dark.4cd603c148d8.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Section Loom, Sections state, 40 columns, light theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-40-light.03bfff32abe4.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Section Loom, Sections state, 60 columns, dark theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-60-dark.958cb6383f36.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Section Loom, Sections state, 60 columns, light theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-60-light.fd461e3994e0.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Section Loom, Sections state, 80 columns, dark theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-80-dark.9aa19b11d7a7.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Section Loom, Sections state, 80 columns, light theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-80-light.1bbb704f7529.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Section Loom, Sections state, 120 columns, dark theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-120-dark.018f96769de7.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Section Loom, Sections state, 120 columns, light theme, 14 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 616,
          "rows": 14,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/default-120-light.b83ed3293878.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Combine status, tasks and a feed with consistent labels.",
      "id": "default",
      "label": "Sections",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Section Loom, Compact sections state, 40 columns, dark theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-40-dark.65396cc301e6.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Section Loom, Compact sections state, 40 columns, light theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-40-light.4ccf74445212.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Section Loom, Compact sections state, 60 columns, dark theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-60-dark.af4b1589e9a2.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Section Loom, Compact sections state, 60 columns, light theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-60-light.adebe8f2a31b.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Section Loom, Compact sections state, 80 columns, dark theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-80-dark.28713df92cd8.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Section Loom, Compact sections state, 80 columns, light theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-80-light.1b7eaaaed894.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Section Loom, Compact sections state, 120 columns, dark theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-120-dark.c50398f707d3.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Section Loom, Compact sections state, 120 columns, light theme, 12 rows",
          "animated": false,
          "animation": null,
          "checks": [
            {
              "evidence": [],
              "name": "width",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "style-leak",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "hard-coded-colour",
              "status": "pass",
              "verdict": "pass"
            },
            {
              "evidence": [],
              "name": "height",
              "status": "pass",
              "verdict": "pass"
            }
          ],
          "h": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/section-loom/compact-120-light.3a2b09daf974.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Collapse secondary sections while keeping the same underlying snapshot.",
      "id": "compact",
      "label": "Compact sections",
      "problems": []
    }
  ],
  "structure": "snapshot -> section helpers\nhelpers -> budget -> lines",
  "thumbnails": {
    "dark": {
      "animation": null,
      "src": "https://pi-tui.ratstack.sh/thumbs/section-loom/dark.40b5d06fb5f0.webp"
    },
    "light": {
      "animation": null,
      "src": "https://pi-tui.ratstack.sh/thumbs/section-loom/light.8cd6c79938c1.webp"
    }
  },
  "url": "https://pi-tui.ratstack.sh/patterns/section-loom/",
  "widths": [
    40,
    60,
    80,
    120
  ]
}
