{
  "$schema": "https://pi-tui.ratstack.sh/schema/pattern.schema.json",
  "aka": [
    "Owned refresh"
  ],
  "animated": true,
  "apis": [
    {
      "name": "ctx.ui.setWidget",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user"
    },
    {
      "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": "pi.on",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events"
    },
    {
      "name": "setWidget",
      "url": null
    },
    {
      "name": "truncateToWidth",
      "url": null
    }
  ],
  "applicability": [
    "Use this when a widget or overlay changes independently of user input."
  ],
  "category": "rendering",
  "categoryLabel": "Rendering and performance",
  "consequences": [
    "A view can react to asynchronous changes without user input.",
    "Redraw cadence does not prove the underlying data was refreshed."
  ],
  "family": "lifecycle",
  "familyLabel": "Lifecycle",
  "id": "refresh-lease",
  "implementation": [
    "Redrawing does not necessarily refresh underlying data.",
    "Stop timers and unsubscribe listeners on disposal.",
    "Reject callbacks against replaced session contexts."
  ],
  "intent": "Pair asynchronous refresh triggers with component-owned cleanup.",
  "jsonUrl": "https://pi-tui.ratstack.sh/patterns/refresh-lease.json",
  "kind": "screen",
  "knownUses": [
    {
      "example": "refresh-and-cleanup",
      "moreRefs": 0,
      "refs": [
        {
          "file": "extensions/pi-autoresearch/index.ts",
          "lines": "1428-1445",
          "sha": "22bd30b1",
          "url": "https://github.com/nicobailon/pi-autoresearch/blob/22bd30b19482f2be5936031b2c6b149115779f16/extensions/pi-autoresearch/index.ts#L1428-L1445"
        }
      ],
      "repo": "pi-autoresearch",
      "repoUrl": "https://github.com/nicobailon/pi-autoresearch",
      "title": "Pair live refresh with owned timer cleanup"
    },
    {
      "example": "refresh-and-cleanup",
      "moreRefs": 0,
      "refs": [
        {
          "file": "coordinate/dashboard.ts",
          "lines": "910-917",
          "sha": "7f32f5d9",
          "url": "https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/dashboard.ts#L910-L917"
        },
        {
          "file": "coordinate/dashboard.ts",
          "lines": "1015-1018",
          "sha": "7f32f5d9",
          "url": "https://github.com/nicobailon/pi-coordination/blob/7f32f5d9d597a31fa477a629dfc6dd26bc649214/coordinate/dashboard.ts#L1015-L1018"
        }
      ],
      "repo": "pi-coordination",
      "repoUrl": "https://github.com/nicobailon/pi-coordination",
      "title": "Pair live refresh with owned timer cleanup"
    },
    {
      "example": "refresh-and-cleanup",
      "moreRefs": 0,
      "refs": [
        {
          "file": "overlay-coordinator.ts",
          "lines": "85-119",
          "sha": "09937ed6",
          "url": "https://github.com/nicobailon/pi-messenger/blob/09937ed647a1b07a3b595bf75943feacb80ff123/overlay-coordinator.ts#L85-L119"
        }
      ],
      "repo": "pi-messenger",
      "repoUrl": "https://github.com/nicobailon/pi-messenger",
      "title": "Pair live refresh with owned timer cleanup"
    },
    {
      "example": "background-session-widget",
      "moreRefs": 0,
      "refs": [
        {
          "file": "background-widget.ts",
          "lines": "24-106",
          "sha": "77df9a81",
          "url": "https://github.com/nicobailon/pi-interactive-shell/blob/77df9a8142a2f731635a4c5a01d68feecb5cced4/background-widget.ts#L24-L106"
        }
      ],
      "repo": "pi-interactive-shell",
      "repoUrl": "https://github.com/nicobailon/pi-interactive-shell",
      "title": "Show bounded background-session status below the editor"
    }
  ],
  "linkedFrom": [
    {
      "how": "owns subscriptions and redraws",
      "id": "event-relay",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/event-relay.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/event-relay.md",
      "name": "Event Relay",
      "url": "https://pi-tui.ratstack.sh/patterns/event-relay/"
    },
    {
      "how": "also ties timers to owners",
      "id": "notice-fuse",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/notice-fuse.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/notice-fuse.md",
      "name": "Notice Fuse",
      "url": "https://pi-tui.ratstack.sh/patterns/notice-fuse/"
    },
    {
      "how": "owns the redraw trigger's lifetime",
      "id": "render-funnel",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/render-funnel.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/render-funnel.md",
      "name": "Render Funnel",
      "url": "https://pi-tui.ratstack.sh/patterns/render-funnel/"
    }
  ],
  "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/refresh-lease.md",
  "motivation": "Messenger, Coordination and Autoresearch pair asynchronous redraw triggers with timer cleanup.",
  "name": "Refresh Lease",
  "participants": [
    {
      "name": "ctx.ui.setWidget",
      "role": "Mounts, replaces or clears one near-editor widget.",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#interact-with-the-user"
    },
    {
      "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": "pi.on",
      "role": "Registers ordered extension event handlers.",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#events"
    },
    {
      "name": "Resource owner",
      "role": "Cancels timers and subscriptions on disposal or shutdown.",
      "url": null
    }
  ],
  "pi": "1.0.3",
  "related": [
    {
      "how": "mutates local state on each tick",
      "id": "tick-heart",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/tick-heart.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/tick-heart.md",
      "name": "Tick Heart",
      "url": "https://pi-tui.ratstack.sh/patterns/tick-heart/"
    }
  ],
  "sampleCode": {
    "code": "// Refresh Lease: distinguish snapshot refresh from redraw and own both resources.\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { PatternStory } from \"../../../src/pattern.ts\";\n\nexport const story: PatternStory = {\n  id: \"refresh-lease\", title: \"Refresh Lease\", kind: \"screen\",\n  apis: [\"setWidget\", \"TUI.requestRender\", \"truncateToWidth\"],\n  states: [\n    { id: \"mounted\", label: \"Refreshing widget\", animated: true, steps: [{ type: \"tick\", ms: 600 }] },\n    { id: \"updated\", label: \"New snapshot\", animated: true, steps: [\n      { type: \"action\", name: \"snapshot\" }, { type: \"tick\", ms: 600 },\n    ] },\n    { id: \"disposed\", label: \"Refresh stopped\", animated: true, steps: [\n      { type: \"action\", name: \"dispose\" }, { type: \"action\", name: \"late-snapshot\" }, { type: \"tick\", ms: 600 },\n    ] },\n  ],\n  setup({ ui, tui, theme, clock, action }) {\n    ui.setEditorText(\"Review the background checks.\");\n    // Fixture publisher: the view subscribes; late snapshots have no listener.\n    const listeners = new Set<(count: number) => void>();\n    const publish = (count: number) => { for (const listener of listeners) listener(count); };\n    let draws = 0;\n    let proofTimer: ReturnType<typeof setTimeout> | undefined;\n    ui.setWidget(\"refresh\", () => {\n      let snapshot = 2;\n      const started = clock.now();\n      const update = (count: number) => { snapshot = count; tui.requestRender(); };\n      listeners.add(update);\n      const redraw = setInterval(() => { draws++; tui.requestRender(); }, 200);\n      return {\n        invalidate() {},\n        render: width => [\n          truncateToWidth(theme.fg(\"accent\", \"Parser checks · \" + snapshot + \"/4 complete\"), width),\n          truncateToWidth(theme.fg(\"muted\", \"Running \" + ((clock.now() - started) / 1000).toFixed(1) + \"s · redraws \" + draws), width),\n        ],\n        dispose() { clearInterval(redraw); listeners.delete(update); },\n      };\n    }, { placement: \"belowEditor\" });\n    action(\"snapshot\", () => publish(3));\n    action(\"late-snapshot\", () => publish(4));\n    action(\"dispose\", () => {\n      ui.setWidget(\"refresh\", undefined); // Pi invokes the component's dispose.\n      ui.setWidget(\"receipt\", [theme.fg(\"success\", \"Refresh stopped · resources released\")]);\n      ui.setStatus(\"refresh\", theme.fg(\"muted\", \"stopped at \" + draws + \" redraws\"));\n      const stoppedDraws = draws;\n      proofTimer = setTimeout(() => {\n        if (draws !== stoppedDraws || listeners.size) throw new Error(\"Refresh lease outlived its view\");\n      }, 500);\n    });\n    return () => { if (proofTimer !== undefined) clearTimeout(proofTimer); };\n  },\n};\n",
    "lang": "ts",
    "path": "stories/patterns/lifecycle/refresh-lease.ts"
  },
  "states": [
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Refresh Lease, Refreshing widget state, 40 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-40-dark.anim.179edd0cb816.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-40-dark.98cd7bf5d452.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 40 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-40-light.anim.e60b14555303.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-40-light.ab79dd0c49d8.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 60 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-60-dark.anim.67f8e446355a.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-60-dark.27d283fc0ef5.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 60 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-60-light.anim.906605f14a73.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-60-light.f2d96baa0620.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 80 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-80-dark.anim.e125a10bf2fd.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-80-dark.fd533578e558.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 80 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-80-light.anim.a001acc480ff.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-80-light.9e188ac9d19b.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 120 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-120-dark.anim.beaab4b72983.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-120-dark.206443cb2dfb.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Refresh Lease, Refreshing widget state, 120 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-120-light.anim.016d168455d9.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/mounted-120-light.b2a4c1991a9a.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Show synthetic background durations advancing.",
      "id": "mounted",
      "label": "Refreshing widget",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Refresh Lease, New snapshot state, 40 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-40-dark.anim.84455973dd93.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-40-dark.bcac4d3f1323.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Refresh Lease, New snapshot state, 40 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-40-light.anim.43f678f9ae01.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-40-light.6db66c7461e8.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Refresh Lease, New snapshot state, 60 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-60-dark.anim.cb24bda84f20.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-60-dark.e1ba88c25141.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Refresh Lease, New snapshot state, 60 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-60-light.anim.f0bd3839652e.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-60-light.55072dce8fb9.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Refresh Lease, New snapshot state, 80 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-80-dark.anim.4d69baf5a7ef.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-80-dark.ae6bf9c89b58.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Refresh Lease, New snapshot state, 80 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-80-light.anim.a66d078d9d09.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-80-light.e7f987a8de59.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Refresh Lease, New snapshot state, 120 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-120-dark.anim.743786f8d0b5.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-120-dark.0cf3d310b054.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Refresh Lease, New snapshot state, 120 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-120-light.anim.d8902fea09a9.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/updated-120-light.91679012673f.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Show a data refresh separately from its redraw.",
      "id": "updated",
      "label": "New snapshot",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Refresh Lease, Refresh stopped state, 40 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-40-dark.anim.d254efc89714.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-40-dark.522cf48cb76a.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 40 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-40-light.anim.392a8dceaa58.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-40-light.d8f61f75ce63.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 60 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-60-dark.anim.8a42ff7ce03f.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-60-dark.b5c74b2144bb.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 60 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-60-light.anim.dc6b3b6d57be.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-60-light.08b840835d13.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 80 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-80-dark.anim.1229ff9f6792.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-80-dark.9d1ee0f71505.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 80 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-80-light.anim.f1fb242e27f8.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-80-light.d2ea0eac76f9.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 120 columns, dark theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-120-dark.anim.4c8d1415c1da.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-120-dark.e697f071e6de.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Refresh Lease, Refresh stopped state, 120 columns, light theme, 11 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-120-light.anim.8e65f28a49e0.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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/refresh-lease/disposed-120-light.9ffc557bd14d.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Clear the widget and show that its timer and listener no longer update the host.",
      "id": "disposed",
      "label": "Refresh stopped",
      "problems": []
    }
  ],
  "structure": "timer / subscription -> data\nchanged data -> requestRender\ndispose -> cancel / unsubscribe",
  "thumbnails": {
    "dark": {
      "animation": "https://pi-tui.ratstack.sh/thumbs/refresh-lease/dark.anim.c4fb68bdafc1.webp",
      "src": "https://pi-tui.ratstack.sh/thumbs/refresh-lease/dark.a94bd8af02c9.webp"
    },
    "light": {
      "animation": "https://pi-tui.ratstack.sh/thumbs/refresh-lease/light.anim.ab80a83415e6.webp",
      "src": "https://pi-tui.ratstack.sh/thumbs/refresh-lease/light.af88e62dadeb.webp"
    }
  },
  "url": "https://pi-tui.ratstack.sh/patterns/refresh-lease/",
  "widths": [
    40,
    60,
    80,
    120
  ]
}
