Files
CodeNomad/packages/ui/src/components/permission-notification-banner.tsx
bizzkoot f01a06d85b feat: add centralized permission notification system for agent/subagent requests
Implements a unified permission notification UI that adapts to different runtime
environments (Electron desktop vs web browser) with distinct visual presentations.

## What Changed

### New Components
- `permission-notification-banner.tsx`: Adaptive notification component
  * Electron (desktop): Full banner with "⚠️ Approval Required" text and count badge
  * Web browser (portrait): Circular indicator badge showing pending count
- `permission-approval-modal.tsx`: Interactive modal for reviewing/approving permissions
  * Displays permission type, detailed message, and diff viewer for file changes
  * Keyboard shortcuts: Enter (allow once), A (always), D (deny), Esc (close)
  * Queue management with "X of Y" counter for multiple pending permissions
- `permission-notification.css`: Comprehensive styling with pulsing animations

### Integration
- Updated `instance-shell2.tsx`:
  * Added banner to desktop center toolbar (next to Command Palette)
  * Added banner to mobile/phone layout center section
  * Added modal component for permission approval workflow
- Updated `controls.css`: Imported new permission notification styles

## Why This Change

**Before**: Permission requests had no visual indicator in the UI, making it
difficult for users to know when agent/subagent actions required approval.

**After**: Users receive clear, persistent visual notifications with:
- Pulsing animation to draw attention
- Environment-appropriate UI (full banner on desktop, compact badge on web)
- Click-to-review workflow with full permission details

## Benefits

1. **Better UX**: Users immediately see when permissions need approval
2. **Responsive Design**: Adapts to desktop (Electron) and web browser contexts
3. **Accessible**: Proper ARIA labels, keyboard shortcuts, and focus management
4. **Queue Management**: Handles multiple pending permissions gracefully
5. **Contextual Information**: Shows diffs for file changes, permission types, etc.

## Impact

- **No Breaking Changes**: Purely additive feature
- **Build**:  Verified successful build
- **Testing**:  Tested in Electron app and web browser
2026-01-07 21:44:43 +08:00

58 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Show, createMemo, type Component } from "solid-js"
import { getPermissionQueueLength } from "../stores/instances"
import { isElectronHost } from "../lib/runtime-env"
interface PermissionNotificationBannerProps {
instanceId: string
onClick: () => void
}
const PermissionNotificationBanner: Component<PermissionNotificationBannerProps> = (props) => {
const queueLength = createMemo(() => getPermissionQueueLength(props.instanceId))
const hasPermissions = createMemo(() => queueLength() > 0)
const isElectron = isElectronHost()
return (
<Show when={hasPermissions()}>
{/* Electron: Full banner with text */}
<Show when={isElectron}>
<button
type="button"
class="permission-notification-banner"
onClick={props.onClick}
aria-label={`${queueLength()} permission${queueLength() > 1 ? "s" : ""} pending approval`}
>
<span class="permission-notification-icon" aria-hidden="true">
</span>
<span class="permission-notification-text">
Approval Required
</span>
<Show when={queueLength() > 1}>
<span class="permission-notification-count" aria-label={`${queueLength()} permissions`}>
{queueLength()}
</span>
</Show>
</button>
</Show>
{/* Web: Compact indicator button */}
<Show when={!isElectron}>
<button
type="button"
class="permission-indicator-button"
onClick={props.onClick}
aria-label={`${queueLength()} permission${queueLength() > 1 ? "s" : ""} pending approval. Click to review.`}
title={`${queueLength()} permission${queueLength() > 1 ? "s" : ""} pending approval`}
>
<span class="permission-indicator-badge">
{queueLength() > 9 ? "9+" : queueLength()}
</span>
</button>
</Show>
</Show>
)
}
export default PermissionNotificationBanner