{
  "$schema": "https://pi-tui.ratstack.sh/schema/pattern.schema.json",
  "aka": [
    "Fixed-tick view"
  ],
  "animated": true,
  "apis": [
    {
      "name": "ctx.ui.custom",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays"
    },
    {
      "name": "Component",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#understand-the-component-model"
    },
    {
      "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": "Box",
      "url": null
    }
  ],
  "applicability": [
    "Use this when animation should continue independently of user input."
  ],
  "category": "animation",
  "categoryLabel": "Animation",
  "consequences": [
    "A live view can animate without a model or user event.",
    "The interval must stop on disposal and resizing can affect behavior."
  ],
  "family": "behavioral",
  "familyLabel": "Behavioral",
  "id": "tick-heart",
  "implementation": [
    "Clear the interval on disposal.",
    "Keep resize-dependent behavior explicit.",
    "The story demonstrates reusable ticking rather than copying a game."
  ],
  "intent": "Advance local view state and request redraws on a component-owned interval.",
  "jsonUrl": "https://pi-tui.ratstack.sh/patterns/tick-heart.json",
  "kind": "screen",
  "knownUses": [
    {
      "example": "arcade-fixed-tick-custom-component",
      "moreRefs": 0,
      "refs": [
        {
          "file": "arcade/tetris.ts",
          "lines": "187-233",
          "sha": "bca5070b",
          "url": "https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/tetris.ts#L187-L233"
        },
        {
          "file": "arcade/ping.ts",
          "lines": "86-139",
          "sha": "bca5070b",
          "url": "https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/ping.ts#L86-L139"
        },
        {
          "file": "arcade/picman.ts",
          "lines": "214-245",
          "sha": "bca5070b",
          "url": "https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/picman.ts#L214-L245"
        },
        {
          "file": "arcade/spice-invaders.ts",
          "lines": "295-345",
          "sha": "bca5070b",
          "url": "https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/spice-invaders.ts#L295-L345"
        },
        {
          "file": "arcade/badlogic-game/badlogic-game.ts",
          "lines": "22-96",
          "sha": "bca5070b",
          "url": "https://github.com/nicobailon/pi-extensions/blob/bca5070b541ffa7d273e74036dcd7d5f8b63eed3/arcade/badlogic-game/badlogic-game.ts#L22-L96"
        }
      ],
      "repo": "pi-extensions",
      "repoUrl": "https://github.com/nicobailon/pi-extensions",
      "title": "Arcade: fixed-tick full-screen component"
    },
    {
      "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"
    }
  ],
  "linkedFrom": [
    {
      "how": "advances the live state",
      "id": "pause-latch",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/pause-latch.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/pause-latch.md",
      "name": "Pause Latch",
      "url": "https://pi-tui.ratstack.sh/patterns/pause-latch/"
    },
    {
      "how": "mutates local state on each tick",
      "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/"
    }
  ],
  "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/tick-heart.md",
  "motivation": "Nico's arcade components own intervals that advance local state independently of keyboard input.",
  "name": "Tick Heart",
  "participants": [
    {
      "name": "ctx.ui.custom",
      "role": "Mounts one interaction and resolves through done.",
      "url": "https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/tui.md#use-custom-screens-and-overlays"
    },
    {
      "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": "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": "Owned interval",
      "role": "Advances local state until disposal stops it.",
      "url": null
    }
  ],
  "pi": "1.0.3",
  "related": [
    {
      "how": "suspends change while preserving input",
      "id": "pause-latch",
      "jsonUrl": "https://pi-tui.ratstack.sh/patterns/pause-latch.json",
      "mdUrl": "https://pi-tui.ratstack.sh/patterns/pause-latch.md",
      "name": "Pause Latch",
      "url": "https://pi-tui.ratstack.sh/patterns/pause-latch/"
    }
  ],
  "sampleCode": {
    "code": "// Tick Heart: a component-owned interval advances local state until disposal.\nimport { Box, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { PatternStory } from \"../../../src/pattern.ts\";\n\nexport const story: PatternStory = {\n  id: \"tick-heart\", title: \"Tick Heart\", kind: \"screen\",\n  apis: [\"ctx.ui.custom\", \"Component\", \"Box\", \"TUI.requestRender\"],\n  states: [\n    { id: \"running\", label: \"Ticking\", animated: true, steps: [{ type: \"tick\", ms: 600 }] },\n    { id: \"narrow\", label: \"Compact viewport\", animated: true, steps: [\n      { type: \"resize\", rows: 12 }, { type: \"tick\", ms: 400 },\n    ] },\n    { id: \"disposed\", label: \"Disposed\", steps: [\n      { type: \"keys\", data: \"\\x1b\" }, { type: \"tick\", ms: 400 },\n      { type: \"action\", name: \"prove-stopped\" },\n    ] },\n  ],\n  setup({ ui, theme, action }) {\n    let ticks = 0, stoppedAt = 0;\n    let lifecycle: \"running\" | \"disposed\" = \"running\";\n    ui.setEditorText(\"Draft a progress note\");\n    ui.setWidget(\"heartbeat\", [theme.fg(\"muted\", \"Local timer · no work is running\")]);\n    action(\"prove-stopped\", () => {\n      if (lifecycle !== \"disposed\" || ticks !== stoppedAt) throw new Error(\"Interval survived disposal\");\n      ui.setWidget(\"heartbeat\", [\n        theme.fg(\"success\", \"Disposed · stopped at tick \" + stoppedAt),\n        theme.fg(\"muted\", \"400ms later · no further ticks\"),\n      ]);\n    });\n    void ui.custom<void>((tui, theme, keys, done) => {\n      const interval = setInterval(() => { ticks++; tui.requestRender(); }, 200);\n      function dispose() {\n        if (lifecycle === \"disposed\") return;\n        lifecycle = \"disposed\";\n        stoppedAt = ticks;\n        clearInterval(interval);\n      }\n      return {\n        dispose,\n        invalidate() {},\n        handleInput(data) {\n          if (keys.matches(data, \"tui.select.cancel\")) { dispose(); done(); }\n        },\n        render(width) {\n          // Shrinking height demonstrates compact mode without changing matrix columns.\n          const compact = width < 60 || tui.terminal.rows <= 12;\n          const slots = compact ? 8 : 16;\n          const marker = Array.from({ length: slots }, (_, index) => index === ticks % slots ? \"●\" : \"·\").join(\" \");\n          const panel = new Box(1, 1, text => theme.bg(\"customMessageBg\", text));\n          panel.addChild(new Text(theme.fg(\"accent\", \"Heartbeat · tick \" + ticks + (compact ? \" · compact\" : \" · full\")), 0, 0));\n          panel.addChild(new Text(theme.fg(\"success\", truncateToWidth(marker, width - 2)), 0, 0));\n          panel.addChild(new Text(theme.fg(\"dim\", \"200ms cadence · escape closes\"), 0, 0));\n          return panel.render(width);\n        },\n      };\n    });\n  },\n};\n",
    "lang": "ts",
    "path": "stories/patterns/behavioral/tick-heart.ts"
  },
  "states": [
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Tick Heart, Ticking state, 40 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-40-dark.anim.5581822b8dbc.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-40-dark.a94e35dcc0f0.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Tick Heart, Ticking state, 40 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-40-light.anim.04f03cd43b07.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-40-light.7fdfd1b3ba05.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Tick Heart, Ticking state, 60 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-60-dark.anim.7eef561e0cfd.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-60-dark.7146ec8d42c0.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Tick Heart, Ticking state, 60 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-60-light.anim.6dbe3bf962e7.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-60-light.0e088a4d7c30.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Tick Heart, Ticking state, 80 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-80-dark.anim.b86214fea2ac.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-80-dark.df31f909feb5.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Tick Heart, Ticking state, 80 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-80-light.anim.1086a6c17742.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-80-light.e268405491c5.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Tick Heart, Ticking state, 120 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-120-dark.anim.4e6aa8919895.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-120-dark.1592c353d864.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Tick Heart, Ticking state, 120 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/running-120-light.anim.645090b7e8ea.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/running-120-light.4ce92d893de8.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Animate a synthetic progress marker at a fixed cadence.",
      "id": "running",
      "label": "Ticking",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Tick Heart, Narrow view state, 40 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-40-dark.anim.dd36d5b13ea4.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-40-dark.3f696bc90db4.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Tick Heart, Narrow view state, 40 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-40-light.anim.ac4312e748ce.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-40-light.ea351ebf5bf6.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Tick Heart, Narrow view state, 60 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-60-dark.anim.f3fab449d90a.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-60-dark.c1d6ca3cca09.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Tick Heart, Narrow view state, 60 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-60-light.anim.b8d6a15b1c29.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-60-light.a8346b70eb1f.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Tick Heart, Narrow view state, 80 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-80-dark.anim.03e77df5bf77.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-80-dark.67a7393ecc40.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Tick Heart, Narrow view state, 80 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-80-light.anim.1685713bc1bd.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-80-light.65ebd94901fb.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Tick Heart, Narrow view state, 120 columns, dark theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-120-dark.anim.9fce12a6308c.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-120-dark.cffa473ffec5.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Tick Heart, Narrow view state, 120 columns, light theme, 12 rows, animated",
          "animated": true,
          "animation": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-120-light.anim.2e73cafb85f2.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": 528,
          "rows": 12,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/narrow-120-light.ca7c35ce6ae5.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Resize and show the view's explicit compact or paused behavior.",
      "id": "narrow",
      "label": "Narrow view",
      "problems": []
    },
    {
      "dont": false,
      "expectFail": [],
      "frames": [
        {
          "alt": "Tick Heart, Disposed state, 40 columns, dark theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-40-dark.38a48f43944e.webp",
          "theme": "dark",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Tick Heart, Disposed state, 40 columns, light theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-40-light.969013d0ad0a.webp",
          "theme": "light",
          "w": 800,
          "width": 40
        },
        {
          "alt": "Tick Heart, Disposed state, 60 columns, dark theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-60-dark.a8ceaace8361.webp",
          "theme": "dark",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Tick Heart, Disposed state, 60 columns, light theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-60-light.a47f559a2536.webp",
          "theme": "light",
          "w": 1200,
          "width": 60
        },
        {
          "alt": "Tick Heart, Disposed state, 80 columns, dark theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-80-dark.23fc1b4a7560.webp",
          "theme": "dark",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Tick Heart, Disposed state, 80 columns, light theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-80-light.d5ae6040c589.webp",
          "theme": "light",
          "w": 1600,
          "width": 80
        },
        {
          "alt": "Tick Heart, Disposed state, 120 columns, dark theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-120-dark.11f5ca8736d6.webp",
          "theme": "dark",
          "w": 2400,
          "width": 120
        },
        {
          "alt": "Tick Heart, Disposed state, 120 columns, light theme, 11 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": 484,
          "rows": 11,
          "still": "https://pi-tui.ratstack.sh/frames/tick-heart/disposed-120-light.acb8ef1bd21c.webp",
          "theme": "light",
          "w": 2400,
          "width": 120
        }
      ],
      "how": "Close the interaction and stop further ticks.",
      "id": "disposed",
      "label": "Disposed",
      "problems": []
    }
  ],
  "structure": "interval -> local state -> redraw\ninput -> local state\ndispose -> stop interval",
  "thumbnails": {
    "dark": {
      "animation": "https://pi-tui.ratstack.sh/thumbs/tick-heart/dark.anim.907be0bc386a.webp",
      "src": "https://pi-tui.ratstack.sh/thumbs/tick-heart/dark.149edd1ff35b.webp"
    },
    "light": {
      "animation": "https://pi-tui.ratstack.sh/thumbs/tick-heart/light.anim.4cb42c0eaac6.webp",
      "src": "https://pi-tui.ratstack.sh/thumbs/tick-heart/light.ba9af1b64b69.webp"
    }
  },
  "url": "https://pi-tui.ratstack.sh/patterns/tick-heart/",
  "widths": [
    40,
    60,
    80,
    120
  ]
}
