Download desktop/src/components/chat/ComputerUsePermissionModal.tsx from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 9.84 kB
-
https://huggingface.co/chenbhao/codev/resolve/e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/components/chat/ComputerUsePermissionModal.tsx
- Command line
-
hf download hf://chenbhao/codev@e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/components/chat/ComputerUsePermissionModal.tsx
-
curl -L -o ComputerUsePermissionModal.tsx https://huggingface.co/chenbhao/codev/resolve/e40073cc959cd4729bf4009662bca374c5c4e266/desktop/src/components/chat/ComputerUsePermissionModal.tsx
9.84 kB
| import { useMemo, useState } from 'react' | |
| import { useTranslation } from '../../i18n' | |
| import { computerUseApi } from '../../api/computerUse' | |
| import { useChatStore } from '../../stores/chatStore' | |
| import type { | |
| ComputerUsePermissionRequest, | |
| ComputerUsePermissionResponse, | |
| } from '../../types/chat' | |
| import { Button } from '../shared/Button' | |
| import { Modal } from '../shared/Modal' | |
| type Props = { | |
| sessionId: string | |
| request: ComputerUsePermissionRequest | null | |
| } | |
| const DEFAULT_GRANT_FLAGS = { | |
| clipboardRead: false, | |
| clipboardWrite: false, | |
| systemKeyCombos: false, | |
| } as const | |
| function denyAllResponse(): ComputerUsePermissionResponse { | |
| return { | |
| granted: [], | |
| denied: [], | |
| flags: { ...DEFAULT_GRANT_FLAGS }, | |
| userConsented: false, | |
| } | |
| } | |
| function buildAllowResponse( | |
| request: ComputerUsePermissionRequest, | |
| ): ComputerUsePermissionResponse { | |
| const now = Date.now() | |
| const granted = request.apps.flatMap((app) => { | |
| if (!app.resolved || app.alreadyGranted) return [] | |
| return [{ | |
| bundleId: app.resolved.bundleId, | |
| displayName: app.resolved.displayName, | |
| grantedAt: now, | |
| tier: app.proposedTier, | |
| }] | |
| }) | |
| const denied = request.apps.flatMap((app) => { | |
| if (app.resolved) return [] | |
| return [{ | |
| bundleId: app.requestedName, | |
| reason: 'not_installed' as const, | |
| }] | |
| }) | |
| const flags = { | |
| ...DEFAULT_GRANT_FLAGS, | |
| ...Object.fromEntries( | |
| Object.entries(request.requestedFlags).filter(([, value]) => value === true), | |
| ), | |
| } | |
| return { | |
| granted, | |
| denied, | |
| flags, | |
| userConsented: true, | |
| } | |
| } | |
| export function ComputerUsePermissionModal({ sessionId, request }: Props) { | |
| const t = useTranslation() | |
| const respondToComputerUsePermission = useChatStore( | |
| (s) => s.respondToComputerUsePermission, | |
| ) | |
| const [openingPane, setOpeningPane] = useState< | |
| 'Privacy_Accessibility' | 'Privacy_ScreenCapture' | null | |
| >(null) | |
| const requestedFlags = useMemo( | |
| () => | |
| request | |
| ? Object.entries(request.requestedFlags) | |
| .filter(([, enabled]) => enabled) | |
| .map(([flag]) => flag) | |
| : [], | |
| [request], | |
| ) | |
| if (!request) return null | |
| const handleDeny = () => { | |
| respondToComputerUsePermission( | |
| sessionId, | |
| request.requestId, | |
| denyAllResponse(), | |
| ) | |
| } | |
| const handleAllow = () => { | |
| respondToComputerUsePermission( | |
| sessionId, | |
| request.requestId, | |
| buildAllowResponse(request), | |
| ) | |
| } | |
| const openSettings = async ( | |
| pane: 'Privacy_Accessibility' | 'Privacy_ScreenCapture', | |
| ) => { | |
| setOpeningPane(pane) | |
| try { | |
| await computerUseApi.openSettings(pane) | |
| } finally { | |
| setOpeningPane(null) | |
| } | |
| } | |
| const tccState = request.tccState | |
| return ( | |
| <Modal | |
| open | |
| onClose={handleDeny} | |
| title={ | |
| tccState | |
| ? t('computerUseApproval.titleTcc') | |
| : t('computerUseApproval.titleApps') | |
| } | |
| width={640} | |
| footer={ | |
| tccState ? ( | |
| <Button variant="ghost" onClick={handleDeny}> | |
| {t('computerUseApproval.deny')} | |
| </Button> | |
| ) : ( | |
| <> | |
| <Button variant="ghost" onClick={handleDeny}> | |
| {t('computerUseApproval.deny')} | |
| </Button> | |
| <Button variant="primary" onClick={handleAllow}> | |
| {t('computerUseApproval.allow')} | |
| </Button> | |
| </> | |
| ) | |
| } | |
| > | |
| {tccState ? ( | |
| <div className="space-y-4"> | |
| <p className="text-sm text-[var(--color-text-secondary)]"> | |
| {t('computerUseApproval.tccHint')} | |
| </p> | |
| <div className="space-y-3"> | |
| <PermissionRow | |
| label={t('computerUseApproval.accessibility')} | |
| granted={tccState.accessibility} | |
| actionLabel={t('computerUseApproval.openAccessibility')} | |
| actionLoading={openingPane === 'Privacy_Accessibility'} | |
| onAction={() => openSettings('Privacy_Accessibility')} | |
| /> | |
| <PermissionRow | |
| label={t('computerUseApproval.screenRecording')} | |
| granted={tccState.screenRecording} | |
| actionLabel={t('computerUseApproval.openScreenRecording')} | |
| actionLoading={openingPane === 'Privacy_ScreenCapture'} | |
| onAction={() => openSettings('Privacy_ScreenCapture')} | |
| /> | |
| </div> | |
| <div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-container-low)] p-3 text-xs text-[var(--color-text-tertiary)]"> | |
| {t('computerUseApproval.tryAgainHint')} | |
| </div> | |
| <div className="flex justify-end"> | |
| <Button variant="secondary" onClick={handleDeny}> | |
| {t('computerUseApproval.tryAgain')} | |
| </Button> | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="space-y-4"> | |
| {request.reason ? ( | |
| <div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-container-low)] p-3"> | |
| <div className="text-xs font-semibold uppercase tracking-wide text-[var(--color-text-tertiary)]"> | |
| {t('computerUseApproval.reason')} | |
| </div> | |
| <div className="mt-1 text-sm text-[var(--color-text-primary)]"> | |
| {request.reason} | |
| </div> | |
| </div> | |
| ) : null} | |
| <div className="space-y-2"> | |
| {request.apps.map((app) => { | |
| const resolved = app.resolved | |
| return ( | |
| <div | |
| key={resolved?.bundleId ?? app.requestedName} | |
| className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-container-low)] p-3" | |
| > | |
| <div className="flex items-start justify-between gap-3"> | |
| <div> | |
| <div className="text-sm font-semibold text-[var(--color-text-primary)]"> | |
| {resolved?.displayName ?? app.requestedName} | |
| </div> | |
| <div className="mt-1 text-xs text-[var(--color-text-tertiary)]"> | |
| {resolved?.bundleId ?? t('computerUseApproval.notInstalled')} | |
| </div> | |
| </div> | |
| <span className="rounded-full bg-[var(--color-surface-container)] px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-[var(--color-text-secondary)]"> | |
| {app.proposedTier} | |
| </span> | |
| </div> | |
| {!resolved ? ( | |
| <p className="mt-2 text-xs text-[var(--color-error)]"> | |
| {t('computerUseApproval.notInstalled')} | |
| </p> | |
| ) : null} | |
| {app.alreadyGranted ? ( | |
| <p className="mt-2 text-xs text-[var(--color-success)]"> | |
| {t('computerUseApproval.alreadyGranted')} | |
| </p> | |
| ) : null} | |
| {app.isSentinel ? ( | |
| <p className="mt-2 text-xs text-[var(--color-warning)]"> | |
| {t('computerUseApproval.sensitiveApp')} | |
| </p> | |
| ) : null} | |
| </div> | |
| ) | |
| })} | |
| </div> | |
| {requestedFlags.length > 0 ? ( | |
| <div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-container-low)] p-3"> | |
| <div className="text-xs font-semibold uppercase tracking-wide text-[var(--color-text-tertiary)]"> | |
| {t('computerUseApproval.alsoRequested')} | |
| </div> | |
| <div className="mt-2 flex flex-wrap gap-2"> | |
| {requestedFlags.map((flag) => ( | |
| <span | |
| key={flag} | |
| className="rounded-full bg-[var(--color-surface-container)] px-2 py-1 text-[11px] text-[var(--color-text-secondary)]" | |
| > | |
| {flag} | |
| </span> | |
| ))} | |
| </div> | |
| </div> | |
| ) : null} | |
| {request.willHide && request.willHide.length > 0 ? ( | |
| <div className="rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-container-low)] p-3 text-sm text-[var(--color-text-secondary)]"> | |
| {request.autoUnhideEnabled | |
| ? t('computerUseApproval.hideWhileWorkingRestore', { | |
| count: request.willHide.length, | |
| }) | |
| : t('computerUseApproval.hideWhileWorking', { | |
| count: request.willHide.length, | |
| })} | |
| </div> | |
| ) : null} | |
| </div> | |
| )} | |
| </Modal> | |
| ) | |
| } | |
| function PermissionRow({ | |
| label, | |
| granted, | |
| actionLabel, | |
| actionLoading, | |
| onAction, | |
| }: { | |
| label: string | |
| granted: boolean | |
| actionLabel: string | |
| actionLoading: boolean | |
| onAction: () => void | |
| }) { | |
| const t = useTranslation() | |
| return ( | |
| <div className="flex items-center justify-between gap-4 rounded-[var(--radius-lg)] border border-[var(--color-border)] bg-[var(--color-surface-container-low)] p-3"> | |
| <div> | |
| <div className="text-sm font-semibold text-[var(--color-text-primary)]"> | |
| {label} | |
| </div> | |
| <div className="mt-1 text-xs text-[var(--color-text-tertiary)]"> | |
| {granted | |
| ? t('computerUseApproval.granted') | |
| : t('computerUseApproval.notGranted')} | |
| </div> | |
| </div> | |
| {!granted ? ( | |
| <Button | |
| variant="secondary" | |
| size="sm" | |
| loading={actionLoading} | |
| onClick={onAction} | |
| > | |
| {actionLabel} | |
| </Button> | |
| ) : null} | |
| </div> | |
| ) | |
| } | |