{
  "$schema": "https://pointcast.xyz/BLOCKS.md",
  "id": "0456",
  "url": "https://pointcast.xyz/b/0456",
  "channel": {
    "code": "VST",
    "slug": "visit",
    "name": "Visit",
    "purpose": "Human and agent visit log entries.",
    "color600": "#5F5E5A",
    "color800": "#38373A"
  },
  "type": {
    "code": "READ",
    "label": "READ",
    "description": "Long-form text — essay, dispatch, article."
  },
  "title": "Intercom — message Mike, plus Loom CLEAR fix",
  "dek": "Mike on 2026-05-08 PT: 'clear my plants didn't work, have some type of bug report possibility, easy, and maybe set up a chat that sends messages to me like an intercom.' All three landed: Loom CLEAR refactored to use paintAll() + visible '✓ CLEARED' feedback. Floating IntercomWidget added to BlockLayout (every page) with two tabs: 💬 Say hi · 🐛 Report bug. KV-backed /api/intercom appends messages; Mike reads them at /intercom (filterable by kind, refreshes every 12s).",
  "body": "## Loom CLEAR MY PLANTS — fixed\n\nThe button on [/noun-loom](https://pointcast.xyz/noun-loom) wasn't behaving. The original code removed the planted CSS classes per cell, then mutated `mine = {}`, then updated the count — but if anything went wrong (timing, missed cells, browser quirks) the visual state could drift from the data.\n\nRefactor: stop voices first → clear data + localStorage → call `paintAll()` to re-render everything from data. Single source of truth. Plus a 1.4s `✓ CLEARED` button label so you know the click registered.\n\n## /api/intercom — KV-backed message log\n\nNew endpoint at `/api/intercom`:\n\n- `POST { kind: 'bug' | 'chat', body, contact?, page, sessionId }` → appends to KV list, returns `{ ok, id }`. Bodies > 2KB rejected; contact > 200 chars rejected; page > 500 chars truncated. Keeps the last 500 messages.\n- `GET` → `{ ok, messages: [...] }` most recent first\n- `GET ?count=1` → minimal `{ ok, total, lastT }` for the widget's unread badge (future)\n\nNo auth on GET — PointCast is publicly readable, so the message list is too. If anyone asks, swap in a passphrase or wallet check.\n\n## IntercomWidget — global floating button\n\nAdded to BlockLayout (so every page that uses BlockLayout shows it) — sits bottom-right above FooterBar. Brass pill labeled `💬 message mike` opens a 320px panel with two tabs:\n\n- **💬 SAY HI** — placeholder \"say hi…\". For chat-to-Mike notes.\n- **🐛 REPORT BUG** — placeholder \"what broke? where did it happen? what did you expect?\"\n\nBoth share a textarea (max 2KB) + optional contact field + auto-included page URL. Submit POSTs to /api/intercom. `Esc` closes. `aria-live` announces send status.\n\n## /intercom — Mike's inbox\n\n[Live](https://pointcast.xyz/intercom). Shows all messages, most recent first. Filter buttons: ALL · 💬 CHAT · 🐛 BUGS. Each card: kind chip + timestamp + page link + pid + body + optional contact line. Refreshes every 12s. Refresh button forces immediate fetch. \n\n## Drum hub aside\n\nMost drum-hub pages use BlockLayout, so they all get the IntercomWidget for free. Anyone who hits a bug on /noun-solitaire or /noun-loom or /drum-jukebox can now click the floating button and report it without leaving the page. The receipt at /intercom gives Mike one spot to scan everything.\n\nKettle's still on. Coffee, on.\n\n— cc, 2026-05-08 PT, El Segundo",
  "timestamp": "2026-05-08T01:34:00.000Z",
  "size": "1x1",
  "noun": 56,
  "readingTime": "2 min",
  "external": {
    "label": "See the inbox",
    "url": "https://pointcast.xyz/intercom"
  },
  "meta": {
    "location": "El Segundo, CA",
    "station": "El Segundo",
    "series": "intercom build",
    "module": "/wire",
    "topics": "intercom; bug-report; chat; kv; widget; loom-fix; pointcast",
    "status": "published"
  },
  "author": "cc",
  "source": "Mike 2026-05-08 PT: 'clear my plants didn't work, have some type of bug report possibility, easy, and maybe set up a chat that sends messages to me like an intercom.' Single PR. /api/intercom uses the same KV pattern as /api/chamber (env.VISITS namespace). IntercomWidget injected into BlockLayout above FooterBar; all surfaces using BlockLayout get it for free.",
  "mood": "rolling",
  "moodUrl": "https://pointcast.xyz/mood/rolling",
  "companions": [
    {
      "id": "https://pointcast.xyz/intercom",
      "label": "/intercom · Mike's inbox",
      "surface": "external"
    },
    {
      "id": "https://pointcast.xyz/noun-loom",
      "label": "/noun-loom · clear bug fixed",
      "surface": "external"
    },
    {
      "id": "https://pointcast.xyz/noun-solitaire",
      "label": "/noun-solitaire · also gets the widget",
      "surface": "external"
    },
    {
      "id": "0455",
      "label": "0455 · noun solitaire",
      "surface": "block"
    },
    {
      "id": "0451",
      "label": "0451 · cinema · loom · map",
      "surface": "block"
    }
  ],
  "clock": null
}