Skip to content

web: permission prompt action buttons clipped below the fold for long commands #56863

Closed as duplicate

Description

@KenLee-SeaburyTFX

Summary

In the web UI at a narrow (phone) viewport, a permission prompt whose shell
command wraps to many lines grows until it hits its max-height and pushes its
action buttons (Deny / Allow always / Allow once) below the visible
viewport. The request cannot be answered from the phone.

Environment

  • opencode version: 2.0.22
  • OS: Linux 6.18.33.1-microsoft-standard-WSL2 (x64) — server runs in WSL2, also exposed over a tailnet
  • Terminal: N/A for this issue (web UI). Server side: TERM=xterm-256color
  • Shell: /bin/bash (server); client is a mobile browser
  • Install/channel: release, curl install (~/.opencode/bin/opencode), update: disable in config
  • Active plugins: none — no plugins key in ~/.config/opencode/opencode.jsonc. One local MCP server (zvec_grep) is configured and unrelated.
  • Client: Chrome on Android (exact Android/Chrome version not captured — the server does not log client user agents)
  • Web bundle served by 2.0.22: index-Ddx0tBUE.css, index-CN8iru5i.js, route-gEGib-yE.js

Reproduction

  1. Start the server (opencode serve --service) and open the web UI on a phone.
  2. Configure permissions so shell commands ask (e.g. { "action": "shell", "resource": "*", "effect": "ask" }).
  3. Have the agent run a shell command long enough to wrap across many lines.
  4. The permission dock appears. As the command text wraps, the dock grows.
  5. The footer holding Deny / Allow always / Allow once ends up below the bottom edge of the viewport and cannot be reached or tapped, leaving the request stuck.

Consistently reproducible with long commands. Short commands that fit without
wrapping are unaffected, because the dock never reaches its max-height.

Expected Behavior

The action buttons remain visible and tappable. The dock should be bounded by
the space actually available to it, so the scrollable command area absorbs the
overflow instead of the footer being pushed out of view.

Actual Behavior

The footer is clipped below the fold. With a long enough command the prompt is
impossible to answer from the phone.

Additional Context

Component structure

Built in route-gEGib-yE.js (data-component=dock-prompt, data-kind=permission):

<div data-component="dock-prompt" data-kind="permission">   <!-- max-height: 100dvh -->
  <div data-slot="permission-body">                          <!-- flex:1; min-height:0 -->
    <div data-slot="permission-header">…</div>
    <div data-slot="permission-content">                     <!-- flex:1; min-height:0 -->
      <div data-slot="permission-row">…hint…</div>
      <div data-slot="permission-row">
        <span data-slot="permission-spacer"></span>
        <div data-slot="permission-patterns">                <!-- flex:1; overflow-y:auto -->
          <code class="text-12-regular text-text-base break-all">…long command…</code>
        </div>
      </div>
    </div>
  </div>
  <div data-slot="permission-footer">                        <!-- flex-shrink:0 -->
    <div data-slot="permission-footer-actions">
      <button>Deny</button><button>Allow always</button><button>Allow once</button>
    </div>
  </div>
</div>

The footer is also mounted as a data-dock-surface="tray" wrapper
(overflow: clip; border-radius: 12px).

Relevant CSS (verbatim from index-Ddx0tBUE.css in 2.0.22)

[data-component=dock-prompt][data-kind=permission]{flex-direction:column;gap:0;min-height:0;max-height:100dvh;display:flex;position:relative}

[data-component=dock-prompt][data-kind=permission] [data-slot=permission-content]{flex-direction:column;flex:1;gap:4px;min-height:0;display:flex}

[data-component=dock-prompt][data-kind=permission] [data-slot=permission-patterns]{scrollbar-width:none;-ms-overflow-style:none;flex-direction:column;flex:1;gap:6px;min-height:0;margin-top:8px;margin-bottom:16px;padding:1px 0 8px;display:flex;overflow-y:auto}

[data-component=dock-prompt][data-kind=permission] [data-slot=permission-patterns]::-webkit-scrollbar{display:none}

[data-component=dock-prompt][data-kind=permission] [data-slot=permission-footer]{flex-shrink:0;justify-content:space-between;align-items:center;margin-top:-24px;padding:32px 8px 8px;display:flex}

Analysis

  • The dock caps itself at max-height: 100dvh — the whole viewport — rather
    than the height actually available where it is docked.
  • The footer is the last flex child and has flex-shrink: 0, so when the
    content overflows it is the footer that ends up outside the visible area.
  • permission-patterns already has flex: 1 and overflow-y: auto, so it
    can absorb the overflow — but only if the dock's height is bounded by its
    container. 100dvh does not bound it against the visible viewport when
    browser chrome or the on-screen keyboard is showing.
  • The page sets interactive-widget=resizes-content
    (<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content, viewport-fit=cover" />),
    which widens the gap between dvh and the actually visible area on Android.
  • Secondary: because the scroll container hides its scrollbar
    (scrollbar-width: none plus ::-webkit-scrollbar{display:none}), there is no
    affordance indicating that the command text scrolls.

Suggested fix

Bound the dock by the space available to it rather than the viewport, e.g. cap
against the parent (max-height: 100% inside a bounded container) or
max-height: min(100dvh, <available>), keeping the footer visible while
permission-patterns scrolls. Consider revealing the scrollbar or adding a fade
so the scrollable command area is discoverable.

Workarounds

  • Rotate to landscape (fewer wrapped lines, shorter dock).
  • Dismiss the on-screen keyboard and let the URL bar retract.
  • Swipe inside the command text — it is a scroll container with a hidden scrollbar.
  • Toggle Auto-accept permissions from the command palette
    (permissions.autoaccept) to skip the prompt entirely.
  • Answer the request out of band:
    POST /api/session/{sessionID}/permission/{requestID}/reply with
    {"decision":"once"} (once | always | reject), e.g. from the TUI or CLI.

Activity

  1. opencode-agent commented on Oct 18, 2026

    @opencode-agent
    Contributor

    Thanks for the very thorough report and CSS analysis! I think this is a duplicate of #49644, which is still open. It describes the same problem: a long command makes the permission prompt taller than the space it has, so the Deny / Allow buttons are pushed off-screen and the request can't be answered.

    Possible duplicates and closely related issues, ordered from most to least likely:

    Your finding that the dock is capped at 100dvh rather than at the space actually available (it still is on dev), and the phone and interactive-widget=resizes-content angle, would be very useful on #49644. Please consider adding them there.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.

  2. opencode-agent commented on Oct 19, 2026

    @opencode-agent
    Contributor

    Closing this as a duplicate of #49644, which is still open. It covers the same problem: a long command makes the permission prompt grow until the Deny / Allow buttons are pushed out of view. We waited a day in case your case was different, and since there was no reply, we're tracking it there.

    Your analysis would help a lot on #49644, so please consider posting it there. That covers the max-height: 100dvh cap on the dock, the footer being pushed out, and the Android interactive-widget=resizes-content angle. If your problem turns out to be different after all, reply here and we can reopen it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions