Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.voicetypr.com/llms.txt

Use this file to discover all available pages before exploring further.

VoiceTypr provides React hooks for checking and requesting macOS system permissions required for recording and text insertion.

Required Permissions

VoiceTypr requires two system permissions:
  1. Microphone - Record audio for transcription
  2. Accessibility - Insert transcribed text at cursor position

usePermissions Hook

Comprehensive hook for managing all permissions.

Import

import { usePermissions } from '@/hooks/usePermissions';
import type { PermissionStatus, PermissionState } from '@/hooks/usePermissions';

Usage

function PermissionsScreen() {
  const {
    permissions,
    checkPermissions,
    requestPermission,
    isChecking,
    isRequesting,
    error,
    allGranted
  } = usePermissions({ checkOnMount: true, showToasts: true });

  return (
    <div>
      <h2>Permissions Status</h2>
      
      {allGranted ? (
        <p className="success">All permissions granted!</p>
      ) : (
        <ul>
          <li>
            Microphone: {permissions.microphone}
            {permissions.microphone === 'denied' && (
              <button onClick={() => requestPermission('microphone')}>
                Request Access
              </button>
            )}
          </li>
          
          <li>
            Accessibility: {permissions.accessibility}
            {permissions.accessibility === 'denied' && (
              <button onClick={() => requestPermission('accessibility')}>
                Request Access
              </button>
            )}
          </li>
        </ul>
      )}

      {error && <p className="error">{error.message}</p>}
      {isChecking && <p>Checking permissions...</p>}
      {isRequesting && <p>Requesting {isRequesting} permission...</p>}
    </div>
  );
}

Options

interface UsePermissionsOptions {
  checkOnMount?: boolean;     // Check permissions on mount (default: true)
  checkInterval?: number;     // Re-check interval in ms (default: 0 = disabled)
  showToasts?: boolean;       // Show toast notifications (default: false)
}
Example:
// Check permissions every 5 seconds and show toasts
const { permissions, allGranted } = usePermissions({
  checkOnMount: true,
  checkInterval: 5000,
  showToasts: true
});

Return Values

permissions

Type: PermissionState
type PermissionStatus = 'checking' | 'granted' | 'denied';

interface PermissionState {
  microphone: PermissionStatus;
  accessibility: PermissionStatus;
}
Current status of each permission.

checkPermissions

Type: () => Promise<void> Manually check all permissions. Usage:
const { checkPermissions } = usePermissions({ checkOnMount: false });

// Check when user returns from System Settings
window.addEventListener('focus', async () => {
  await checkPermissions();
});

requestPermission

Type: (type: 'microphone' | 'accessibility') => Promise<void> Request a specific permission. Parameters:
type
string
required
Permission type: "microphone" or "accessibility"
Usage:
const { requestPermission } = usePermissions();

// Request microphone access
await requestPermission('microphone');

// Request accessibility access
await requestPermission('accessibility');
Behavior:
  1. Invokes backend permission request command
  2. Opens System Settings if permission not granted
  3. Re-checks permissions after 1.5 seconds

isChecking

Type: boolean Whether permissions are currently being checked.

isRequesting

Type: string | null Permission type currently being requested ("microphone", "accessibility", or null).

error

Type: Error | null Last error encountered during check/request.

allGranted

Type: boolean Whether all required permissions are granted. Equivalent to:
const allGranted = 
  permissions.microphone === 'granted' && 
  permissions.accessibility === 'granted';

useMicrophonePermission Hook

Specialized hook for microphone permission only.

Import

import { useMicrophonePermission } from '@/hooks/useMicrophonePermission';

Usage

function MicrophoneCheck() {
  const { 
    hasPermission, 
    isChecking, 
    checkPermission, 
    requestPermission 
  } = useMicrophonePermission();

  if (isChecking) {
    return <LoadingSpinner />;
  }

  if (hasPermission === false) {
    return (
      <div>
        <p>Microphone access is required</p>
        <button onClick={requestPermission}>Grant Access</button>
      </div>
    );
  }

  return <p>Microphone ready</p>;
}

Options

interface MicrophonePermissionOptions {
  checkOnMount?: boolean;  // Default: true
}

Return Values

hasPermission

Type: boolean | null
  • true - Permission granted
  • false - Permission denied
  • null - Not yet checked

isChecking

Type: boolean Whether permission check is in progress.

checkPermission

Type: () => Promise<boolean> Manually check microphone permission. Returns: true if granted, false if denied

requestPermission

Type: () => Promise<boolean> Request microphone permission from the user. Returns: true if granted, false if denied

Events

The hook listens to backend events:
  • microphone-granted - Permission granted
  • microphone-denied - Permission denied

useAccessibilityPermission Hook

Specialized hook for accessibility permission only.

Import

import { useAccessibilityPermission } from '@/hooks/useAccessibilityPermission';

Usage

function AccessibilityCheck() {
  const { 
    hasPermission, 
    isChecking, 
    checkPermission, 
    requestPermission 
  } = useAccessibilityPermission();

  if (isChecking) {
    return <LoadingSpinner />;
  }

  if (hasPermission === false) {
    return (
      <div>
        <p>Accessibility access is required to insert text</p>
        <button onClick={requestPermission}>Grant Access</button>
      </div>
    );
  }

  return <p>Accessibility ready</p>;
}

Options

interface AccessibilityPermissionOptions {
  checkOnMount?: boolean;  // Default: true
}

Return Values

Same as useMicrophonePermission:
  • hasPermission: boolean | null
  • isChecking: boolean
  • checkPermission: () => Promise<boolean>
  • requestPermission: () => Promise<boolean>

Events

  • accessibility-granted - Permission granted
  • accessibility-denied - Permission denied

Component Examples

Permission Onboarding Screen

import { usePermissions } from '@/hooks/usePermissions';
import { Check, X } from 'lucide-react';

function OnboardingPermissions() {
  const { permissions, requestPermission, allGranted, isRequesting } = usePermissions();

  const handleNext = () => {
    if (allGranted) {
      // Proceed to next onboarding step
    }
  };

  return (
    <div className="onboarding">
      <h1>Grant Permissions</h1>
      <p>VoiceTypr needs access to your microphone and accessibility features.</p>

      <ul className="permissions-list">
        <li>
          {permissions.microphone === 'granted' ? (
            <Check className="granted" />
          ) : (
            <X className="denied" />
          )}
          <div>
            <h3>Microphone</h3>
            <p>Record your voice for transcription</p>
            {permissions.microphone === 'denied' && (
              <button 
                onClick={() => requestPermission('microphone')}
                disabled={isRequesting === 'microphone'}
              >
                {isRequesting === 'microphone' ? 'Requesting...' : 'Grant Access'}
              </button>
            )}
          </div>
        </li>

        <li>
          {permissions.accessibility === 'granted' ? (
            <Check className="granted" />
          ) : (
            <X className="denied" />
          )}
          <div>
            <h3>Accessibility</h3>
            <p>Insert transcribed text at cursor position</p>
            {permissions.accessibility === 'denied' && (
              <button 
                onClick={() => requestPermission('accessibility')}
                disabled={isRequesting === 'accessibility'}
              >
                {isRequesting === 'accessibility' ? 'Requesting...' : 'Grant Access'}
              </button>
            )}
          </div>
        </li>
      </ul>

      <button 
        onClick={handleNext}
        disabled={!allGranted}
        className="next-button"
      >
        Continue
      </button>
    </div>
  );
}

Permission Guard

import { usePermissions } from '@/hooks/usePermissions';
import { ReactNode } from 'react';

interface PermissionGuardProps {
  children: ReactNode;
  fallback?: ReactNode;
}

function PermissionGuard({ children, fallback }: PermissionGuardProps) {
  const { allGranted, isChecking, requestPermission } = usePermissions();

  if (isChecking) {
    return <LoadingScreen />;
  }

  if (!allGranted) {
    return fallback || (
      <div className="permission-required">
        <h2>Permissions Required</h2>
        <p>Please grant all permissions to use VoiceTypr</p>
        <button onClick={() => requestPermission('microphone')}>
          Grant Microphone Access
        </button>
        <button onClick={() => requestPermission('accessibility')}>
          Grant Accessibility Access
        </button>
      </div>
    );
  }

  return <>{children}</>;
}

// Usage
function App() {
  return (
    <PermissionGuard>
      <MainApp />
    </PermissionGuard>
  );
}

TypeScript Types

type PermissionStatus = 'checking' | 'granted' | 'denied';

interface PermissionState {
  microphone: PermissionStatus;
  accessibility: PermissionStatus;
}

interface UsePermissionsReturn {
  permissions: PermissionState;
  checkPermissions: () => Promise<void>;
  requestPermission: (type: keyof PermissionState) => Promise<void>;
  isChecking: boolean;
  isRequesting: string | null;
  error: Error | null;
  allGranted: boolean;
}

function usePermissions(options?: {
  checkOnMount?: boolean;
  checkInterval?: number;
  showToasts?: boolean;
}): UsePermissionsReturn;

function useMicrophonePermission(options?: {
  checkOnMount?: boolean;
}): {
  hasPermission: boolean | null;
  isChecking: boolean;
  checkPermission: () => Promise<boolean>;
  requestPermission: () => Promise<boolean>;
};

function useAccessibilityPermission(options?: {
  checkOnMount?: boolean;
}): {
  hasPermission: boolean | null;
  isChecking: boolean;
  checkPermission: () => Promise<boolean>;
  requestPermission: () => Promise<boolean>;
};

See Also