Files
humanlayer/humanlayer-wui/docs/DEVELOPER_GUIDE.md
Allison Durham 11a988a068 add TypeScript types, React hooks, and UI components for web interface (for sundeep) (#214)
* refactor: reorganize daemon client to mirror Rust architecture

- Split monolithic daemon-client.ts into organized modules:
  - lib/daemon/types.ts: All TypeScript type definitions
  - lib/daemon/client.ts: DaemonClient implementation
  - lib/daemon/errors.ts: Error types
  - lib/daemon/validation.ts: Type guards and validation helpers
  - lib/daemon/index.ts: Controlled public exports
- Matches Rust daemon_client module structure for consistency
- Improves maintainability and separation of concerns

* feat: add React hooks layer for daemon interactions

- useApprovals: Manage approval requests with data enrichment
- useSessions: List and launch Claude Code sessions
- useConversation: View conversation history
- useDaemonConnection: Monitor daemon health
- Includes real-time updates via polling
- Provides loading states, error handling, and refresh functions
- Hooks handle all complexity, components just render

* feat: add UI utilities and type definitions

- UI types: UnifiedApprovalRequest for display-friendly data
- Data enrichment: Join approvals with session context
- Formatting utilities: truncate, formatTimestamp, formatParameters
- Error formatting: Convert technical errors to user-friendly messages
- Separation of UI concerns from protocol implementation

* docs: improve documentation structure and developer guides

- Update README to be concise with development section first
- Add comprehensive documentation:
  - ARCHITECTURE.md: System design with mermaid diagrams
  - DEVELOPER_GUIDE.md: Best practices and do's/don'ts
  - API.md: React hooks reference
- Clear guidance on which layer to use (hooks vs daemon client)
- Examples showing correct usage patterns for frontend developers

* formatting

* feat: add real-time subscription support and example components

- Update daemon client to support event callbacks for subscriptions
- Implement proper real-time updates in useApprovalsWithSubscription hook
- Add ApprovalsPanel component using shadcn/ui components
- Fix linting errors with proper eslint comments
- Update App.tsx imports to use new daemon module path
- Fix TypeScript return types in hooks

* feat(wui): add missing shadcn/ui components

Add card, badge, alert, and collapsible components from shadcn/ui
to resolve TypeScript import errors in ApprovalsPanel

* add react
2025-06-16 12:10:41 -07:00

4.0 KiB

Developer Guide

For Frontend Developers

This guide helps you build UI components that interact with the HumanLayer daemon.

DO

Use Hooks for Everything

import { useApprovals, useSessions } from '@/hooks'

function MyComponent() {
  const { approvals, loading, error, approve, deny } = useApprovals()
  const { sessions, launchSession } = useSessions()

  // Hooks handle all the complexity
}

Use UI Types for Props

import { UnifiedApprovalRequest } from '@/types/ui'

interface Props {
  approval: UnifiedApprovalRequest // ✅ UI type
}

Import Enums When Needed

import { ApprovalType, SessionStatus } from '@/lib/daemon/types'

if (approval.type === ApprovalType.FunctionCall) {
  // This is fine - enums are meant to be used
}

DON'T

Don't Use the Daemon Client Directly

// ❌ WRONG - Never do this in components
import { daemonClient } from '@/lib/daemon'
const approvals = await daemonClient.fetchApprovals()

// ✅ CORRECT - Use hooks instead
const { approvals } = useApprovals()

Don't Use Raw Protocol Types in Components

// ❌ WRONG - FunctionCall is a protocol type
import { FunctionCall } from '@/lib/daemon/types'
interface Props {
  approval: FunctionCall
}

// ✅ CORRECT - Use UI types
import { UnifiedApprovalRequest } from '@/types/ui'
interface Props {
  approval: UnifiedApprovalRequest
}

Common Patterns

Handling Approvals

function ApprovalCard({ approval }: { approval: UnifiedApprovalRequest }) {
  const { approve, deny, respond } = useApprovals()
  const [isProcessing, setIsProcessing] = useState(false)

  const handleApprove = async () => {
    setIsProcessing(true)
    try {
      await approve(approval.callId)
      toast.success('Approved!')
    } catch (error) {
      toast.error(error.message)
    } finally {
      setIsProcessing(false)
    }
  }

  return (
    <Card>
      <h3>{approval.title}</h3>
      <p>{approval.sessionQuery}</p>
      <Button onClick={handleApprove} disabled={isProcessing}>
        Approve
      </Button>
    </Card>
  )
}

Launching Sessions

function LaunchButton() {
  const { launchSession } = useSessions()
  const [query, setQuery] = useState('')

  const handleLaunch = async () => {
    try {
      const { sessionId } = await launchSession({
        query,
        model: 'sonnet',
        working_dir: '/path/to/project',
      })
      navigate(`/sessions/${sessionId}`)
    } catch (error) {
      alert(error.message)
    }
  }

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <button onClick={handleLaunch}>Launch</button>
    </>
  )
}

Real-time Updates

function LiveApprovals() {
  // This hook automatically polls for updates
  const { approvals } = useApprovalsWithSubscription()

  return (
    <div>
      {approvals.map(approval => (
        <ApprovalCard key={approval.id} approval={approval} />
      ))}
    </div>
  )
}

Understanding the Layers

1. Components (Your Code)

  • Import hooks and UI types
  • Handle user interactions
  • Render UI

2. Hooks (React Layer)

  • Manage state with useState
  • Handle loading/error states
  • Enrich data (join approvals + sessions)
  • Format errors for display

3. Daemon Client (Protocol Layer)

  • Type-safe Tauri invocations
  • 1:1 mapping to Rust API
  • No business logic

4. Rust/Daemon

  • Handles actual daemon communication
  • Manages Unix socket connection
  • Protocol implementation

Tips

  • Loading States: All hooks provide loading - use it!
  • Error Handling: Hooks format errors, just display error string
  • Refreshing: Actions like approve() auto-refresh the list
  • Polling: useApprovalsWithSubscription polls every 5 seconds
  • Types: When in doubt, check what the hook returns

Need Help?

  • Check the API Reference for all available hooks
  • Look at existing components for examples
  • The TypeScript compiler will guide you - trust the types!