# @hiyve/react-collaboration

Chat, polls, Q&A, and file management components for Hiyve.

## Installation

```bash
npx @hiyve/cli login
npm install @hiyve/react-collaboration
```

## Quick Start

```tsx
import { ChatPanel, PollsWindow, QAPanel, FileManager } from '@hiyve/react-collaboration';
import { HiyveProvider } from '@hiyve/react';

function CollaborationSidebar() {
  return (
    <HiyveProvider generateRoomToken={generateRoomToken}>
      <ChatPanel maxHeight={400} />
      <PollsWindow />
      <QAPanel />
      <FileManager />
    </HiyveProvider>
  );
}
```

## Components

### Chat

| Component | Description |
|-----------|-------------|
| `ChatPanel` | Full chat interface with message list, input, and avatars |
| `ChatMessageList` | Scrollable message list with auto-scroll, avatars, and timestamps. Can be used standalone or composed inside `ChatPanel`/`ChatHub` |
| `ChatInput` | Message input with send button, character limit, and validation. Can be used standalone or composed inside `ChatPanel`/`ChatHub` |

### Chat Hub

| Component | Description |
|-----------|-------------|
| `ChatHub` | Multi-channel chat layout with channel sidebar and message area. Responsive: side-by-side on desktop, stacked on mobile |
| `ChatChannelList` | Searchable, groupable list of chat channels with unread badges and online indicators |

### Polls

| Component | Description |
|-----------|-------------|
| `PollsWindow` | Complete polling interface with creation, voting, and results |
| `ActivePoll` | Displays a single active poll for voting |
| `PollCreator` | Form for creating new polls |
| `PollEditor` | Form for editing existing polls |
| `PollHeader` | Header for an individual poll |
| `PollHistory` | List of past polls |
| `PollOption` | Single poll option with vote count and percentage |
| `PollResultsViewer` | Detailed results view for a completed poll |
| `SimplePollResults` | Compact results summary |
| `PollsSession` | Manages poll session lifecycle with room storage |
| `PollsSessionViewer` | Read-only viewer for previously saved poll sessions |
| `PollsWindowHeader` | Header for the polls window |

### Q&A

| Component | Description |
|-----------|-------------|
| `QAPanel` | Complete Q&A interface with questions, answers, and voting |
| `AnswerSection` | Displays answers for a question |
| `QuestionInput` | Input form for submitting questions |
| `QuestionItem` | Single question with vote count and answer toggle |
| `QuestionList` | Sorted, filterable list of questions |
| `QAPanelHeader` | Header for the Q&A panel |
| `QASession` | Manages Q&A session lifecycle with room storage |
| `QASessionViewer` | Read-only viewer for previously saved Q&A sessions |

### File Manager

| Component | Description |
|-----------|-------------|
| `FileManager` | Full file browser with upload, download, folders, and sharing |
| `FileViewer` | Preview and playback for files (audio, video, images, documents) |
| `MediaFileViewer` | Enhanced media player for audio/video with waveform, playback controls, and region support. Requires `@hiyve/react-media-player` |
| `FileIcon` | File type icon with customizable icon and color maps |
| `FileManagerToolbar` | Toolbar with search, view toggle, upload, and custom actions |
| `FileManagerFilterChips` | Horizontal filter chip bar for filtering files by resource type |
| `FileTableView` | Table/list layout view for rendering files and folders as rows |
| `FileCardView` | Card/grid layout view for rendering files and folders as cards |
| `RoomSummaryViewer` | Displays room summary data (participants, recordings, files) |
| `FileListSkeleton` | Loading skeleton for the file list |
| `FileCacheProvider` | Provides a shared file tree cache to child components |
| `CreateFolderDialog` | Dialog for creating new folders |
| `RenameFileDialog` | Dialog for renaming files |
| `DeleteConfirmDialog` | Confirmation dialog for file deletion |
| `FileSharingDialog` | Dialog for configuring file sharing |

### Room File Scope

| Component | Description |
|-----------|-------------|
| `RoomFileScope` | Wraps `FileManager` to filter files by the current room. Place between `FileCacheProvider` and `FileManager` |

```tsx
<FileCacheProvider>
  <RoomFileScope>
    <FileManager />
  </RoomFileScope>
</FileCacheProvider>
```

**RoomFileScope Props**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `roomName` | `string` | — | Room name to filter by. Defaults to the active room from `HiyveProvider` |
| `defaultShowAll` | `boolean` | `false` | Start with all files visible instead of room-only |
| `disableToggle` | `boolean` | `false` | Disable the room/all files toggle |
| `children` | `ReactNode` | — | Child components |

**useRoomFileScope Hook**

Returns the current room file scope context, or `null` when not inside a `RoomFileScope`.

| Property | Type | Description |
|----------|------|-------------|
| `roomName` | `string \| null` | Active room name |
| `showRoomFilesOnly` | `boolean` | Whether filtering is active |
| `setShowRoomFilesOnly` | `(value: boolean \| ((prev: boolean) => boolean)) => void` | Toggle room filtering |
| `disableToggle` | `boolean` | Whether the toggle is disabled |

### Session Common

| Component | Description |
|-----------|-------------|
| `SessionEmptyState` | Empty state display for session-based features |
| `SessionHeader` | Shared header for session panels (notes, polls, Q&A) |

## ChatPanel Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `title` | `string` | `'Chat'` | Header title |
| `showHeader` | `boolean` | `true` | Show the header |
| `showTimestamps` | `boolean` | `true` | Show timestamps on messages |
| `showAvatars` | `boolean` | `true` | Show user avatars |
| `placeholder` | `string` | `'Type a message...'` | Input placeholder text |
| `disabled` | `boolean` | `false` | Disable the input |
| `maxHeight` | `string \| number` | — | Maximum height before scrolling |
| `onNewMessage` | `(message: ChatMessage) => void` | — | Called when a remote message arrives |
| `onFocus` | `() => void` | — | Called when the chat gains focus |
| `labels` | `Partial<ChatPanelLabels>` | — | Custom text labels |
| `icons` | `Partial<ChatPanelIcons>` | — | Custom icons |
| `colors` | `Partial<ChatPanelColors>` | — | Custom color values |
| `styles` | `Partial<ChatPanelStyles>` | — | Custom style values |
| `renderProps` | `ChatPanelRenderProps` | — | Render props for advanced customization |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## ChatMessageList Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `messages` | `ChatMessage[]` | — | Array of messages to display |
| `localUserId` | `string` | — | The local user's ID (for distinguishing own messages) |
| `showTimestamps` | `boolean` | `true` | Show timestamps on messages |
| `showAvatars` | `boolean` | `true` | Show avatars for messages |
| `colors` | `Partial<ChatPanelColors>` | — | Custom color values |
| `styles` | `Partial<ChatPanelStyles>` | — | Custom style values |
| `labels` | `Partial<ChatPanelLabels>` | — | Custom text labels |
| `renderProps` | `ChatPanelRenderProps` | — | Render props for advanced customization |
| `onError` | `(error: Error) => void` | — | Called when an error occurs (e.g., timestamp formatting) |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## ChatInput Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `onSend` | `(content: string) => void` | — | Called when user sends a message |
| `disabled` | `boolean` | `false` | Whether the input is disabled |
| `placeholder` | `string` | `'Type a message...'` | Placeholder text for the input field |
| `colors` | `Partial<ChatPanelColors>` | — | Custom color values |
| `styles` | `Partial<ChatPanelStyles>` | — | Custom style values |
| `icons` | `Partial<ChatPanelIcons>` | — | Custom icons |
| `maxLength` | `number` | `CHAT_CONFIG.MAX_MESSAGE_LENGTH` | Maximum message length |
| `onError` | `(error: Error) => void` | — | Called when an error occurs during message send |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## ChatHub Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `channels` | `ChatChannel[]` | — | Array of channels to display in the sidebar |
| `activeChannelId` | `string \| null` | — | Currently active channel ID |
| `messages` | `ChatMessage[]` | — | Messages for the active channel |
| `localUserId` | `string` | — | The local user's ID |
| `isLoadingHistory` | `boolean` | `false` | Whether history is loading |
| `onChannelSelect` | `(channelId: string) => void` | — | Called when a channel is selected |
| `onSend` | `(content: string) => void` | — | Called when a message is sent |
| `onLoadMore` | `() => void` | — | Called to load more history |
| `labels` | `Partial<ChatHubLabels>` | — | Custom text labels |
| `colors` | `Partial<ChatHubColors>` | — | Custom color values |
| `channelListProps` | `Partial<ChatChannelListProps>` | — | Props forwarded to the channel list |
| `messageListProps` | `Partial<ChatMessageListProps>` | — | Props forwarded to the message list |
| `inputProps` | `Partial<ChatInputProps>` | — | Props forwarded to the input |
| `channelListWidth` | `number` | `300` | Width of the channel list panel in pixels |
| `renderMessageHeader` | `(channel: ChatChannel \| undefined) => ReactNode` | — | Custom render for the message area header |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

### Quick Start: Multi-Channel Chat

```tsx
import { ChatHub } from '@hiyve/react-collaboration';

const channels = [
  { id: 'general', name: 'General', unreadCount: 3, lastMessagePreview: 'Hello!' },
  { id: 'design', name: 'Design Team', unreadCount: 0 },
];

function MultiChannelChat() {
  const [activeId, setActiveId] = useState<string | null>('general');
  const [messages, setMessages] = useState<ChatMessage[]>([]);

  return (
    <ChatHub
      channels={channels}
      activeChannelId={activeId}
      messages={messages}
      localUserId="user-123"
      onChannelSelect={setActiveId}
      onSend={(content) => handleSendMessage(activeId, content)}
      channelListProps={{ showSearch: true }}
    />
  );
}
```

## ChatChannelList Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `channels` | `ChatChannel[]` | — | Array of channels to display |
| `activeChannelId` | `string \| null` | — | Currently active channel ID |
| `onChannelSelect` | `(channelId: string) => void` | — | Called when a channel is selected |
| `showSearch` | `boolean` | `false` | Show search filter |
| `groupBy` | `(channel: ChatChannel) => string` | — | Group channels by a key derived from each channel |
| `labels` | `Partial<ChatChannelListLabels>` | — | Custom text labels |
| `colors` | `Partial<ChatChannelListColors>` | — | Custom color values |
| `renderChannelItem` | `(channel: ChatChannel, isActive: boolean, defaultContent: ReactNode) => ReactNode` | — | Custom render for a channel item |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## PollsWindow Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `title` | `string` | — | Header title |
| `showHeader` | `boolean` | `true` | Show the header |
| `maxHeight` | `string \| number` | — | Maximum height before scrolling |
| `disabled` | `boolean` | `false` | Disable interaction |
| `readOnly` | `boolean` | `false` | Hide create button and disable voting |
| `ownerOnlyCreate` | `boolean` | `false` | Only room owner can create polls |
| `initialPolls` | `Poll[]` | — | Initial polls to display |
| `enableAutoSave` | `boolean` | `false` | Enable auto-save to file storage |
| `autoSaveInterval` | `number` | — | Auto-save interval in milliseconds |
| `fileLocation` | `string` | — | File storage location |
| `initialFileId` | `string` | — | Existing file ID for resuming auto-save |
| `onPollCreated` | `(poll: Poll) => void` | — | Called when a poll is created |
| `onPollStarted` | `(poll: Poll) => void` | — | Called when a poll is started |
| `onPollEnded` | `(poll: Poll) => void` | — | Called when a poll is ended |
| `onPollDeleted` | `(pollId: string) => void` | — | Called when a poll is deleted |
| `onVoteCast` | `(pollId: string, optionIds: string[]) => void` | — | Called when user votes |
| `onPollsChange` | `(polls: Poll[]) => void` | — | Called when polls change |
| `onAutoSave` | `(fileId: string) => void` | — | Called after auto-save completes |
| `onAutoSaveError` | `(error: Error) => void` | — | Called on auto-save error |
| `labels` | `Partial<PollsWindowLabels>` | — | Custom text labels |
| `icons` | `Partial<PollsWindowIcons>` | — | Custom icons |
| `colors` | `Partial<PollsWindowColors>` | — | Custom color values |
| `styles` | `Partial<PollsWindowStyles>` | — | Custom style values |
| `renderProps` | `PollsWindowRenderProps` | — | Render props for advanced customization |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## QAPanel Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `title` | `string` | `'Q&A'` | Header title |
| `showHeader` | `boolean` | `true` | Show the header |
| `maxHeight` | `string \| number` | — | Maximum height before scrolling |
| `disabled` | `boolean` | `false` | Disable interaction |
| `readOnly` | `boolean` | `false` | Hide question input and disable interaction |
| `ownerOnlyModerate` | `boolean` | `true` | Only room owner can answer and delete |
| `allowAnonymous` | `boolean` | `false` | Hide author names on questions |
| `initialQuestions` | `Question[]` | — | Initial questions to display |
| `enableAutoSave` | `boolean` | `false` | Enable auto-save to file storage |
| `autoSaveInterval` | `number` | `3000` | Auto-save interval in milliseconds |
| `fileLocation` | `string` | `'/Questions'` | File storage location |
| `initialFileId` | `string` | — | Existing file ID for resuming auto-save |
| `onQuestionPosted` | `(question: Question) => void` | — | Called when a question is posted |
| `onQuestionAnswered` | `(question: Question) => void` | — | Called when a question is answered |
| `onNewQuestion` | `(question: Question) => void` | — | Called when a remote question arrives |
| `onQuestionsChange` | `(questions: Question[]) => void` | — | Called when questions change |
| `onAutoSave` | `(fileId: string) => void` | — | Called after auto-save completes |
| `onAutoSaveError` | `(error: Error) => void` | — | Called on auto-save error |
| `labels` | `Partial<QAPanelLabels>` | — | Custom text labels |
| `icons` | `Partial<QAPanelIcons>` | — | Custom icons |
| `colors` | `Partial<QAPanelColors>` | — | Custom color values |
| `styles` | `Partial<QAPanelStyles>` | — | Custom style values |
| `renderProps` | `QAPanelRenderProps` | — | Render props for advanced customization |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## FileManager Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `files` | `FileEntry[]` | — | Files in current folder (auto-wired inside `FileCacheProvider`) |
| `folders` | `FolderEntry[]` | — | Subfolders in current folder (auto-wired inside `FileCacheProvider`) |
| `currentPath` | `string` | — | Current folder path (auto-wired inside `FileCacheProvider`) |
| `isLoading` | `boolean` | — | Whether files are loading (auto-wired inside `FileCacheProvider`) |
| `userId` | `string` | — | Current user's ID (auto-wired inside `FileCacheProvider`) |
| `isRoomOwner` | `boolean` | — | Whether user is room owner |
| `roomName` | `string` | — | Room name |
| `showToolbar` | `boolean` | `true` | Show the toolbar |
| `showBreadcrumbs` | `boolean` | `true` | Show breadcrumb navigation |
| `enableDragDrop` | `boolean` | `true` | Enable drag-and-drop file moving |
| `enableMultiSelect` | `boolean` | `true` | Enable multi-file selection |
| `enableSmartOpening` | `boolean` | `false` | Route files to their native tabs (whiteboard, notes, etc.) |
| `availableTabs` | `string[]` | — | Available tabs for smart opening |
| `filterFileTypes` | `ResourceType[]` | — | Filter to specific file types (forces flat view) |
| `columns` | `FileListColumn[]` | — | Columns to display |
| `showOnlySharedFiles` | `boolean` | `false` | Show only files shared with user |
| `showBothOwnedAndShared` | `boolean` | `false` | Show both owned and shared files |
| `customAction` | `CustomToolbarAction \| null` | — | Custom toolbar action button |
| `isVisible` | `boolean` | — | Whether component is visible (for lazy loading) |
| `refresh` | `number` | — | Increment to trigger refresh |
| `resourceViewerTypes` | `{ add?, remove?, replace? }` | — | Override which resource types open in inline viewer |
| `customViewers` | `CustomViewerMap` | — | Custom viewer renderers for specific resource types |
| `fileNameFormatter` | `(file: FileEntry) => string` | — | Custom file name formatter |
| `onNavigate` | `(path: string) => void` | — | Navigate to folder (auto-wired inside `FileCacheProvider`) |
| `onFileOpen` | `(file: FileEntry) => void` | — | Called when a file is double-clicked |
| `onRefresh` | `() => void` | — | Refresh file list (auto-wired inside `FileCacheProvider`) |
| `onUpload` | `(file: File, location: string) => Promise<void>` | — | Upload handler |
| `onDownload` | `(files: FileEntry[]) => Promise<void>` | — | Download handler |
| `onDelete` | `(files: FileEntry[]) => Promise<void>` | — | Delete handler |
| `onDeleteFolder` | `(folder: FolderEntry) => Promise<void>` | — | Delete folder handler |
| `onCreateFolder` | `(name: string, parentPath: string) => Promise<void>` | — | Create folder handler |
| `onRename` | `(file: FileEntry, newName: string) => Promise<void>` | — | Rename handler |
| `onShare` | `(files: FileEntry[], userIds: string[]) => Promise<void>` | — | Share handler |
| `onMove` | `(files: FileEntry[], newLocation: string) => Promise<void>` | — | Move handler |
| `onGetFileUrl` | `(file: FileEntry) => Promise<string>` | — | Get presigned URL for viewing |
| `onFetchFileTypes` | `() => Promise<string[]>` | — | Get available file types for filtering |
| `onGetAttendees` | `() => Promise<Array<{ userId: string; userName?: string }>>` | — | Get attendees for sharing dialog |
| `onSwitchToTab` | `(tabName: string, file: FileEntry) => void` | — | Called when smart opening routes to a tab |
| `onFileEdit` | `(file: FileEntry) => void` | — | Called when a file should be opened in an editor (prevents fallback viewer) |
| `onSelect` | `(selection: { fileId: string; filename: string }) => void` | — | Called when a file is selected |
| `onFileCountChange` | `(count: number) => void` | — | Called when file count changes |
| `isUploading` | `boolean` | — | Whether an upload is in progress (auto-wired inside `FileCacheProvider`) |
| `isDownloading` | `boolean` | — | Whether a download is in progress (auto-wired inside `FileCacheProvider`) |
| `isRefreshing` | `boolean` | — | Whether a refresh is in progress (auto-wired inside `FileCacheProvider`) |
| `sortStorageKey` | `string` | — | Key for persisting sort state across sessions |
| `labels` | `Partial<FileManagerLabels>` | — | Custom text labels |
| `icons` | `Partial<FileManagerIcons>` | — | Custom icons |
| `colors` | `Partial<FileManagerColors>` | — | Custom color values |
| `styles` | `Partial<FileManagerStyles>` | — | Custom style values |
| `renderProps` | `FileManagerRenderProps` | — | Render props for advanced customization |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## FileCacheProvider Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | — | Child components |
| `files` | `FileEntry[]` | — | When provided, enables library mode. The provider derives the file tree from this flat list instead of using HiyveProvider |
| `onUploadFile` | `(file: File, location: string) => Promise<FileEntry \| null>` | — | Upload handler (library mode) |
| `onDeleteFile` | `(fileId: string) => Promise<boolean>` | — | Delete handler (library mode) |
| `onCreateFolder` | `(name: string, parentPath: string) => Promise<boolean>` | — | Create folder handler (library mode) |
| `onDeleteFolder` | `(path: string) => Promise<boolean>` | — | Delete folder handler (library mode) |
| `onMoveFile` | `(fileId: string, newPath: string) => Promise<boolean>` | — | Move file handler (library mode) |
| `onRenameFile` | `(fileId: string, newName: string) => Promise<boolean>` | — | Rename file handler (library mode) |
| `onShareFile` | `(files: FileEntry[], userIds: string[]) => Promise<boolean>` | — | Share files handler (library mode) |
| `onGetFileUrl` | `(file: FileEntry) => Promise<string \| null>` | — | Get file URL handler (library mode) |
| `onRefresh` | `() => void` | — | Refresh handler called after mutations (library mode) |

**Room mode** (default): Place inside `HiyveProvider`. Automatically manages the file cache when a room is joined.

**Library mode**: Provide the `files` prop and `on*` callbacks to use without `HiyveProvider`.

## FileViewer Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `open` | `boolean` | — | Whether viewer is open |
| `onClose` | `() => void` | — | Close viewer handler |
| `file` | `FileEntry \| null` | — | File being viewed |
| `fileUrl` | `string` | — | Presigned URL for file content |
| `loading` | `boolean` | — | Whether file is loading |
| `error` | `string \| null` | — | Error message if loading failed |
| `parentRef` | `React.RefObject<HTMLElement>` | — | Reference to parent container (for positioning) |
| `onFileOpen` | `(file: FileEntry) => void` | — | Called to open another file (for linked files) |
| `customViewers` | `CustomViewerMap` | — | Custom viewer renderers for specific resource types |
| `enableRegions` | `boolean` | `true` | Enable region creation and loop playback for audio/video |
| `regions` | `Array<{ id, start, end, content, channelIdx? }>` | — | Initial named regions to load |
| `onRegionChange` | `(region: { id, start, end, content, channelIdx? }) => void` | — | Called when a region is created or updated |
| `onRegionDelete` | `(regionId: string) => void` | — | Called when a region is deleted |
| `labels` | `Partial<{ loading, error, noPreview, close, pages, noInlinePreview, openInDedicatedTab }>` | — | Custom text labels |
| `colors` | `Partial<{ background, headerBackground, headerText, loadingIndicator, errorText }>` | — | Custom color values |
| `styles` | `Partial<{ headerHeight, maxContentWidth, padding }>` | — | Custom style values |

## MediaFileViewer Props

Enhanced media player for audio/video files. Requires `@hiyve/react-media-player`.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `src` | `string` | — | Media URL to play |
| `file` | `FileEntry` | — | File entry for media type detection |
| `onError` | `(error: Error) => void` | — | Error callback |
| `enableWaveform` | `boolean` | `true` | Enable waveform visualization |
| `enableRegions` | `boolean` | `false` | Enable region management |
| `enableVolumeControl` | `boolean` | `true` | Enable volume control |
| `enableRateControl` | `boolean` | `true` | Enable playback rate control |
| `enableKeyboardShortcuts` | `boolean` | `true` | Enable keyboard shortcuts |
| `regions` | `Array<{ id, start, end, content, channelIdx? }>` | — | Initial named regions to load |
| `onRegionChange` | `(region: { id, start, end, content, channelIdx? }) => void` | — | Called when a region is created or updated |
| `onRegionDelete` | `(regionId: string) => void` | — | Called when a region is deleted |
| `labels` | `Partial<MediaFileViewerLabels>` | — | Custom text labels |
| `colors` | `Partial<MediaFileViewerColors>` | — | Custom color values |
| `mediaPlayerLabels` | `Partial<Record<string, string>>` | — | Labels forwarded to the inner MediaPlayer |
| `mediaPlayerColors` | `Partial<Record<string, string>>` | — | Colors forwarded to the inner MediaPlayer |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## FileIcon Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `resourceType` | `ResourceType` | — | Resource type to display icon for |
| `size` | `number` | `24` | Icon size in pixels |
| `icons` | `Partial<FileIconMap>` | — | Custom icon mapping |
| `colors` | `Partial<FileIconColorMap>` | — | Custom color mapping |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## RoomSummaryViewer Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `summaryData` | `RoomSummaryData` | — | Room summary data to display |
| `file` | `FileEntry \| null` | — | File entry for the summary |
| `onClose` | `() => void` | — | Close handler |
| `onFileOpen` | `(file: RoomSummaryFile) => void` | — | Called to open a file from the summary |
| `getFilesByType` | `(type: string) => Promise<FileEntry[]>` | — | Get files by type from cache |
| `onEnrichSummary` | `(fileId: string) => Promise<RoomSummaryData \| null>` | — | Enrich summary with additional data |
| `labels` | `Partial<RoomSummaryViewerLabels>` | — | Custom text labels |
| `colors` | `Partial<RoomSummaryViewerColors>` | — | Custom color values |
| `sx` | `SxProps<Theme>` | — | MUI sx styling prop |

## Dialog Props

### CreateFolderDialog

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `open` | `boolean` | — | Whether dialog is open |
| `onClose` | `() => void` | — | Close dialog handler |
| `onCreate` | `(folderName: string) => Promise<void>` | — | Create folder handler |
| `parentPath` | `string` | — | Parent path for new folder |
| `isCreating` | `boolean` | — | Whether creation is in progress |
| `labels` | `Partial<{ title, placeholder, create, cancel, error }>` | — | Custom text labels |

### RenameFileDialog

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `open` | `boolean` | — | Whether dialog is open |
| `onClose` | `() => void` | — | Close dialog handler |
| `onRename` | `(newName: string) => Promise<void>` | — | Rename handler |
| `file` | `FileEntry \| null` | — | File being renamed |
| `isRenaming` | `boolean` | — | Whether rename is in progress |
| `labels` | `Partial<{ title, placeholder, rename, cancel, error }>` | — | Custom text labels |

### DeleteConfirmDialog

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `open` | `boolean` | — | Whether dialog is open |
| `onClose` | `() => void` | — | Close dialog handler |
| `onConfirm` | `() => Promise<void>` | — | Confirm delete handler |
| `items` | `Array<FileEntry \| FolderEntry>` | — | Items being deleted |
| `isDeleting` | `boolean` | — | Whether deletion is in progress |
| `labels` | `Partial<{ title, message, messagePlural, confirm, cancel }>` | — | Custom text labels |

### FileSharingDialog

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `open` | `boolean` | — | Whether dialog is open |
| `onClose` | `() => void` | — | Close dialog handler |
| `onShare` | `(userIds: string[]) => Promise<void>` | — | Share handler |
| `files` | `FileEntry[]` | — | Files being shared |
| `availableUsers` | `Array<{ userId: string; userName?: string }>` | — | Available users to share with |
| `isSharing` | `boolean` | — | Whether sharing is in progress |
| `labels` | `Partial<{ title, titlePlural, selectUsers, share, cancel, noUsers }>` | — | Custom text labels |

## Hooks

### Polls

| Hook | Description |
|------|-------------|
| `usePollState` | Manages local poll state (create, vote, end, delete) |
| `usePollSync` | Synchronizes polls across participants in real-time |
| `usePollPersistence` | Saves and loads poll sessions to/from room storage |
| `usePollListener` | Listens for incoming poll updates from other participants |

### Q&A

| Hook | Description |
|------|-------------|
| `useQAState` | Manages local Q&A state (post, answer, vote, delete) |
| `useQASync` | Synchronizes Q&A across participants in real-time |
| `useQAPersistence` | Saves and loads Q&A sessions to/from room storage |
| `useQAListener` | Listens for incoming Q&A updates from other participants |
| `useQASessionState` | Manages Q&A session lifecycle state |

### File Manager

| Hook | Description |
|------|-------------|
| `useFileCache` | Accesses the shared file tree cache (requires `FileCacheProvider`) |
| `useFileCacheSafe` | Same as `useFileCache` but returns `null` outside `FileCacheProvider` |
| `useFileNavigation` | Manages folder navigation with breadcrumb history |
| `useFileOperations` | Provides upload, download, rename, delete, and folder operations |
| `useFileDragDrop` | Handles drag-and-drop file uploads |
| `useFileDialogs` | Manages open/close state for file dialogs |
| `useFileSelection` | Tracks selected files with multi-select support |
| `useRoomFileScope` | Accesses room file scope context (returns `null` outside `RoomFileScope`) |

### Session

| Hook | Description |
|------|-------------|
| `useSessionState` | Generic session lifecycle hook with start, open, and close actions. Tracks active state, session data, and associated file ID |
| `useFileSessionLoader` | Fetches and parses a saved session file, with loading state and error handling. Requires `FileCacheProvider` |

## Utilities

### Polls

| Export | Description |
|--------|-------------|
| `generatePollId` | Creates a unique poll identifier |
| `generateOptionId` | Creates a unique poll option identifier |
| `generateResponseId` | Creates a unique text response identifier |
| `generateVoteMessageId` | Creates a unique vote message identifier |
| `createYesNoOptions` | Creates pre-built Yes/No poll options |
| `createRatingOptions` | Creates a 1-5 rating scale poll |
| `createDefaultPoll` | Creates a poll object with default settings |
| `defaultPollSettings` | Default poll configuration values |
| `defaultPollResults` | Empty poll results object |
| `getOptionVoteCount` | Returns the vote count for a poll option |
| `calculateVotePercentage` | Calculates vote percentage for a poll option |
| `getTotalVotes` | Returns total votes across all options |
| `getTotalVoters` | Returns total unique voters |
| `hasUserVoted` | Checks whether a user has voted on a poll |
| `getTimeRemaining` | Calculates remaining time for a timed poll |
| `formatRelativeTime` | Formats a timestamp as relative time ("2m ago") |
| `calculateServerTimeOffset` | Calculates clock offset between client and server |

### Q&A

| Export | Description |
|--------|-------------|
| `generateQuestionId` | Creates a unique question identifier |
| `generateAnswerId` | Creates a unique answer identifier |
| `defaultFormatTimestamp` | Default timestamp formatter for Q&A messages |

### File Manager

| Export | Description |
|--------|-------------|
| `formatFileName` | Formats a file name for display |
| `formatFileDate` | Formats a file date for display |
| `truncateFileName` | Truncates long file names with ellipsis |
| `getFileType` | Determines file type from name or MIME type |
| `getFileExtension` | Extracts file extension from a file name |
| `getResourceTypeFromContentType` | Maps a MIME type or extension to a `ResourceType` |
| `isFolder` / `isFile` | Type guards for file system items |

### File Reference Utilities

Utilities for discovering file references when sharing files that link to other files (e.g., a clip composition referencing audio clips). Built-in extractors handle `clip-composition` and `assignment` resource types.

| Export | Description |
|--------|-------------|
| `registerFileReferenceExtractor` | Register a custom extractor for a resource type. Returns a cleanup function |
| `hasFileReferences` | Check whether a resource type has a registered extractor |
| `extractFileReferences` | Extract referenced file IDs from parsed file content |

```tsx
import {
  registerFileReferenceExtractor,
  hasFileReferences,
  extractFileReferences,
} from '@hiyve/react-collaboration';

// Register a custom extractor for playlist files
const cleanup = registerFileReferenceExtractor('playlist', (content) => {
  const playlist = content as { tracks?: Array<{ fileId: string }> };
  return (playlist.tracks ?? []).map(t => t.fileId);
});

// Check and extract references
if (hasFileReferences('playlist')) {
  const referencedIds = extractFileReferences('playlist', parsedContent);
}

// Unregister when done
cleanup();
```

### Constants

| Export | Description |
|--------|-------------|
| `CHAT_CONFIG` | Chat panel configuration constants |
| `RESOURCE_TAB_MAPPING` | Maps resource types to sidebar tab names for smart opening |
| `defaultColumns` | Default columns for the file list table |
| `flatViewColumns` | Columns used in flat/filtered file views |
| `RESOURCE_VIEWER_TYPES` | Set of resource types that open in the inline FileViewer |
| `mergeResourceViewerTypes(baseSet, overrides)` | Merge function for customizing the viewer type set via `{ add?, remove?, replace? }` |
| `mergeResourceTabMapping(overrides?)` | Merge function for customizing the resource-type-to-tab mapping via `{ add?, remove?, replace? }` |

## Customization

Every component supports customization through `labels`, `icons`, `colors`, and `styles` props. Pass a partial object to override only the values you need.

### Chat

```tsx
import { ChatPanel, defaultChatPanelColors } from '@hiyve/react-collaboration';

<ChatPanel
  labels={{
    title: 'Mensajes',
    placeholder: 'Escribe un mensaje...',
    emptyState: 'Sin mensajes',
  }}
  colors={{
    headerBackground: '#f5f5f5',
    containerBackground: '#ffffff',
    localMessageBackground: '#1976d2',
  }}
  styles={{
    avatarSize: 28,
    messageBorderRadius: 16,
  }}
/>
```

**Chat defaults and merge functions:**

| Default | Merge Function |
|---------|---------------|
| `defaultChatPanelLabels` | `mergeChatPanelLabels` |
| `defaultChatPanelIcons` | `mergeChatPanelIcons` |
| `defaultChatPanelColors` | `mergeChatPanelColors` |
| `defaultChatPanelStyles` | `mergeChatPanelStyles` |

### Chat Hub

```tsx
import { ChatHub, defaultChatHubColors } from '@hiyve/react-collaboration';

<ChatHub
  channels={channels}
  activeChannelId={activeId}
  messages={messages}
  localUserId="user-123"
  onChannelSelect={setActiveId}
  onSend={handleSend}
  labels={{ emptyStateMessage: 'Selecciona una conversacion' }}
  colors={{ subtitleColor: '#888' }}
  channelListProps={{
    labels: { title: 'Conversaciones', searchPlaceholder: 'Buscar...' },
  }}
/>
```

**Chat Hub defaults and merge functions:**

| Default | Merge Function |
|---------|---------------|
| `defaultChatHubLabels` | `mergeChatHubLabels` |
| `defaultChatHubColors` | `mergeChatHubColors` |
| `defaultChatChannelListLabels` | `mergeChatChannelListLabels` |
| `defaultChatChannelListColors` | `mergeChatChannelListColors` |

### Polls

```tsx
import { PollsWindow, defaultPollsWindowLabels } from '@hiyve/react-collaboration';

<PollsWindow
  labels={{
    createPoll: 'Neue Umfrage',
    vote: 'Abstimmen',
  }}
  colors={{
    primary: '#7c3aed',
  }}
/>
```

**Polls defaults and merge functions:**

| Default | Merge Function |
|---------|---------------|
| `defaultPollsWindowLabels` | `mergePollsWindowLabels` |
| `defaultPollCreatorLabels` | `mergePollCreatorLabels` |
| `defaultPollEditorLabels` | `mergePollEditorLabels` |
| `defaultPollResultsViewerLabels` | `mergePollResultsViewerLabels` |
| `defaultPollsWindowIcons` | `mergePollsWindowIcons` |
| `defaultPollsWindowColors` | `mergePollsWindowColors` |
| `defaultPollsWindowStyles` | `mergePollsWindowStyles` |
| `defaultPollsSessionLabels` | -- |

### Q&A

```tsx
import { QAPanel, defaultQAPanelLabels } from '@hiyve/react-collaboration';

<QAPanel
  labels={{
    askQuestion: 'Poser une question',
    answer: 'Repondre',
  }}
  icons={{
    upvote: <MyUpvoteIcon />,
  }}
/>
```

**Q&A defaults and merge functions:**

| Default | Merge Function |
|---------|---------------|
| `defaultQAPanelLabels` | `mergeQAPanelLabels` |
| `defaultQAPanelIcons` | `mergeQAPanelIcons` |
| `defaultQAPanelColors` | `mergeQAPanelColors` |
| `defaultQAPanelStyles` | `mergeQAPanelStyles` |
| `defaultQASessionLabels` | -- |
| `defaultQASessionViewerLabels` | -- |
| `defaultQASessionViewerColors` | -- |

### File Manager

```tsx
import {
  FileManager,
  defaultFileManagerLabels,
  defaultFileIcons,
} from '@hiyve/react-collaboration';

<FileManager
  labels={{
    upload: 'Subir archivo',
    newFolder: 'Nueva carpeta',
  }}
/>
```

**File Manager defaults and merge functions:**

| Default | Merge Function |
|---------|---------------|
| `defaultFileManagerLabels` | `mergeFileManagerLabels` |
| `defaultFileManagerIcons` | `mergeFileManagerIcons` |
| `defaultFileManagerColors` | `mergeFileManagerColors` |
| `defaultFileManagerStyles` | `mergeFileManagerStyles` |

> **Note:** The unprefixed names (`defaultLabels`, `mergeLabels`, `defaultIcons`, `mergeIcons`, `defaultColors`, `mergeColors`, `defaultStyles`, `mergeStyles`) are deprecated. Use the prefixed `defaultFileManager*` / `mergeFileManager*` names shown above.
| `defaultFileIcons` | `mergeFileIcons` |
| `defaultFileIconColors` | `mergeFileIconColors` |
| `defaultViewerLabels` | -- |
| `defaultViewerColors` | -- |
| `defaultViewerStyles` | -- |
| `defaultMediaFileViewerLabels` | `mergeMediaFileViewerLabels` |
| `defaultMediaFileViewerColors` | `mergeMediaFileViewerColors` |
| `defaultCreateFolderLabels` | -- |
| `defaultRenameLabels` | -- |
| `defaultDeleteLabels` | -- |
| `defaultShareLabels` | -- |
| `defaultRoomSummaryLabels` | -- |
| `defaultRoomSummaryColors` | -- |

### Session Common

| Default |
|---------|
| `defaultSessionEmptyStateLabels` |
| `defaultSessionHeaderLabels` |

## Namespaced Constants

Some constant names are shared across the polls and Q&A domains. They are exported with domain prefixes to avoid conflicts:

| Polls Export | Q&A Export |
|-------------|------------|
| `pollsDATA_MESSAGE_TYPES` | `qaDATA_MESSAGE_TYPES` |
| `pollsTIMING` | `qaTIMING` |
| `pollsSORT_MODES` | `qaSORT_MODES` |
| `pollsFILE_STORAGE` | `qaFILE_STORAGE` |
| `POLL_CONFIG` | `QA_CONFIG` |

## Key Types

| Type | Description |
|------|-------------|
| `ChatMessage` | Chat message data (id, userId, userName, content, timestamp, isSystem, isLocal) |
| `ChatMessageListProps` | Props for the `ChatMessageList` component |
| `ChatInputProps` | Props for the `ChatInput` component |
| `ChatHubProps` | Props for the `ChatHub` component |
| `ChatHubLabels` | Customizable text labels for `ChatHub` |
| `ChatHubColors` | Color configuration for `ChatHub` layout |
| `ChatChannelListProps` | Props for the `ChatChannelList` component |
| `ChatChannelListLabels` | Customizable text labels for `ChatChannelList` |
| `ChatChannelListColors` | Color configuration for `ChatChannelList` |
| `ChatChannel` | Channel data (id, name, avatarUrl, subtitle, unreadCount, lastMessageAt, lastMessagePreview, isOnline, type) |
| `Poll` | Poll data including question, options, settings, status, and results |
| `PollOptionType` | A single poll option with text and vote data |
| `PollType` | `'multiple-choice' \| 'multiple-select' \| 'yes-no' \| 'rating' \| 'text'` |
| `PollStatus` | `'active' \| 'ended'` |
| `PollSettings` | Poll configuration (anonymous, multiSelect, showResultsWhileVoting, duration) |
| `PollResults` | Aggregated poll results (totalVotes, totalVoters) |
| `PollsSessionFile` | Saved poll session file structure |
| `Question` | Q&A question data (id, content, author, upvotes, answers, isPinned) |
| `Answer` | Q&A answer data (id, content, author, isAccepted) |
| `SortMode` | `'newest' \| 'votes' \| 'unanswered'` |
| `QASessionFile` | Saved Q&A session file structure |
| `ResourceType` | File type identifier (image, video, audio, pdf, text, whiteboard, recording, etc.) |
| `FileEntry` | File data (fileId, fileName, resourceType, userId, location, size, modified) |
| `FolderEntry` | Folder data (name, path, children count) |
| `FileSystemItem` | `FileEntry \| FolderEntry` |
| `FileListColumn` | `'name' \| 'type' \| 'size' \| 'modified' \| 'owner' \| 'actions'` |
| `FileViewerState` | File viewer overlay state |
| `FilterChipDefinition` | Filter chip definition (`label`, `types`) for `FileManagerFilterChips` |
| `FileListViewProps` | Shared props for `FileTableView` and `FileCardView` |
| `CustomToolbarAction` | Custom toolbar button configuration (label, icon, onClick, disabled) |
| `CustomViewerRenderer` | `(data: unknown, file: FileEntry, onClose: () => void) => ReactNode` |
| `CustomViewerMap` | `Record<string, CustomViewerRenderer>` |
| `FileCacheProviderProps` | Props for `FileCacheProvider` (children, files, on* callbacks) |
| `FileCacheContextValue` | Value returned by `useFileCache` (cacheState, isReady, file operations) |
| `CacheState` | `'idle' \| 'initializing' \| 'ready' \| 'switching' \| 'error'` |
| `FileReferenceExtractor` | `(content: unknown) => string[]` — extracts referenced file IDs from file content |
| `RoomFileScopeProps` | Props for `RoomFileScope` (roomName, defaultShowAll, disableToggle) |
| `RoomFileScopeContextValue` | Value returned by `useRoomFileScope` (roomName, showRoomFilesOnly, setShowRoomFilesOnly, disableToggle) |
| `RoomSummaryData` | Room summary with participants, recordings, files, transcription |
| `RoomSummaryViewerLabels` | Customizable text labels for `RoomSummaryViewer` |
| `RoomSummaryViewerColors` | Color configuration for `RoomSummaryViewer` |
| `MediaFileViewerProps` | Props for the `MediaFileViewer` component |
| `MediaFileViewerLabels` | Customizable text labels for `MediaFileViewer` (loading, errorPrefix, playbackFallback) |
| `MediaFileViewerColors` | Color configuration for `MediaFileViewer` (background, errorText) |

## Requirements

- **`@hiyve/react`** (`^2.0.0`) -- components must be rendered inside `HiyveProvider`
- **`@hiyve/rtc-client`** -- WebRTC client (provided by HiyveProvider)
- **`@hiyve/utilities`** (`^1.0.0`)
- **`@mui/material`** (`^5.0.0 || ^6.0.0`) and **`@mui/icons-material`**
- **`@emotion/react`** (`^11.0.0`) and **`@emotion/styled`** (`^11.0.0`)
- **`react`** (`^18.0.0`)
- **`dayjs`** (`^1.11.0`) -- optional, used for date formatting in file manager
- **`@hiyve/react-media-player`** (`^1.0.0`) -- optional, enables enhanced media playback in `MediaFileViewer`

## License

Proprietary - Hiyve SDK
