Skip to content

feat(mcp): diary-map v2 — no-forced-scroll inline + fullscreen escape hatch (display-mode-aware) #1231

Description

@legreffier

Context

Follow-up to #1194 / #1229 (the v1 diary-map MCP app). v1 ships an overview + zone + entry-mosaic UI, validated in the mcp-host fixture. But the real runtime is an embedded iframe inside a chat turn (Claude Desktop, ChatGPT), and a mosaic of many EntryCards does not fit there.

Goal (from design discussion): the inline view must never force the user to scroll. Scrolling is allowed as an escape hatch (fullscreen), not the default. Folders / nested "zoom into a sub-zone" is explicitly a later iteration — not this one.

Verified host constraints (research captured in signed diary entry on issue-1194-diary-map-app)

Source: @modelcontextprotocol/ext-apps@1.7.2 SDK + MCP Apps spec 2026-01-26 + Claude/ChatGPT docs.

  • Claude / Claude Desktop: published design guideline = inline max height ~500px, auto-fit, no nested scrolling inline; beyond that Claude scrolls the whole card. Claude ignores size-changed notifications and reads iframe.contentDocument.documentElement height directly → must drive height via document.documentElement.style.height (cap to budget, min-height not 100vh, never set height pre-paint). Fullscreen supported on desktop/web, NOT on Claude mobile (inline only).
  • ChatGPT Apps SDK: inline has no observed max-height (grows to content); fullscreen supported but fullscreen↔inline can remount + lose state.
  • displayMode ∈ {inline, fullscreen, pip}. App reads via getHostContext(), requests via requestDisplayMode({mode}) which returns the actual mode (host may refuse) — always gate on availableDisplayModes.
  • containerDimensions in host context is optional/host-populated — read if present, never depend on it.
  • No server-side size hint exists (_meta.ui = csp/permissions/domain/prefersBorder only).

Proposed v2 scope

  1. Inline budget ≈ 500px (Claude worst case). Inline = zone overview and/or a compact focused view; never a long entry list inline.
  2. Fullscreen is the scroll escape hatch. "View all entries" requests fullscreen (gated on availableDisplayModes), where scrolling/the full list is allowed. Inline-only fallback for Claude mobile (e.g. a compact paged/top-N control surface, no nested scroll).
  3. Display-mode-aware layouts: branch at runtime on getHostContext() platform + availableDisplayModes + theme. Inline-compact vs fullscreen-rich.
  4. Claude height handling: explicitly set documentElement.style.height (the autoResize fixes in feat(mcp): human-first diary map exploration app (rebuild #1194, supersedes #1212) #1229 work in the fixture but Claude ignores size-changed).
  5. Out of scope (later): nested sub-zones / drill-down folders.

Acceptance

  • In an inline iframe constrained to ~500px, the user can understand the diary and navigate zones without being forced to scroll.
  • "See all entries in a zone" works via fullscreen where available, with a non-broken inline fallback on Claude mobile.
  • Verified against the real Claude Desktop + ChatGPT hosts (not just the local fixture).

Relates to #1194, builds on #1229.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions