mirror of
https://github.com/yjs/yjs.git
synced 2025-12-14 18:57:45 +01:00
documentation for attribution feature
This commit is contained in:
237
attribution-manager.md
Normal file
237
attribution-manager.md
Normal file
@@ -0,0 +1,237 @@
|
||||
|
||||
# Attribution Feature
|
||||
|
||||
The Attribution feature extends Yjs types to provide rich metadata about content
|
||||
changes, including information about who created, deleted, or formatted content.
|
||||
This enables powerful collaborative editing features such as authorship tracking
|
||||
and change visualization. The information about who performed which changes can
|
||||
be handled by a separate CRDT (which is part of the attribution manager).
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Attribution Manager
|
||||
|
||||
The `attributionManager` is the central component that tracks and manages
|
||||
attribution data. It must be passed to methods that support attribution to
|
||||
enable the feature.
|
||||
|
||||
Different implementations of AttributionManager are available for different use cases:
|
||||
- `DiffingAttributionManager`: Highlights the differences between two Yjs documents
|
||||
- `SnapshotAttributionManager`: Highlights the differences between two snapshots
|
||||
|
||||
### Attributed Content
|
||||
|
||||
Attributed content includes standard Yjs operations enhanced with attribution metadata:
|
||||
|
||||
```javascript
|
||||
// Standard content
|
||||
[{ insert: 'hello world' }]
|
||||
|
||||
// Attributed content
|
||||
[
|
||||
{ insert: 'hello', attribution: { insert: ['kevin'] } },
|
||||
{ insert: ' world', attribution: { insert: ['alice'] } }
|
||||
]
|
||||
```
|
||||
|
||||
### Delete Attribution
|
||||
|
||||
Deleted content is represented in attributed results to maintain authorship information and proper position tracking:
|
||||
|
||||
```javascript
|
||||
// Shows deleted content with attribution
|
||||
[
|
||||
{ insert: 'hello ', attribution: { delete: ['kevin'] } },
|
||||
{ insert: 'world' }
|
||||
]
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### YText
|
||||
|
||||
#### `getDelta([attributionManager])`
|
||||
|
||||
Returns the delta representation of the YText content, optionally with attribution information.
|
||||
|
||||
**Parameters:**
|
||||
- `attributionManager` (optional): The attribution manager instance
|
||||
|
||||
**Returns:**
|
||||
- Array of delta operations, with attribution metadata if `attributionManager` is provided
|
||||
|
||||
**Examples:**
|
||||
|
||||
```javascript
|
||||
const ytext = new Y.Text()
|
||||
// Content is inserted during collaborative editing
|
||||
// Attribution is handled automatically by the server
|
||||
|
||||
// Without attribution
|
||||
const delta = ytext.getDelta()
|
||||
// [{ insert: 'hello world' }]
|
||||
|
||||
// With attribution
|
||||
const attributedDelta = ytext.getDelta(attributionManager)
|
||||
// [
|
||||
// { insert: 'hello', attribution: { insert: ['kevin'] } },
|
||||
// { insert: ' world', attribution: { insert: ['alice'] } }
|
||||
// ]
|
||||
```
|
||||
|
||||
#### `getContent([attributionManager])`
|
||||
|
||||
Returns the content representation with optional attribution information.
|
||||
|
||||
**Parameters:**
|
||||
- `attributionManager` (optional): The attribution manager instance
|
||||
|
||||
**Returns:**
|
||||
- Content representation with attribution metadata if `attributionManager` is provided
|
||||
|
||||
### YArray
|
||||
|
||||
#### `getContent([attributionManager])`
|
||||
|
||||
Returns the array content with optional attribution information for each element.
|
||||
|
||||
**Parameters:**
|
||||
- `attributionManager` (optional): The attribution manager instance
|
||||
|
||||
**Returns:**
|
||||
- Array content with attribution metadata if `attributionManager` is provided
|
||||
|
||||
### YMap
|
||||
|
||||
#### `getContent([attributionManager])`
|
||||
|
||||
Returns the map content with optional attribution information for each key-value pair.
|
||||
|
||||
**Parameters:**
|
||||
- `attributionManager` (optional): The attribution manager instance
|
||||
|
||||
**Returns:**
|
||||
- Map content with attribution metadata if `attributionManager` is provided
|
||||
|
||||
## Position Adjustments
|
||||
|
||||
When working with attributed content, position calculations must account for deleted content that appears in the attributed representation but not in the standard representation.
|
||||
|
||||
### Example: Position Adjustment
|
||||
|
||||
```javascript
|
||||
// Standard content (length: 5)
|
||||
ytext.toString() // "world"
|
||||
|
||||
// Attributed content (includes deleted content)
|
||||
ytext.getDelta(attributionManager)
|
||||
// [
|
||||
// { insert: 'hello ', attribution: { delete: ['kevin'] } }, // positions 0-5
|
||||
// { insert: 'world' } // positions 6-10
|
||||
// ]
|
||||
|
||||
// To insert after "world":
|
||||
// - Standard position: 5 (after "world")
|
||||
// - Attributed position: 11 (after "world" accounting for deleted "hello ")
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
Events in Yjs are enhanced to work with attributed content, automatically adjusting positions when attribution is considered.
|
||||
|
||||
### Event Position Adjustment
|
||||
|
||||
When an `attributionManager` is used, event positions are automatically adjusted to account for deleted content.
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
// Initial content: "hello world"
|
||||
// User deletes "hello " (positions 0-6)
|
||||
// Current visible content: "world"
|
||||
|
||||
ytext.observe((event, transaction) => {
|
||||
// User wants to insert "!" after "world"
|
||||
|
||||
// Standard event (without attribution)
|
||||
const standardDelta = event.getDelta()
|
||||
// Shows insertion at position 5 (after "world" in visible content)
|
||||
|
||||
// Attributed event (with attribution manager)
|
||||
const attributedDelta = event.getDelta(attributionManager)
|
||||
// Shows insertion at position 11 (accounting for deleted "hello ")
|
||||
// [
|
||||
// { insert: 'hello ', attribution: { delete: ['kevin'] } },
|
||||
// { insert: 'world' },
|
||||
// { insert: '!' } // inserted at attributed position 11
|
||||
// ]
|
||||
})
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Authorship Visualization
|
||||
|
||||
Display content with visual indicators of who created each part:
|
||||
|
||||
```javascript
|
||||
function renderWithAuthorship(ytext, attributionManager) {
|
||||
const attributedDelta = ytext.getDelta(attributionManager)
|
||||
|
||||
return attributedDelta.map(op => {
|
||||
const author = op.attribution?.insert?.[0] || 'unknown'
|
||||
const isDeleted = op.attribution?.delete
|
||||
|
||||
return {
|
||||
content: op.insert,
|
||||
author,
|
||||
isDeleted,
|
||||
className: `author-${author} ${isDeleted ? 'deleted' : ''}`
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Change Tracking
|
||||
|
||||
Track who made specific changes to content:
|
||||
|
||||
```javascript
|
||||
function trackChanges(ytext, attributionManager) {
|
||||
ytext.observe((event, transaction) => {
|
||||
const changes = event.changes.getAttributedDelta?.(attributionManager) || event.changes.delta
|
||||
|
||||
changes.forEach(change => {
|
||||
if (change.attribution) {
|
||||
console.log(`Change by ${change.attribution.insert?.[0] || change.attribution.delete?.[0]}:`, change)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Attribution Manager Lifecycle
|
||||
|
||||
- Create one attribution manager per document or collaboration session
|
||||
- Ensure the attribution manager is consistently used across all operations
|
||||
- Pass the same attribution manager instance to all methods that need attribution
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Upgrading Existing Code
|
||||
|
||||
To add attribution support to existing Yjs applications:
|
||||
|
||||
1. **Add attribution manager**: Create and configure an attribution manager
|
||||
2. **Update method calls**: Add the attribution manager parameter to relevant method calls
|
||||
3. **Handle attributed content**: Update code to handle the new attribution metadata format
|
||||
4. **Adjust position calculations**: Update position calculations to account for deleted content
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
The Attribution feature is fully backward compatible:
|
||||
- All existing methods work without the attribution manager parameter
|
||||
- Existing code continues to work unchanged
|
||||
- Attribution is opt-in and doesn't affect performance when not used
|
||||
Reference in New Issue
Block a user