Read-Only Mode Implementation¶
Overview¶
Read-only mode prevents users from modifying documents and annotations while still allowing viewing and navigation. This mode is triggered by insufficient permissions or explicit configuration.
When Read-Only Mode is Active¶
- No UPDATE Permission: User lacks CAN_UPDATE permission (corpus or document)
 - Explicit Override: Parent component passes 
readOnly={true} - No Corpus Context: Document viewed outside corpus (limited features)
 - Backend Lock: Document has 
backendLock: true 
Component Support Status¶
✅ Fully Implemented¶
Document Renderers¶
- PDF Component: Accepts 
read_onlyprop, prevents annotation creation - TxtAnnotatorWrapper: Accepts 
readOnlyandallowInputprops 
Annotation Components¶
- UnifiedLabelSelector: Disabled when 
readOnly={true} - SelectionLayer: Shows "Document is read-only" message
 - AnnotationMenu: Shows only copy option (no Apply Label)
 
Floating Components¶
- FloatingDocumentInput: Hides chat mode button
 - FloatingDocumentControls: Hides "Start New Analysis" button
 - FloatingAnalysesPanel: Passes readOnly to AnalysisTraySelector
 - FloatingExtractsPanel: Passes readOnly to ExtractTraySelector
 - FloatingSummaryPreview: Display-only component
 
Content Components¶
- UnifiedContentFeed: Passes readOnly to all content items
 - PostItNote: Changes cursor to default, hides edit indicator
 - HighlightItem: Disables delete functionality
 - RelationItem: Disables delete and edit functions
 
Other Components¶
- ChatTray: Starts fresh conversation, hides history
 - UnifiedKnowledgeLayer: Hides "Edit Summary" button
 - NoteEditor: Component hidden entirely
 - NewNoteModal: Component hidden entirely
 
Components That Don't Need Changes¶
- ZoomControls: Zoom is inherently view-only
 - SidebarControlBar: Navigation is view-only
 - SearchResultCard: View-only by nature
 
Implementation Patterns¶
Pattern 1: Conditional Rendering¶
// Hide components that have no read-only use case
{!readOnly && (
  <NoteEditor
    noteId={editingNoteId}
    onSave={handleSave}
  />
)}
Pattern 2: Prop Passing¶
// Pass readOnly to child components
<UnifiedContentFeed
  readOnly={readOnly}
  items={contentItems}
/>
Pattern 3: Conditional Handlers¶
// Disable handlers in read-only mode
<HighlightItem
  annotation={annotation}
  onDelete={readOnly ? undefined : handleDelete}
  read_only={readOnly}
/>
Pattern 4: UI State Changes¶
// Change cursor and hover states
const PostItNote = styled.div<{ $readOnly?: boolean }>`
  cursor: ${props => props.$readOnly ? 'default' : 'pointer'};
  &:hover .edit-indicator {
    display: ${props => props.$readOnly ? 'none' : 'block'};
  }
`;
User Experience in Read-Only Mode¶
Visual Indicators¶
- Cursor Changes: Pointer cursor becomes default cursor
 - Hidden Controls: Edit/delete buttons are hidden
 - Disabled Inputs: Form inputs are disabled
 - Status Messages: "Document is read-only" shown when attempting actions
 
Available Features¶
- ✅ View document content
 - ✅ Navigate pages
 - ✅ Zoom in/out
 - ✅ Search within document
 - ✅ View existing annotations
 - ✅ Copy text
 - ✅ View notes (cannot edit)
 - ✅ Navigate between documents
 
Disabled Features¶
- ❌ Create new annotations
 - ❌ Edit existing annotations
 - ❌ Delete annotations
 - ❌ Create/edit notes
 - ❌ Start new analyses
 - ❌ Modify summaries
 - ❌ Change corpus settings
 
Testing Read-Only Mode¶
Unit Tests¶
describe('Read-Only Mode', () => {
  it('should prevent annotation creation', () => {
    render(<DocumentKnowledgeBase readOnly={true} />);
    // Select text
    selectText('sample text');
    // Verify no annotation menu appears
    expect(screen.queryByRole('menu')).not.toBeInTheDocument();
  });
  it('should hide edit buttons', () => {
    render(<UnifiedKnowledgeLayer readOnly={true} />);
    expect(screen.queryByText('Edit Summary')).not.toBeInTheDocument();
  });
});
Integration Tests¶
it('should enforce read-only when user lacks permissions', () => {
  const mockPermissions = ['CAN_READ']; // No UPDATE
  render(
    <MockedProvider mocks={[mockWithPermissions(mockPermissions)]}>
      <DocumentKnowledgeBase documentId="123" corpusId="456" />
    </MockedProvider>
  );
  // Verify read-only behavior
});
E2E Tests¶
test('read-only mode prevents modifications', async ({ page }) => {
  // Login as read-only user
  await loginAsReadOnlyUser(page);
  // Navigate to document
  await page.goto('/documents/123');
  // Try to select text for annotation
  await page.selectText('sample text');
  // Verify no annotation menu
  await expect(page.locator('.annotation-menu')).not.toBeVisible();
});
Migration Guide¶
For Existing Components¶
-  
Add readOnly prop to interface:
interface MyComponentProps { // ... existing props readOnly?: boolean; } -  
Conditionally render edit controls:
{!readOnly && ( <Button onClick={handleEdit}>Edit</Button> )} -  
Disable interaction handlers:
const handleClick = readOnly ? undefined : () => { // Edit logic }; -  
Update styles for read-only state:
cursor: ${props => props.readOnly ? 'default' : 'pointer'}; 
For New Components¶
- Always accept 
readOnlyprop - Design with read-only mode in mind
 - Provide clear visual feedback
 - Test both modes thoroughly
 
Troubleshooting¶
Component not respecting read-only mode¶
- Verify component receives readOnly prop
 - Check if handlers are conditionally disabled
 - Ensure UI elements are hidden/disabled
 
Read-only mode too restrictive¶
- Identify view-only features being blocked
 - Separate modification from viewing logic
 - Allow navigation and viewing operations
 
Inconsistent read-only behavior¶
- Check permission flow from parent
 - Verify all child components receive prop
 - Ensure consistent prop naming (readOnly vs read_only)