{
  "$schema": "https://pi-tui.ratstack.sh/schema/pattern.schema.json",
  "aka": [
    "Coalesced redraw"
  ],
  "animated": true,
  "apis": [
    {
      "name": "TUI.requestRender",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "name": "Component",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "name": "ctx.ui.setWidget",
      "url": null
    }
  ],
  "applicability": [
    "Use this when many output events do not need separate scheduled redraws."
  ],
  "category": "rendering",
  "categoryLabel": "Rendering and performance",
  "consequences": [
    "Several events can share one scheduled redraw request.",
    "Pending-timer cancellation and flushing need explicit semantics."
  ],
  "family": "presentation",
  "familyLabel": "Presentation",
  "id": "render-funnel",
  "implementation": [
    "Cancel the pending timer on disposal.",
    "Pi also coalesces requestRender calls internally."
  ],
  "intent": "Collapse bursty redraw scheduling behind one pending timer.",
  "jsonUrl": "https://pi-tui.ratstack.sh/patterns/render-funnel.json",
  "kind": "screen",
  "knownUses": [
    {
      "example": "coalesced-redraws",
      "moreRefs": 0,
      "refs": [
        {
          "file": "overlay-component.ts",
          "lines": "263-273",
          "sha": "77df9a81",
          "url": "https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L263-L273"
        },
        {
          "file": "overlay-component.ts",
          "lines": "1098-1125",
          "sha": "77df9a81",
          "url": "https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/overlay-component.ts#L1098-L1125"
        }
      ],
      "repo": "pi-interactive-shell",
      "repoUrl": "https://github.com/nicobailon/pi-interactive-shell",
      "title": "Coalesce bursty redraw requests"
    },
    {
      "example": "coalesced-redraws",
      "moreRefs": 0,
      "refs": [
        {
          "file": "render-scheduler.ts",
          "lines": "1-28",
          "sha": "859dee67",
          "url": "https://github.com/nicobailon/pi-powerline-footer/blob/859dee671b633fb533b07ceba3e6c1ab1c43360a/render-scheduler.ts#L1-L28"
        }
      ],
      "repo": "pi-powerline-footer",
      "repoUrl": "https://github.com/nicobailon/pi-powerline-footer",
      "title": "Coalesce bursty redraw requests"
    },
    {
      "example": "reattach-session-list",
      "moreRefs": 0,
      "refs": [
        {
          "file": "reattach-overlay.ts",
          "lines": "18-115",
          "sha": "77df9a81",
          "url": "https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L18-L115"
        },
        {
          "file": "reattach-overlay.ts",
          "lines": "311-448",
          "sha": "77df9a81",
          "url": "https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/reattach-overlay.ts#L311-L448"
        }
      ],
      "repo": "pi-interactive-shell",
      "repoUrl": "https://github.com/nicobailon/pi-interactive-shell",
      "title": "Reattach overlay supports bounded selection and deferred refresh"
    }
  ],
  "linkedFrom": [
    {
      "how": "schedules redraws for appended output",
      "id": "tail-anchor",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/tail-anchor.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/tail-anchor.md",
      "name": "Tail Anchor",
      "url": "https://pi-tui.ratstack.sh/patterns/tail-anchor/"
    }
  ],
  "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/render-funnel.md",
  "motivation": "Interactive Shell and Powerline Footer receive bursts of updates that share a pending redraw timer.",
  "name": "Render Funnel",
  "participants": [
    {
      "name": "TUI.requestRender",
      "role": "Requests a coalesced redraw after state changes.",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "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": "Pending timer",
      "role": "Combines a burst into one scheduled render request.",
      "url": null
    }
  ],
  "pi": "1.0.3",
  "related": [
    {
      "how": "owns the redraw trigger's lifetime",
      "id": "refresh-lease",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/refresh-lease.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/refresh-lease.md",
      "name": "Refresh Lease",
      "url": "https://pi-tui.ratstack.sh/patterns/refresh-lease/"
    }
  ],
  "sampleCode": {
    "code": "// Render Funnel: one pending timer combines a burst into one redraw request.\nimport { wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { PatternStory } from \"../../../src/pattern.ts\";\n\ntype Schedule =\n  | { kind: \"idle\" }\n  | { kind: \"pending\"; timer: ReturnType<typeof setTimeout> }\n  | { kind: \"closed\" };\n// Lifecycle: idle --event--> pending --flush--> idle.\n// More events stay pending; close cancels its timer and enters closed.\nexport const story: PatternStory = {\n  id: \"render-funnel\", title: \"Render Funnel\", kind: \"screen\",\n  apis: [\"Component\", \"TUI.requestRender\", \"ctx.ui.setWidget\"],\n  states: [\n    { id: \"burst\", label: \"Burst of updates\", animated: true, steps: [\n      { type: \"action\", name: \"burst\" }, { type: \"tick\", ms: 500 },\n    ] },\n    { id: \"settled\", label: \"Single scheduled redraw\", animated: true, steps: [\n      { type: \"tick\", ms: 200 }, { type: \"action\", name: \"assert-settled\" },\n    ] },\n    { id: \"closed\", label: \"Closed before timer\", animated: true, steps: [\n      { type: \"action\", name: \"close-pending\" }, { type: \"tick\", ms: 700 },\n      { type: \"action\", name: \"assert-closed\" },\n    ] },\n  ],\n  setup({ ui, theme, tui, action }) {\n    let schedule: Schedule = { kind: \"idle\" };\n    let events = 0, requests = 0, latest = \"Waiting for task output\";\n    const arrivals: ReturnType<typeof setTimeout>[] = [];\n    function receive(text: string) {\n      if (schedule.kind === \"closed\") return;\n      latest = text; events++;\n      if (schedule.kind === \"pending\") return;\n      schedule = { kind: \"pending\", timer: setTimeout(() => {\n        schedule = { kind: \"idle\" }; requests++; tui.requestRender();\n      }, 600) };\n    }\n    function close() {\n      if (schedule.kind === \"pending\") clearTimeout(schedule.timer);\n      schedule = { kind: \"closed\" };\n    }\n    ui.setEditorText(\"Keep the task draft\");\n    ui.setWidget(\"output\", () => ({\n      invalidate() {},\n      render(width) {\n        const lines = [\"Task output · \" + schedule.kind, latest,\n          events + \" events → \" + requests + \" scheduled requests\",\n          schedule.kind === \"closed\" ? \"Timer cancelled · draft stays intact\" :\n            schedule.kind === \"pending\" ? \"Burst shares one pending timer\" : \"Latest output flushed\"];\n        return lines.flatMap((line, index) => wrapTextWithAnsi(line, width)\n          .map(row => theme.fg(index === 0 ? \"accent\" : \"muted\", row)));\n      },\n    }));\n    action(\"burst\", () => {\n      receive(\"Parser task started\");\n      arrivals.push(setTimeout(() => receive(\"Parser task: 3 checks\"), 200));\n      arrivals.push(setTimeout(() => receive(\"Parser task: 6 checks passed\"), 400));\n    });\n    action(\"assert-settled\", () => {\n      if (events !== 3 || requests !== 1 || schedule.kind !== \"idle\") throw new Error(\"Burst did not coalesce\");\n    });\n    action(\"close-pending\", () => { receive(\"Review task queued\"); close(); tui.requestRender(); });\n    action(\"assert-closed\", () => {\n      if (requests !== 1 || schedule.kind !== \"closed\" || ui.getEditorText() !== \"Keep the task draft\")\n        throw new Error(\"Cancelled timer changed the host\");\n    });\n    return () => { close(); arrivals.forEach(clearTimeout); ui.setWidget(\"output\", undefined); };\n  },\n};\n",
    "lang": "ts",
    "path": "stories/patterns/presentation/render-funnel.ts"
  },
  "states": [
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Render Funnel, Burst of updates state, 40 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-40-dark.anim.4c4766cfe939.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-40-dark.2d6aa7ec8f22.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Render Funnel, Burst of updates state, 40 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-40-light.anim.e290c244b209.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-40-light.ff4cef5dd1d5.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Render Funnel, Burst of updates state, 60 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-60-dark.anim.e1742b3e47fd.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-60-dark.35be7a0c2a63.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Render Funnel, Burst of updates state, 60 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-60-light.anim.292b4f9ac6d4.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-60-light.e1cec7c32830.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Render Funnel, Burst of updates state, 80 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-80-dark.anim.213515e509fb.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-80-dark.796dc43c0fbf.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Render Funnel, Burst of updates state, 80 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-80-light.anim.8d1b56325680.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-80-light.ed1e1cc5f90f.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Render Funnel, Burst of updates state, 120 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-120-dark.anim.ed980afff745.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-120-dark.f68237a40871.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Render Funnel, Burst of updates state, 120 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-120-light.anim.b3c7bfbbf8a3.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/burst-120-light.8f7d889d1261.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Show one synthetic event burst updating the latest visible output.",
      "id": "burst",
      "label": "Burst of updates",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Render Funnel, Single scheduled redraw state, 40 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-40-dark.anim.ab53e52ac76c.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-40-dark.87320f5ef400.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 40 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-40-light.anim.f45363af515f.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-40-light.dc9242fccdb1.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 60 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-60-dark.anim.e76da39335c6.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-60-dark.a26559b19b25.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 60 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-60-light.anim.0cb386b5815d.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-60-light.81770623e8b2.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 80 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-80-dark.anim.5f261edaf6d3.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-80-dark.87ac67118246.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 80 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-80-light.anim.0c915b8a9f45.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-80-light.d576dcbb8abc.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 120 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-120-dark.anim.16da5bdfc599.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-120-dark.16f8ae08b916.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Render Funnel, Single scheduled redraw state, 120 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-120-light.anim.0e1d258d5f0e.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/settled-120-light.a5880548e3cd.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Display a render counter showing the burst shares one scheduled request.",
      "id": "settled",
      "label": "Single scheduled redraw",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Render Funnel, Closed before timer state, 40 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-40-dark.anim.841f9b426947.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-40-dark.91151b2fdef6.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Render Funnel, Closed before timer state, 40 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-40-light.anim.6e87a10b33c9.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-40-light.f3380e5791b2.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Render Funnel, Closed before timer state, 60 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-60-dark.anim.0c4c3a03d84f.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-60-dark.f366bc66b3c7.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Render Funnel, Closed before timer state, 60 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-60-light.anim.fe46b1e163f4.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-60-light.9bb3f7c89b8b.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Render Funnel, Closed before timer state, 80 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-80-dark.anim.de7d1aa3cc77.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-80-dark.29dcb4cb00d5.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Render Funnel, Closed before timer state, 80 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-80-light.anim.5c7da5ab7758.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-80-light.ea2f5f301818.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Render Funnel, Closed before timer state, 120 columns, dark theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-120-dark.anim.9bc132fb1f84.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-120-dark.dc0ec8ce975c.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Render Funnel, Closed before timer state, 120 columns, light theme, 13 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-120-light.anim.ec125bb394bb.webp",
          "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": 572,
          "rows": 13,
          "still": "https://pi-tui.ratstack.sh/frames/render-funnel/closed-120-light.9eb1bd2b9eaf.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Show the host unchanged after a pending redraw is cancelled.",
      "id": "closed",
      "label": "Closed before timer",
      "problems": []
    }
  ],
  "structure": "event burst -> pending timer\none callback -> requestRender",
  "thumbnails": {
    "dark": {
      "animation": "https://pi-tui.ratstack.sh/thumbs/render-funnel/dark.anim.3717dee5d3b4.webp",
      "src": "https://pi-tui.ratstack.sh/thumbs/render-funnel/dark.2cc5686bfe5d.webp"
    },
    "light": {
      "animation": "https://pi-tui.ratstack.sh/thumbs/render-funnel/light.anim.49e6a0bdf6b0.webp",
      "src": "https://pi-tui.ratstack.sh/thumbs/render-funnel/light.0ef08367fd7d.webp"
    }
  },
  "url": "https://pi-tui.ratstack.sh/patterns/render-funnel/",
  "widths": [
    40,
    60,
    80,
    120
  ]
}
