Skip to content

Auto-close browser tab after plan confirmation (with environment detection) #41

@jellydn

Description

@jellydn

What

Add intelligent browser tab closing after plan approval/deny, with environment-aware behavior:

  • Local Claude Code: Auto-close tab after decision (1-2s delay for UX confirmation)
  • Local OpenCode: Auto-close tab after decision (1-2s delay for UX confirmation)
  • Remote sessions: Keep tab open (already no browser opened, but if user opens manually, don't close)

Why

Current Problem: Browser tabs remain open indefinitely after plan decisions, creating friction:

  1. Manual cleanup required: Users must manually close tabs after each plan review
  2. Tab accumulation: Frequent plan reviews lead to many open tabs
  3. Broken workflow flow: The UI says "Return to your Claude Code terminal" but the tab stays open, requiring manual action
  4. Inconsistent with expectations: Users expect the tool to disappear after completing its purpose

User Impact: Every plan approval requires an extra manual step (closing tab) that breaks the seamless workflow.

How

Phase 1: Environment Detection (packages/server/remote.ts)

// Add new detection function
export function getPlanEnvironment(): 'claude-code' | 'opencode' | 'unknown' {
  // Check for Claude Code hook environment
  if (process.env.CLAIDE_HOOK !== undefined) {
    return 'claude-code'
  }
  
  // Check for OpenCode plugin environment
  if (process.env.OPENCODE_PLUGIN !== undefined) {
    return 'opencode'
  }
  
  // Fallback: detect via hook file presence or command context
  return 'unknown'
}

Phase 2: Frontend Closing Logic (packages/ui/components/Viewer.tsx)

// After approve/deny success:
const shouldCloseTab = !isRemoteSession && environment !== 'unknown'

if (shouldCloseTab) {
  setTimeout(() => {
    // Show brief "closing..." message
    setClosingState(true)
    
    // Attempt close (note: window.close() only works for window.open() windows)
    window.close()
    
    // Fallback: show message if close blocked
    setTimeout(() => {
      if (!window.closed) {
        setCloseFailed(true)
      }
    }, 500)
  }, 1500) // 1.5s delay to show success message
}

Phase 3: Graceful Fallback UI

If window.close() is blocked by browser security (script-opened windows restriction):

{closeFailed && (
  <div className="banner">
    You can safely close this tab and return to your terminal.
  </div>
)}

Implementation Files

  1. packages/server/remote.ts: Add getPlanEnvironment() function
  2. packages/ui/components/Viewer.tsx: Add close logic after approve/deny
  3. packages/ui/types.ts: Add PlanEnvironment type
  4. packages/server/index.ts: Pass environment to frontend via /api/plan response

Browser Security Note

window.close() only works on windows opened by window.open(). Since we use open/xdg-open/start, the browser may block closing. Fallback UI is essential.

Acceptance Criteria

  • Local Claude Code: Tab closes 1.5s after approval
  • Local OpenCode: Tab closes 1.5s after approval
  • Remote sessions: No close attempt (tab stays open if manually opened)
  • Fallback message shown if browser blocks window.close()
  • Success message visible for at least 1s before close attempt
  • Environment detection works for both plugins

Open Questions

  • Should there be a user setting to disable auto-close? (e.g., PLANNOTATOR_NO_AUTO_CLOSE=1)
  • Should the delay be configurable? Currently hardcoded 1500ms

Related: Current behavior shows "Return to your Claude Code terminal" message but doesn't actually close the tab.

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