# Conditional Templates
* Conditional Templates let you conditionally show or hide parts of the Velt Component Wireframes.
* You can add conditions based on the same data models available in [Template Variables](/advanced-customization/template-variables).
## Usage
```jsx
{/* Content to render if condition is true */}
```
```html
```
## Syntax
The condition is specified as a string that will be evaluated as a JavaScript expression. You can use:
* Comparison operators: `===`, `==`, `!==`, `>`, `<`, `>=`, `<=`
* Logical operators: `&&`, `||`, `!`
* Template variables enclosed in curly braces: `{variable.property}`
## Example Usage
```jsx
{/* 1. Show content only for open annotations: */}
This annotation is currently open.
{/* 2. Display a message for annotations with more than 5 comments: */}
This is a popular annotation!
{/* 3. Combine multiple conditions: */}
This is a new open annotation without any comments yet.
```
```html
This annotation is currently open.
This is a popular annotation!
This is a new open annotation without any comments yet.
```
# Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/dark-light-mode.png)
You can customize components by enabling Dark Mode.
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
To enable Dark Mode for all components:
```jsx
let client = useVeltClient();
client.setDarkMode(true);
```
To enable Dark Mode for individual components:
```jsx
```
API Method:
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/dark-light-mode.png)
You can customize components by enabling Dark Mode.
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
To enable Dark Mode for all components:
```jsx
let client = useVeltClient();
client.setDarkMode(true);
```
To enable Dark Mode for individual components:
```html
```
API Method:
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
```jsx React / Next.js
import { VeltComments, useVeltClient } from '@veltdev/react';
import { useEffect } from 'react'
export default function YourDocument() {
const { client } = useVeltClient();
useEffect(()=>{
if(client){
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
}
})
return (
)
}
```
```html HTML
```
# Remove Shadow DOM
## Disable ShadowDOM
If you want to customize components using global CSS, you can do so by disabling the ShadowDOM.
By default, a ShadowDOM is used with certain components to ensure that your application's CSS does not interfere with the styling of the SDK components.
If you want your application's CSS to affect the styling of certain SDK components, you can disable the ShadowDOM.
There are different properties and methods that can be used to disable the ShadowDOM of individual components.
Examples:
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
## Disable ShadowDOM
If you want to customize components using global CSS, you can do so by disabling the ShadowDOM.
By default, a ShadowDOM is used with certain components to ensure that your application's CSS does not interfere with the styling of the SDK components.
If you want your application's CSS to affect the styling of certain SDK components, you can disable the ShadowDOM.
There are different properties and methods that can be used to disable the ShadowDOM of individual components.
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
```jsx React / Next.js
import { VeltCommentBubble } from '@veltdev/react';
import { useEffect } from 'react'
export default function YourDocument() {
return (
)
}
```
```html HTML
```
# Use CSS Library
You can use a CSS library such as Tailwind to style the SDK components.
Simply add the library's classes to the SDK components just like you would to any other component.
To add Custom CSS, ensure that the [Shadow DOM is turned off](./remove-shadow-dom) for the SDK component you are styling.
Conceptual Example:
```jsx
...
{/* Header */}
...
```
You can use a CSS library such as Tailwind to style the SDK components.
Simply add the library's classes to the SDK components just like you would to any other component.
To add Custom CSS, ensure that the [Shadow DOM is turned off](./remove-shadow-dom) for the SDK component you are styling.
Conceptual Example:
```jsx
...
{/* Filter */}
{/* List */}
{/* Empty Placeholder */}
{/* Page Mode Composer */}
```
```HTML HTML
```
# Use Custom CSS
## Add Custom CSS
To add custom CSS to the component, just add CSS into the wireframe template.
To add Custom CSS, ensure that the [Shadow DOM is turned off](./remove-shadow-dom) for the SDK component you are styling.
Conceptual Example:
```jsx
...
{/* Header */}
...
```
## Add Custom CSS
To add custom CSS to the component, just add CSS into the wireframe template.
To add Custom CSS, ensure that the [Shadow DOM is turned off](./remove-shadow-dom) for the SDK component you are styling.
Conceptual Example:
```jsx
...
{/* Filter */}
{/* List */}
{/* Empty Placeholder */}
{/* Page Mode Composer */}
```
```HTML HTML
```
# Replace component layout
You can change the default HTML of Velt Components by providing your own HTML as a child of the component.
```jsx
Attachments
```
```html
Attachments
```
# Reorder Components
To modify the order of subcomponents, simply rearrange them within the wireframe template. The rendered component will reflect this new order.
For example, you can change the order of components in the header of a comment dialog.
```jsx
```
```html
```
# Overview
You can change the default HTML of Velt Components using two patterns:
If you modify the component using both the `Standalone` and `Hierarchical` patterns, the `Hierarchical` pattern will override the `Standalone` pattern.
### Patterns
#### a. Standalone Component Customization (recommended):
* This pattern lets you modify any Velt Component without the need to include all of its siblings. e.g., Composer submit button, Comment Dialog Header.
* By focusing on individual components, this approach simplifies the customization process and enhances maintainability.
* It provides greater flexibility and granular control over specific parts of the UI without needing to write the entire component structure.
```jsx
Custom HTML
```
```html
Custom HTML
```
#### b. Hierarchical Component Customization
* This pattern lets you modify any Velt Component by specifying its entire structure.
* This approach facilitates easier modifications to multiple sibling components simultaneously, offering a larger control on the component's:
* structure and hierarchy.
* styling relationships between sibling components.
* The trade-off is writing more code.
```jsx
{/* Ghost Banner */}
{/* Assignee Banner */}
{/* Increase gap between components */}
{/* Header */}
{/* Body */}
{/* Composer */}
{/* All Comments */}
```
```html
```
# Remove Components
To remove a subcomponent from appearing, simply delete it from the wireframe template.
In this example priority and copy link buttons are removed from the header.
```jsx
```
```html
```
# Overview
You can fully customize any Velt Component.
Here are some concepts that will help you get started. Once you know them, you can follow the [general setup guide](/advanced-customization/setup) to start customizing components.
## 1. Wireframes
### a. What is a Wireframe?
* Wireframes are powerful templates that define the structure and appearance of Velt components, serving as a blueprint for customizing their default layout and styling.
* When you want to modify a component's appearance, you do so by altering its corresponding wireframe, which acts as a global definition determining how components will be displayed.
* Wireframes themselves don't render directly in your application. After customizing a wireframe, you still need to place the actual Velt components in your app as usual, which will then render according to your customized wireframe definitions.
### b. How to customize a Wireframe?
* Add the root Wireframe component.
* Find the Wireframe component of the target Velt Component. Add it inside the root Wireframe.
* Modify the target component wireframe by either adding custom HTML as children inside it or by adding custom styling.
### c. What can go inside a Wireframe?
You can add the following inside a Wireframe:
* Custom HTML
* Custom inline styling on non-Velt components
* Velt's wireframe components
You cannot add:
* Regular Velt Components inside a Wireframe
* Custom inline styling on Velt components. Instead, you can wrap Velt Wireframe components in regular HTML components and style them with inline css.
### d. How to use a customized Wireframe?
After modifying a Wireframe template:
* Place the regular Velt component in your app as usual
* The component will render with the layout changes defined in your customized Wireframe
* You'll see the updated appearance reflecting your template modifications
## 2. Make Layout Changes
Adding children will remove the CSS for that component.
If you add an empty wireframe with no children, it will keep its original style and children.
### a. Replace component layout
To completely change the layout of Velt's components, provide your own custom HTML as children within the target component's wireframe.
[Learn more](/advanced-customization/customize-layout/change-default-html).
### b. Remove components
To remove a subcomponent, simply delete it from the wireframe template. This will prevent it from appearing in the rendered component.
[Learn more](/advanced-customization/customize-layout/remove-existing).
### c. Reorder components
To change the order of subcomponents, simply rearrange them within the wireframe template. The rendered component will reflect this new order.
[Learn more](/advanced-customization/customize-layout/change-order).
## 3. Make CSS Changes
### a. Remove Shadow DOM
* Shadow DOM is enabled by default for Velt Components. This prevents CSS from the host app affecting Velt Components.
* Disable Shadow DOM for specific components to allow your app's CSS to style Velt Components. This affects the component and its children.
[Learn more](/advanced-customization/customize-css/remove-shadow-dom).
### b. Add Dark Mode
Velt components default to light mode. To enable dark mode for specific components, use the component's designated prop or API method.
[Learn more](/advanced-customization/customize-css/dark-mode).
### c. Use CSS Library
To style Velt components with a CSS library like Tailwind:
1. Disable Shadow DOM
2. Add the library's classes inside the wireframe or directly on the rendered Velt components. You can find the component CSS selectors by inspecting the component in the browser's developer tools.
This works just like styling any other component in your application.
[Learn more](/advanced-customization/customize-css/use-css-library).
### d. Use Custom CSS
Same as above, you can add your classes to the wireframes or directly on the rendered Velt components.
[Learn more](/advanced-customization/customize-css/use-custom-css).
## 5. Template Variables
You can render dynamic data from these certain Data Classes inside your Velt Components Wireframe.
[Learn more](/advanced-customization/template-variables).
## 6. Conditional Templates
You can conditionally show or hide parts of the Velt Component Wireframes.
[Learn more](/advanced-customization/conditional-templates).
## 6. Variants
If you want to have multiple versions of customized components that you can swap between, you can use variants.
[Learn more](/advanced-customization/variants)
# General Setup
## 1. Add the Velt Wireframe component
* Add the `Velt Wireframe` component to the root of your app.
* Add the wireframes of any components you want to customize within the `Velt Wireframe` component.
```jsx
import { VeltWireframe, VeltProvider } from '@veltdev/react'
```
```jsx
{/* Customized component wireframes go here */}
```
Add the inline `style="display:none;"` CSS attribute to the `` element to ensure it's never displayed on the DOM. While we automatically handle this, there might be instances where the wireframe becomes visible if the SDK hasn't initialized yet.
```html
```
## 2. Add a component's wireframe
* Copy the `wireframe` of the component you want to modify. You will find the wireframe for each component within it's feature documentation.
* Place it inside the `Velt Wireframe` component.
```jsx
```
```html
```
## 3. Customize the wireframe template
* Modify the layout of the `wireframe` by:
* Adding custom HTML or CSS
* Reorganizing subcomponents
* Removing subcomponents within the template
* To customize multiple components, simply add more Wireframes as children to the `Velt Wireframe` component.
If you add any children to a Wireframe, the default styling of that particular component will be removed
```jsx
Custom HTML
```
```html
Custom HTML
```
## 4. Add the component
Now that you've modified the wireframe, place the component (not the wireframe) in your app layout as you normally would
The `wireframe` components are not meant to be rendered directly in your application. They serve as template definitions that specify how Velt components will render globally. After defining your wireframes, you should still place the actual Velt components in your app layout as you normally would.
```jsx
```
```html
```
# Template Variables
You can render dynamic data from these Data Classes inside your Velt Components Wireframe.
## Types
There are two types of template variables:
**1. Global Template Variables:** These can be used across all components. example: the current logged in `User` is available across all components.
**2. Local Template Variables:** These are only available within the component it's data class is used. Eg: `CommentAnnotation` is only avaialable within the comment dialog container and its child components.
## Syntax
* Refer the supported Class' fields in the `field` field of the `Velt Data` component.
* You can refer to nested fields using the dot notation.
```jsx
```
```jsx
```
## Global Template Variables
### **1. User**
This represents the current logged-in user. It is available across all components. Learn more about the User data class [here](/api-reference/models/User).
### **2. Unread Comment Annotation Count**
This represents the number of unread comment annotations on the current document.
### **3. Unread Comment Count**
This represents the total number of unread comments on the current document.
## Local Template Variables
### **1. UserContact**
This represents the user contact object (it has the same class as the User). It is available on the autocomplete component where the user contacts are rendered. Learn more about the UserContact data class [here](/api-reference/models/User).
### **2. CommentAnnotation**
This represents the comment thread. This is available within the comment feature components. Learn more about the CommentAnnotation data class [here](/api-reference/models/CommentAnnotation).
### **3. Comment**
This represents the message inside the thread. This is available within the comment feature components. Learn more about the Comment data class [here](/api-reference/models/Comment).
## Example Usage
```jsx
```
```jsx
```
# Variants
Variants allow you to create and use multiple customized versions of a single component in different parts of your app.
Example: You can have a customized Comment Dialog for the sidebar and another for the pin.
## 1. Define a Variant
To define a variant:
* Add a template of the component wireframe to the `Velt Wireframe` component
* Give it a `variant` name
```jsx
#Your wireframe for the variant
#Your wireframe for the variant
```
```html
#Your wireframe for the variant
#Your wireframe for the variant
```
## 2. Use a Variant
To apply a specific variant, simply set the `variant` prop when using the Velt component in your app.
```jsx
```
```html
```
## 3. Pre-defined Variants
* Some components have pre-defined variants with reserved names and specific functionality
* To customize pre-defined variants:
1. Add the `variant` property with its reserved name to the specific Velt Wireframe component you want to customize
2. Provide the customized template for that variant within the wireframe component
* Check each component's customization documentation for available pre-defined variants
For example, the `Comment Dialog` has two pre-defined `variants`:
```jsx
{/* This pre-defined variant will change the appearance of the Comment Dialog within Pin, Area, and Text comments only */}
#Your wireframe for variant
{/* This pre-defined variant will change the appearance of the Comment Dialog within the Sidebar only */}
#Your wireframe for variant
```
```html
#Your wireframe for variant
#Your wireframe for variant
```
## 4. Unsupported Components
Some components currently do not support `variants`. Please check the customization docs for each component to check if `variants` are supported.
# null
## getAllCommentAnnotations()
Subscribe to comments on the current document.
* **Signature**: `getAllCommentAnnotations(documentId?: string, location?: object)`
* **Params**: `documentId?: string, location?: object`
* **Returns**: `Observable`
## deleteSelectedComment()
To delete selected comments.
* **Signature**: `deleteSelectedComment()`
* **Params**: none
* **Returns**: `Promise`
## selectCommentByAnnotationId()
To select comment by annotation ID.
* **Signature**: `selectCommentByAnnotationId(annotationId?: string)`
* **Params**: `annotationId`
* **Returns**: void
## enableAreaComment()
To enable area comment.
* **Signature**: `enableAreaComment()`
* **Params**: none
* **Returns**: void
## disableAreaComment()
To disable area comment.
* **Signature**: `disableAreaComment()`
* **Params**: none
* **Returns**: void
## enableTextComments()
To enable text comment selection.
* **Signature**: `enableTextComments()`
* **Params**: none
* **Returns**: `any`
## disableTextComments()
To disable text comment selection.
* **Signature**: `disableTextComments()`
* **Params**: none
* **Returns**: `any`
## allowedElementIds()
To allow text selection in specific elements only.
* **Signature**: `allowedElementIds(elementIds: string[])`
* **Params**: `elementIds: string[]`
* **Returns**: `any`
## allowedElementClassNames()
To allow adding comments in specific elements only.
* **Signature**: `allowedElementClassNames(classNames: string[])`
* **Params**: `classNames: string[]`
* **Returns**: `any`
## allowedElementQuerySelectors()
To allow adding comments in specific elements only.
* **Signature**: `allowedElementQuerySelectors(querySelectors: string[])`
* **Params**: `querySelectors: string[]`
* **Returns**: `any`
## updateCommentDialogPosition()
To update comment dialog position.
* **Signature**: `updateCommentDialogPosition()`
* **Params**: none
* **Returns**: void
## enableFloatingCommentDialog()
To enable floating comment dialog.
* **Signature**: `enableFloatingCommentDialog()`
* **Params**: none
* **Returns**: `any`
## disableFloatingCommentDialog()
To disable floating comment dialog.
* **Signature**: `disableFloatingCommentDialog()`
* **Params**: none
* **Returns**: `any`
## attachComment()
To add comments on specific elements.
* **Signature**: `attachComment(elementId: string)`
* **Params**: `elementId: string`
* **Returns**: `any`
## openCommentSidebar()
To open comment sidebar.
* **Signature**: `openCommentSidebar()`
* **Params**: none
* **Returns**: `any`
## closeCommentSidebar()
To close comment sidebar.
* **Signature**: `closeCommentSidebar()`
* **Params**: none
* **Returns**: `any`
## toggleCommentSidebar()
To toggle comment sidebar.
* **Signature**: `toggleCommentSidebar()`
* **Params**: none
* **Returns**: `any`
## enableModeratorMode()
To enable moderator mode.
* **Signature**: `enableModeratorMode()`
* **Params**: none
* **Returns**: `any`
## disableModeratorMode()
To disable moderator mode.
* **Signature**: `disableModeratorMode()`
* **Params**: none
* **Returns**: `any`
## enableStreamMode()
To enable stream mode.
* **Signature**: `enableStreamMode()`
* **Params**: none
* **Returns**: `any`
## disableStreamMode()
To disable stream mode.
* **Signature**: `disableStreamMode()`
* **Params**: none
* **Returns**: `any`
## enableSignInButton()
To show sign in button in comments if user is signed out.
* **Signature**: `enableSignInButton()`
* **Params**: none
* **Returns**: `any`
## disableSignInButton()
To hide sign in button in comments if user is signed out.
* **Signature**: `disableSignInButton()`
* **Params**: none
* **Returns**: `any`
## enableUpgradeButton()
To enable upgrade button in comments when plan is expired.
* **Signature**: `enableUpgradeButton()`
* **Params**: none
* **Returns**: `any`
## disableUpgradeButton()
To disable upgrade button in comments when plan is expired.
* **Signature**: `disableUpgradeButton()`
* **Params**: none
* **Returns**: `any`
## onCommentModeChange()
Subscribe to comment mode change.
* **Signature**: `onCommentModeChange()`
* **Params**: none
* **Returns**: `Observable`
## enableAttachments()
Enable attachments feature in comments.
* **Signature**: `enableAttachments()`
* **Params**: none
* **Returns**: `any`
## disableAttachments()
Disable attachments feature in comments.
* **Signature**: `disableAttachments()`
* **Params**: none
* **Returns**: `any`
## isUserGlobalContact()
Get if user is part of global contact or not.
* **Signature**: `isUserGlobalContact()`
* **Params**: none
* **Returns**: `Observable`
## enableDeviceInfo()
Enable device info in comments.
* **Signature**: `enableDeviceInfo()`
* **Params**: none
* **Returns**: `any`
## disableDeviceInfo()
Disable device info in comments.
* **Signature**: `disableDeviceInfo()`
* **Params**: none
* **Returns**: `any`
## enableCommentMode()
Enable comment mode to add comments.
* **Signature**: `enableCommentMode()`
* **Params**: none
* **Returns**: `any`
## disableCommentMode()
Disable comment mode.
* **Signature**: `disableCommentMode()`
* **Params**: none
* **Returns**: `any`
## enablePersistentCommentMode()
Enable persistent comment mode.
* **Signature**: `enablePersistentCommentMode()`
* **Params**: none
* **Returns**: `any`
## disablePersistentCommentMode()
Disable persistent comment mode.
* **Signature**: `disablePersistentCommentMode()`
* **Params**: none
* **Returns**: `any`
## enableCommentIndex()
To show comment number.
* **Signature**: `enableCommentIndex()`
* **Params**: none
* **Returns**: `any`
## disableCommentIndex()
To hide comment number.
* **Signature**: `disableCommentIndex()`
* **Params**: none
* **Returns**: `any`
## enablePopoverMode()
To enable popover mode.
* **Signature**: `enablePopoverMode()`
* **Params**: none
* **Returns**: `any`
## disablePopoverMode()
To disable popover mode.
* **Signature**: `disablePopoverMode()`
* **Params**: none
* **Returns**: `any`
## enablePopoverTriangleComponent()
To show triangle in popover mode.
* **Signature**: `enablePopoverTriangleComponent()`
* **Params**: none
* **Returns**: `any`
## disablePopoverTriangleComponent()
To hide triangle in popover mode.
* **Signature**: `disablePopoverTriangleComponent()`
* **Params**: none
* **Returns**: `any`
## enableDialogOnHover()
To enable dialog on hover.
* **Signature**: `enableDialogOnHover()`
* **Params**: none
* **Returns**: `any`
## disableDialogOnHover()
To disable dialog on hover.
* **Signature**: `disableDialogOnHover()`
* **Params**: none
* **Returns**: `any`
## enableDialogOnTargetElementClick()
To enable feature to show dialog on target element click.
* **Signature**: `enableDialogOnTargetElementClick()`
* **Params**: none
* **Returns**: `any`
## disableDialogOnTargetElementClick()
To disable feature to show dialog on target element click.
* **Signature**: `disableDialogOnTargetElementClick()`
* **Params**: none
* **Returns**: `any`
## enablePriority()
To enable feature to set priority on comments.
* **Signature**: `enablePriority()`
* **Params**: none
* **Returns**: `any`
## disablePriority()
To disable feature to set priority on comments.
* **Signature**: `disablePriority()`
* **Params**: none
* **Returns**: `any`
## enableStatus()
To enable the feature to set status on comments.
* **Signature**: `enableStatus()`
* **Params**: none
* **Returns**: `any`
## disableStatus()
To disable the feature to set status on comments.
* **Signature**: `disableStatus()`
* **Params**: none
* **Returns**: `any`
## enableResolveButton()
To enable the feature to show the resolve button.
* **Signature**: `enableResolveButton()`
* **Params**: none
* **Returns**: `any`
## disableResolveButton()
To disable the feature to show the resolve button.
* **Signature**: `disableResolveButton()`
* **Params**: none
* **Returns**: `any`
## enableGhostComments()
To enable the feature to show ghost comments.
* **Signature**: `enableGhostComments()`
* **Params**: none
* **Returns**: `any`
## disableGhostComments()
To disable the feature to show ghost comments.
* **Signature**: `disableGhostComments()`
* **Params**: none
* **Returns**: `any`
## enableGhostCommentsIndicator()
To enable the feature to show ghost comments message in comment dialog and comment sidebar.
* **Signature**: `enableGhostCommentsIndicator()`
* **Params**: none
* **Returns**: `any`
## disableGhostCommentsIndicator()
To disable the feature to show ghost comments message in comment dialog and comment sidebar.
* **Signature**: `disableGhostCommentsIndicator()`
* **Params**: none
* **Returns**: `any`
## setCustomStatus()
Sets custom status filters.
* **Signature**: `setCustomStatus(statuses: CustomStatus[])`
* **Params**: `statuses: CustomStatus[]`
* **Returns**: `void`
## setCustomPriority()
Sets custom priority filters.
* **Signature**: `setCustomPriority(priorities: CustomPriority[])`
* **Params**: `priorities: CustomPriority[]`
* **Returns**: `void`
## setCustomCategory()
Sets custom categories filters.
* **Signature**: `setCustomCategory(categories: CustomCategory[])`
* **Params**: `categories: CustomCategory[]`
* **Returns**: `void`
## enableInboxMode()
To enable inbox mode.
* **Signature**: `enableInboxMode()`
* **Params**: none
* **Returns**: `any`
## disableInboxMode()
To disable inbox mode.
* **Signature**: `disableInboxMode()`
* **Params**: none
* **Returns**: `any`
## enableAutoCategorize()
To enable auto categorize feature.
* **Signature**: `enableAutoCategorize()`
* **Params**: none
* **Returns**: `any`
## disableAutoCategorize()
To disable auto categorize feature.
* **Signature**: `disableAutoCategorize()`
* **Params**: none
* **Returns**: `any`
## enableDarkMode()
To enable dark mode in comments.
* **Signature**: `enableDarkMode()`
* **Params**: none
* **Returns**: `any`
## disableDarkMode()
To disable dark mode in comments.
* **Signature**: `disableDarkMode()`
* **Params**: none
* **Returns**: `any`
## setContextProvider()
Sets the comment context provider.
* **Signature**: `setContextProvider(provider: (documentId: string, location?: any) => any)`
* **Params**: `provider: (documentId: string, location?: any) => any`
* **Returns**: `void`
## enableSuggestionMode()
To enable suggestion mode.
* **Signature**: `enableSuggestionMode()`
* **Params**: none
* **Returns**: `any`
## disableSuggestionMode()
To disable suggestion mode.
* **Signature**: `disableSuggestionMode()`
* **Params**: none
* **Returns**: `any`
## enableMobileMode()
To enable mobile mode.
* **Signature**: `enableMobileMode()`
* **Params**: none
* **Returns**: `any`
## disableMobileMode()
To disable mobile mode.
* **Signature**: `disableMobileMode()`
* **Params**: none
* **Returns**: `any`
## enableInlineCommentMode()
To enable inline comment mode.
* **Signature**: `enableInlineCommentMode()`
* **Params**: none
* **Returns**: `any`
## disableInlineCommentMode()
To disable inline comment mode.
* **Signature**: `disableInlineCommentMode()`
* **Params**: none
* **Returns**: `any`
## enableMinimap()
To enable minimap.
* **Signature**: `enableMinimap()`
* **Params**: none
* **Returns**: `any`
## disableMinimap()
To disable minimap.
* **Signature**: `disableMinimap()`
* **Params**: none
* **Returns**: `any`
## showCommentsOnDom()
To show comments on DOM.
* **Signature**: `showCommentsOnDom()`
* **Params**: none
* **Returns**: `any`
## hideCommentsOnDom()
To hide comments on DOM.
* **Signature**: `hideCommentsOnDom()`
* **Params**: none
* **Returns**: `any`
## enableCommentTool()
To enable comment tool.
* **Signature**: `enableCommentTool()`
* **Params**: none
* **Returns**: `any`
## disableCommentTool()
To disable comment tool.
* **Signature**: `disableCommentTool()`
* **Params**: none
* **Returns**: `any`
## setTotalMediaLength()
To set total media length.
* **Signature**: `setTotalMediaLength(totalMediaLength: number)`
* **Params**: `totalMediaLength: number`
* **Returns**: `void`
## onCommentAdd()
To get documentId, location, and set context data when a comment is added.
* **Signature**: `onCommentAdd()`
* **Params**: none
* **Returns**: `Observable`
## onCommentUpdate()
To get data when a comment is updated.
* **Signature**: `onCommentUpdate()`
* **Params**: none
* **Returns**: `Observable`
## addCommentOnSelectedText()
To add a comment on selected text.
* **Signature**: `addCommentOnSelectedText()`
* **Params**: none
* **Returns**: `void`
## enableSidebarUrlNavigation()
To enable navigation when a comment is clicked in the sidebar.
* **Signature**: `enableSidebarUrlNavigation()`
* **Params**: none
* **Returns**: `void`
## disableSidebarUrlNavigation()
To disable navigation when a comment is clicked in the sidebar.
* **Signature**: `disableSidebarUrlNavigation()`
* **Params**: none
* **Returns**: `void`
## enableSidebarButtonOnCommentDialog()
To enable the sidebar button on the comment dialog.
* **Signature**: `enableSidebarButtonOnCommentDialog()`
* **Params**: none
* **Returns**: `void`
## disableSidebarButtonOnCommentDialog()
To disable the sidebar button on the comment dialog.
* **Signature**: `disableSidebarButtonOnCommentDialog()`
* **Params**: none
* **Returns**: `void`
## onSidebarButtonOnCommentDialogClick()
To detect a click on the sidebar button on the comment dialog.
* **Signature**: `onSidebarButtonOnCommentDialogClick()`
* **Params**: none
* **Returns**: `Observable`
## enableReactions()
To enable reactions in comments.
* **Signature**: `enableReactions()`
* **Params**: none
* **Returns**: `void`
## disableReactions()
To disable reactions in comments.
* **Signature**: `disableReactions()`
* **Params**: none
* **Returns**: `void`
## setRecordings()
To set allowed recordings in comments.
* **Signature**: `setRecordings(allowedRecordings: string | string[])`
* **Params**: `allowedRecordings: string | string[]`
* **Returns**: `void`
## addManualComment()
To add manual comment with context.
* **Signature**: `addManualComment(config: ManualCommentAnnotationConfig)`
* **Params**: `config: ManualCommentAnnotationConfig`
* **Returns**: `void`
## addCommentOnElement()
To manually add a comment on an element.
* **Signature**: `addCommentOnElement(data: CommentOnElementConfig)`
* **Params**: `data: CommentOnElementConfig`
* **Returns**: `void`
## enablePrivateCommentMode()
To enable private comment mode to add private comments. This mode can be enabled for admin users only.
* **Signature**: `enablePrivateCommentMode()`
* **Params**: none
* **Returns**: `void`
## disablePrivateCommentMode()
To disable private comment mode.
* **Signature**: `disablePrivateCommentMode()`
* **Params**: none
* **Returns**: `void`
## enableScrollToComment()
To enable scroll to comment.
* **Signature**: `enableScrollToComment()`
* **Params**: none
* **Returns**: `void`
## disableScrollToComment()
To disable scroll to comment.
* **Signature**: `disableScrollToComment()`
* **Params**: none
* **Returns**: `void`
## enableUserMentions()
To enable user mentions in comments.
* **Signature**: `enableUserMentions()`
* **Params**: none
* **Returns**: `void`
## disableUserMentions()
To disable user mentions in comments.
* **Signature**: `disableUserMentions()`
* **Params**: none
* **Returns**: `void`
## setCommentSidebarFilters()
To apply filters on comment sidebar.
* **Signature**: `setCommentSidebarFilters(filters: any)`
* **Params**: `filters: any`
* **Returns**: `void`
## setPinCursorImage()
To set custom cursor for comment.
* **Signature**: `setPinCursorImage(imageString: string)`
* **Params**: `imageString`
* **Returns**: `void`
## enableCommentPinHighlighter()
To enable comment pin highlighter.
* **Signature**: `enableCommentPinHighlighter()`
* **Params**: none
* **Returns**: `void`
## disableCommentPinHighlighter()
To disable comment pin highlighter.
* **Signature**: `disableCommentPinHighlighter()`
* **Params**: none
* **Returns**: `void`
## enableDeleteOnBackspace()
To enable deleting comment on backspace.
* **Signature**: `enableDeleteOnBackspace()`
* **Params**: none
* **Returns**: `void`
## disableDeleteOnBackspace()
To disable deleting comment on backspace.
* **Signature**: `disableDeleteOnBackspace()`
* **Params**: none
* **Returns**: `void`
## enableHotkey()
To enable hotkey.
* **Signature**: `enableHotkey()`
* **Params**: none
* **Returns**: `void`
## disableHotkey()
To disable hotkey.
* **Signature**: `disableHotkey()`
* **Params**: none
* **Returns**: `void`
## createCustomListDataOnComment()
To create custom list data on comment.
* **Signature**: `createCustomListDataOnComment(customListDataOnComment)`
* **Params**: `customListDataOnComment`
* **Returns**: `void`
## createCustomListDataOnAnnotation()
To create custom list data on annotations.
* **Signature**: `createCustomListDataOnAnnotation(customChipDropdownData)`
* **Params**: `customChipDropdownData`
* **Returns**: `void`
# null
## getOnlineUsersOnCurrentDocument()
Subscribe to cursors of all online users who are either active or inactive on the current document.
* **Signature**: `getOnlineUsersOnCurrentDocument: () => Observable`
* **Params**: none
* **Returns**: `Observable`
## setInactivityTime()
This method sets the inactivity time in milliseconds.
* **Signature**: `setInactivityTime: (milliseconds: number) => void;`
* **Params**: `milliseconds: number`
* **Returns**: `void`
## allowedElementIds()
To allow cursors in specific elements only.
* **Signature**: `allowedElementIds: (elementIds: string[]) => any`
* **Params**: `elementIds: string[]`
* **Returns**: `any`
# null
## getLiveStateData()
Retrieves live state data as an observable based on the provided ID.
* **Signature**: `getLiveStateData(liveStateDataId?: string): Observable`
* **Params**: `liveStateDataId?: string`
* **Returns**: `Observable`
## setLiveStateData()
Sets live state data for the provided ID and data.
* **Signature**: `setLiveStateData(liveStateDataId: string, liveStateData: any): any`
* **Params**: `liveStateDataId: string , liveStateData: any`
* **Returns**: `any`
## enableSingleEditorMode()
Enables the single editor mode with an optional configuration.
* **Signature**: `enableSingleEditorMode(config?: SingleEditorConfig): void`
* **Params**: `config?: SingleEditorConfig`
* **Returns**: `void`
## disableSingleEditorMode()
Disables the single editor mode.
* **Signature**: `disableSingleEditorMode(): void`
* **Params**: none
* **Returns**: `void`
## isUserEditor()
Checks if the current user is an editor. Returns an observable.
* **Signature**: `isUserEditor(): Observable`
* **Params**: none
* **Returns**: `Observable`
## getEditor()
Retrieves the current editor information.
* **Signature**: `getEditor(): Observable`
* **Params**: none
* **Returns**: `Observable`
## setUserAsEditor()
Sets the current user as an editor.
* **Signature**: `setUserAsEditor(): void`
* **Params**: none
* **Returns**: `void`
## resetUserAccess()
Resets the user access.
* **Signature**: `resetUserAccess(): void`
* **Params**: none
* **Returns**: `void`
## singleEditorModeContainerIds()
Disables elements inside specific elements only when single editor mode is on.
* **Signature**: `singleEditorModeContainerIds(elementIds: string[]): void`
* **Params**: `elementIds: string[]`
* **Returns**: `void`
## enableAutoSyncState()
Enables the autosync state feature.
* **Signature**: `enableAutoSyncState(): void`
* **Params**: none
* **Returns**: `void`
## disableAutoSyncState()
Disables the autosync state feature.
* **Signature**: `disableAutoSyncState(): void`
* **Params**: none
* **Returns**: `void`
## requestEditorAccess()
Initiates a request for editor access.
* **Signature**: `requestEditorAccess(): Observable`
* **Params**: none
* **Returns**: `Observable`
## isEditorAccessRequested()
Checks if editor access has been requested.
* **Signature**: `isEditorAccessRequested(): Observable<{ requestStatus: string, requestedBy: User } | null>`
* **Params**: none
* **Returns**: `Observable<{ requestStatus: string, requestedBy: User } | null>`
## acceptEditorAccessRequest()
Accepts an editor access request.
* **Signature**: `acceptEditorAccessRequest(): void`
* **Params**: none
* **Returns**: `void`
## rejectEditorAccessRequest()
Rejects an editor access request.
* **Signature**: `rejectEditorAccessRequest(): void`
* **Params**: none
* **Returns**: `void`
## cancelEditorAccessRequest()
Cancels an editor access request.
* **Signature**: `cancelEditorAccessRequest(): void`
* **Params**: none
* **Returns**: `void`
## editCurrentTab()
Edits the current tab.
* **Signature**: `editCurrentTab(): void`
* **Params**: none
* **Returns**: `void`
## setEditorAccessTimeout()
Sets a timeout for editor access. (in milliseconds)
* **Signature**: `setEditorAccessTimeout(timeout: number): void`
* **Params**: `timeout: number`
* **Returns**: `void`
## enableEditorAccessTransferOnTimeOut()
Enables the transfer of editor access on timeout.
* **Signature**: `enableEditorAccessTransferOnTimeOut(): void`
* **Params**: none
* **Returns**: `void`
## disableEditorAccessTransferOnTimeOut()
Disables the transfer of editor access on timeout.
* **Signature**: `disableEditorAccessTransferOnTimeOut(): void`
* **Params**: none
* **Returns**: `void`
## enableDefaultSingleEditorUI()
Enables the default UI for single editor mode.
* **Signature**: `enableDefaultSingleEditorUI(): void`
* **Params**: none
* **Returns**: `void`
## disableDefaultSingleEditorUI()
Disables the default UI for single editor mode.
* **Signature**: `disableDefaultSingleEditorUI(): void`
* **Params**: none
* **Returns**: `void`
# null
## setMaxDays()
To set maximum days within the notification tool and notification element.
* **Signature**: `setMaxDays(days)`
* **Params**: `days`
* **Returns**: void
# null
## getOnlineUsersOnCurrentDocument()
Subscribe to a list of all online users who are either active or inactive on the current document. You can also use this find how many users are online on the current document and show them custom messages.
Example:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.getOnlineUsersOnCurrentDocument();
```
* **Signature**: `getOnlineUsersOnCurrentDocument: () => Observable`
* **Params**: none
* **Returns**: `Observable`
## setInactivityTime()
By default if the user is inactive for >5 minutes, we mark them offline. By inactive we mean they haven't moved their mouse or haven't made any keyboard event. You can configure this default threshold using this method.
Example:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.setInactivityTime();
```
* **Signature**: `setInactivityTime: (milliseconds: number) => void`
* **Params**: `milliseconds: number`
* **Returns**: `void`
## enableFollowAlongMode()
This method enables Follow Along Mode.
Example:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.enableFollowAlongMode();
```
* **Signature**: `enableFollowAlongMode: (useHistoryAPI: boolean) => any`
* **Params**: `useHistoryAPI: boolean`
* **Returns**: `any`
## disableFollowAlongMode()
This method disables Follow Along Mode.
Example:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.disableFollowAlongMode();
```
* **Signature**: `disableFollowAlongMode: () => any`
* **Params**: none
* **Returns**: `any`
# null
## initRecording()
This method initializes recording.
Example:
```jsx
const recorderElement = client.getRecorderElement();
recorderElement.initRecording(type: string, panelId?: string);
```
* **Signature**: `(type: string, panelId?: string) => any`
* **Params**: `type: string, panelId?: string`
* **Returns**: `any`
## onRecordedData()
This method listens to recorded data.
Example:
```jsx
const recorderElement = client.getRecorderElement();
recorderElement.onRecordedData((id,tag) => {
console.log(id,tag)
});
```
* **Signature**: `() => Observable`
* **Params**: none
* **Returns**: `any`
# null
## enableRewriter()
This method enables the rewriter.
Example:
```jsx
const rewriterElement = client.getRewriterElement();
rewriterElement.enableRewriter();
```
* **Signature**: `enableRewriter() => void`
* **Params**: none
* **Returns**: `void`
## disableRewriter()
This method disables the rewriter.
Example:
```jsx
const rewriterElement = client.getRewriterElement();
rewriterElement.disableRewriter();
```
* **Signature**: `disableRewriter() => void`
* **Params**: none
* **Returns**: void
# null
## enableLiveSelection()
This method enables live selection mode.
Example:
```jsx
const liveSelectionElement = client.getSelectionElement();
liveSelectionElement.enableLiveSelection();
```
* **Signature**: `enableLiveSelection() => void`
* **Params**: none
* **Returns**: `void`
## disableLiveSelection()
This method disables live selection mode.
Example:
```jsx
const liveSelectionElement = client.getSelectionElement();
liveSelectionElement.disableLiveSelection();
```
* **Signature**: `disableLiveSelection() => void`
* **Params**: none
* **Returns**: `void`
# null
## initConfig()
Used to set up initial configurations for the Velt SDK.
* **Signature**: `initConfig(apiKey: string, config?: Config)`
* **Params**: `apiKey: string, config?: Config`
* **Returns**: none
## identify()
Used to authenticate the client's user with the Velt SDK.
* **Signature**: `identify(user: User, userOptions?: UserOptions)`
* **Params**: `user: User, userOptions?: UserOptions`
* **Returns**: `Promise`
## setDocumentId()
Tell us the unique ID of the current document/resource on which you want to enable collaboration.
* **Signature**: `setDocumentId(id: string)`
* **Params**: `id: string`
* **Returns**: none
## unsetDocumentId()
You can unset the Document Id to pause the Velt SDK functions.
* **Signature**: `unsetDocumentId()`
* **Params**: none
* **Returns**: none
## setLocation()
Tell us the custom params of the current document/resource to identify sub document inside a document.
* **Signature**: `setLocation(params: Location, appendLocation?: boolean)`
* **Params**: `params: Location, appendLocation?: boolean`
* **Returns**: none
## removeLocation()
To remove location from a User.
* **Signature**: `removeLocation(location?: Location)`
* **Params**: `location?: Location`
* **Returns**: none
## setScrollableElementsIds()
Tell us about the scrollable document ids to keep track on its scroll changes.
* **Signature**: `setScrollableElementsIds(ids: string[])`
* **Params**: `ids: string[]`
* **Returns**: none
## removeScrollableElementsIds()
To remove document params from a User.
* **Signature**: `removeScrollableElementsIds()`
* **Params**: none
* **Returns**: none
## getPresenceElement()
Get the Presence Element Object to access the raw presence data.
* **Signature**: `getPresenceElement()`
* **Params**: none
* **Returns**: `PresenceElement`
## getCursorElement()
Get the Cursor Element Object to access the raw cursor data.
* **Signature**: `getCursorElement()`
* **Params**: none
* **Returns**: `CursorElement`
## getCommentElement()
Get the Comment Element Object to access the raw comment data.
* **Signature**: `getCommentElement()`
* **Params**: none
* **Returns**: `CommentElement`
## getTagElement()
Get the Tag Pin Annotation Object to access the raw tag data.
* **Signature**: `getTagElement()`
* **Params**: none
* **Returns**: `TagElement`
## getSelectionElement()
Get the Selection Object to enable/disable the feature.
* **Signature**: `getSelectionElement()`
* **Params**: none
* **Returns**: `SelectionElement`
## getRecorderElement()
Get the Recorder Object.
* **Signature**: `getRecorderElement()`
* **Params**: none
* **Returns**: `RecorderElement`
## getContactElement()
Get the Contact Object.
* **Signature**: `getContactElement()`
* **Params**: none
* **Returns**: `ContactElement`
## getRewriterElement()
Get the Rewriter Object.
* **Signature**: `getRewriterElement()`
* **Params**: none
* **Returns**: `RewriterElement`
## getLiveStateSyncElement()
Get the LiveStateSync Object.
* **Signature**: `getLiveStateSyncElement()`
* **Params**: none
* **Returns**: `LiveStateSyncElement`
## getArrowElement()
Get the Arrow Object.
* **Signature**: `getArrowElement()`
* **Params**: none
* **Returns**: `ArrowElement`
## signOutUser()
To sign out a user.
* **Signature**: `signOutUser()`
* **Params**: none
* **Returns**: `any`
## disableFeatures()
Provide a list of features to disable. Provide an empty array to enable all the features.
* **Signature**: `disableFeatures(features: Array)`
* **Params**: `features: Array`
* **Returns**: none
## addLocation()
Add location to show comments, tags, recorders etc. for provided location also.
* **Signature**: `addLocation(location: any)`
* **Params**: `location: any`
* **Returns**: none
## isUserAllowed\$()
To check if user is allowed in provided document or not.
* **Signature**: `isUserAllowed$()`
* **Params**: none
* **Returns**: `Observable`
## getUserRole\$()
To get user role in provided document.
* **Signature**: `getUserRole$()`
* **Params**: none
* **Returns**: `Observable`
## isPlanExpired\$()
To check if plan is expired or not.
* **Signature**: `isPlanExpired$()`
* **Params**: none
* **Returns**: `Observable`
## inviteUsers()
To invite a list of users to the document.
* **Signature**: `inviteUsers(documentUsers: Array)`
* **Params**: `documentUsers: Array`
* **Returns**: none
## removeVeltContent()
To remove Velt specific content from provided html content.
* **Signature**: `removeVeltContent(htmlContent: string)`
* **Params**: `htmlContent: string`
* **Returns**: `string`
## injectCustomCss()
To inject custom CSS.
* **Signature**: `injectCustomCss(customCss: CustomCss)`
* **Params**: `customCss: CustomCss`
* **Returns**: none
## setLanguage()
To set the language.
* **Signature**: `setLanguage(language: string)`
* **Params**: `language: string`
* **Returns**: none
## setTranslations()
To set the translations for the language.
* **Signature**: `setTranslations(language: string, translations: { [key: string]: string })`
* **Params**: `language: string, translations: { [key: string]: string }`
* **Returns**: none
## enableAutoTranslation()
To enable auto translation.
* **Signature**: `enableAutoTranslation()`
* **Params**: none
* **Returns**: none
## disableAutoTranslation()
To disable auto translation.
* **Signature**: `disableAutoTranslation()`
* **Params**: none
* **Returns**: none
## getVeltInitState()
Subscribe to detect whether Velt is initialized.
* **Signature**: `getVeltInitState()`
* **Params**: none
* **Returns**: `Observable`
# React Hooks
## useIdentify()
Hook that calls [client.identify()](/get-started/setup/authenticate)
```jsx
import { useIdentify } from "@veltdev/react";
import { useEffect, useState } from "react";
export default function YourAuthComponent() {
// Create the Velt user object
let veltUser = {
userId: "your-user-id",
name: "John Smith"
email: "jsmith@velt.dev",
photoUrl: "https://i.pravatar.cc/304",
organizationId: "theoffice",
};
//identify Velt user
useIdentify(veltUser)
return (
Your Auth Template
)
}
```
## useSetDocumentId()
Hook that calls [client.setDocumentId()](/get-started/setup/initialize-document)
```jsx
import { useSetDocumentId } from '@veltdev/react';
export default function YourDocument() {
useSetDocumentId("my-document-id")
return (
Your Document Template
)
}
```
## useUnsetDocumentId()
Hook that calls [client.unsetDocumentId()](https://docs.velt.dev/api-reference/api-methods/velt-client)
```jsx
import { useUnsetDocumentId } from '@veltdev/react';
export default function YourDocument() {
useUnsetDocumentId()
return (
Your Document Template
)
}
```
## useSetLocation()
Hook that calls [client.setLocation()](/key-concepts/locations/set-location)
Hooks currently do not support automatic change detection in variables.
```jsx
import { useSetLocation } from '@veltdev/react';
export default function YourDocument() {
const handleSetLocation = () => {
useSetLocation({
scene: "scene1"
})
}
return (
)
}
```
## useLiveStateSyncUtils()
Hook that calls [client.getLiveStateSyncElement()](/realtime-collaboration/single-editor-mode/setup)
```jsx
import { useLiveStateSyncUtils } from '@veltdev/react';
export default function YourDocument() {
let liveStateSyncElement = useLiveStateSyncUtils()
//Enable Single Editor Mode
liveStateSyncElement?.enableSingleEditorMode();
return (
)
}
```
## useLiveStateData()
Hook that calls [liveStateSyncElement.getLiveStateData()](/realtime-collaboration/live-state-sync/setup)
```jsx
import { useLiveStateData, useSetLiveStateData} from '@veltdev/react';
export default function YourDocument() {
//sets the live state data object
useSetLiveStateData("myUniqueId", {
"myKey": "hello world!",
})
let data = useLiveStateData("myUniqueId")
return (
{data?.myKey}
)
}
```
## useSetLiveStateData()
Hook that calls [liveStateSyncElement.setLiveStateData()](/realtime-collaboration/live-state-sync/setup)
```jsx
import { useLiveStateData, useSetLiveStateData} from '@veltdev/react';
export default function YourDocument() {
useSetLiveStateData("myUniqueId", {
"myKey": "hello world!",
})
//gets the live state data object
let data = useLiveStateData("myUniqueId")
return (
{data?.myKey}
)
}
```
## useEditor()
The `useEditor()` hook is used to call [liveStateSyncElement.getEditor()](/realtime-collaboration/single-editor-mode/setup) without having to handle the subscription.
```jsx
import { useEditor} from '@veltdev/react';
export default function YourDocument() {
let user = useEditor()
console.log("Editor User Data: ", user)
return (
)
}
```
## useUserEditorState()
Hook that calls [liveStateSyncElement.isUserEditor()](/realtime-collaboration/single-editor-mode/setup)
```jsx
import { useUserEditorState} from '@veltdev/react';
export default function YourDocument() {
let isEditor = useUserEditorState()
console.log("Is User Editor?: ", isEditor)
return (
Is User Editor?: {isEditor}
)
}
```
## useEditorAccessTimer()
With the `useEditorAccessTimer` hook, you can get the Editor Access Timer state.
```jsx
import { useEditorAccessTimer } from '@veltdev/react';
function YourReactComponent {
const editorAccessTimer = useEditorAccessTimer();
useEffect(() => {
if (editorAccessTimer?.state === 'completed') {
onEditorAccessTimerCompleted();
}
}, [editorAccessTimer]);
const onEditorAccessTimerCompleted = () => {
// If user is editor, make requester as editor
acceptEditorAccessRequest();
// If user is requester, make it as editor
setUserAsEditor();
}
}
```
The `editorAccessTimer` class has the following schema:
* `state`: `'idle'` | `'inProgress'` | `'completed' `
* `durationLeft`: `number`
## useEditorAccessRequestHandler()
Hook that calls [liveStateSyncElement.isEditorAccessRequested()](/realtime-collaboration/single-editor-mode/setup)
```jsx
import { useEditorAccessRequestHandler } from '@veltdev/react';
export default function YourDocument= () {
const editorAccessRequested = useEditorAccessRequestHandler();
return (
Is Editor Access Being Requested? : {editorAccessRequested}
)
}
```
## usePresenceUtils()
Hook that calls [client.getPresenceElement()](/realtime-collaboration/presence/customize-ui/create-your-own-ui)
```jsx
import { usePresenceUtils } from '@veltdev/react';
export default function YourDocument() {
let presenceElement = usePresenceUtils();
return (
)
}
```
## useCursorUtils()
Hook that calls [client.getCursorElement()](/realtime-collaboration/cursors/customize-ui/create-your-own-ui)
```jsx
import { useCursorUtils } from '@veltdev/react';
export default function YourDocument() {
let cursorElement = useCursorUtils();
return (
)
}
```
## useCommentUtils()
Hook that calls `client.getCommentElement()`.
```jsx
import { useCommentUtils } from '@veltdev/react';
export default function YourDocument() {
let commentElement = useCommentUtils();
return (
)
}
```
## useLiveSelectionUtils()
Hook that calls `client.getLiveSelectElement()`.
```jsx
import { useLiveSelectionUtils } from '@veltdev/react';
export default function YourDocument() {
let liveSelectionElement = useLiveSelectionUtils()
return (
)
}
```
## useRecorderUtils()
Hook that calls `client.getRecorderElement()`.
```jsx
import { useRecorderUtils } from '@veltdev/react';
export default function YourDocument() {
let recorderElement = useRecorderUtils();
return (
)
}
```
## useContactUtils()
Hook that calls `client.getContactElement()`.
```jsx
import { useContactUtils } from '@veltdev/react';
export default function YourDocument() {
let contactElement = useContactUtils();
return (
)
}
```
## useContactSelected()
Hook that calls `client.onContactSelected()`.
```jsx
import React, { useEffect } from 'react';
import { useContactUtils, useContactSelected } from '@veltdev/react';
function YourComponent() {
const contactUtils = useContactUtils();
useEffect(() => {
console.log('contactUtils: ', contactUtils);
}, [contactUtils]);
const onContactSelected = useContactSelected();
useEffect(() => {
console.log('onContactSelected: ', onContactSelected);
}, [onContactSelected]);
return (
// Your component code
);
}
```
## useAIRewriterUtils()
Hook that calls `client.getRewriterElement()`.
```jsx
import { useAIRewriterUtils } from '@veltdev/react';
export default function YourDocument() {
let AIRewriterElement = useAIRewriterUtils();
return (
)
}
```
## useHuddleUtils()
Hook that calls `client.getHuddleElement()`.
```jsx
import { useHuddleUtils } from '@veltdev/react';
export default function YourDocument() {
let huddleElement = useHuddleUtils();
return (
)
}
```
## useCommentAnnotations()
Hook that calls `commentElement.getAllCommentAnnotations()`
See [CommentAnnotations Class Model](/api-reference/models/CommentAnnotation)
```jsx
import { useCommentAnnotations } from '@veltdev/react';
export default function YourDocument() {
let commentAnnotations = useCommentAnnotations('my-document-id', { id:'my-location-id',locationName:"MyLocationName"})
return (
{commentAnnotations.map(x =>
{x.annotationId}
)}
)
}
```
## useCommentModeState()
Hook that calls [commentElement.onCommentModeChange()](/async-collaboration/comments/customize-behavior/event-handlers)
```jsx
import { useCommentModeState } from '@veltdev/react';
export default function YourDocument() {
let commentModeState = useCommentModeState()
return (
)
}
```
## useUnreadCommentCountOnCurrentDocument
```jsx
import { useUnreadCommentCountOnCurrentDocument } from '@veltdev/react';
export default function YourDocument() {
let unreadCommentCountOnCurrentDocument = useUnreadCommentCountOnCurrentDocument();
return (
Unread Commment Count on Current Document: {unreadCommentCountOnCurrentDocument}
)
}
```
## useUnreadCommentAnnotationCountByLocationId
```jsx
import { useUnreadCommentAnnotationCountByLocationId } from '@veltdev/react';
export default function YourDocument() {
let unreadCommentAnnotationCountByLocationId = useUnreadCommentAnnotationCountByLocationId(locationId);
return (
Unread Comment Annotations By Location: {unreadCommentAnnotationCountByLocationId}
)
}
```
## useUnreadCommentCountByLocationId
```jsx
import { useUnreadCommentCountByLocationId } from '@veltdev/react';
export default function YourDocument() {
let unreadCommentCountByLocationId = useUnreadCommentCountByLocationId(locationId);
return (
Unread Comments By Location: {unreadCommentCountByLocationId}
)
}
```
# null
## AutocompleteItem Class
| Property | Type | Optional | Description |
| ----------- | ------ | -------- | -------------------------- |
| id | string | No | Unique identifier |
| name | string | No | Name of the item |
| description | string | Yes | Optional description |
| icon | object | Yes | Optional icon information |
| icon.url | string | Yes | URL of the icon |
| icon.svg | string | Yes | SVG representation of icon |
| link | string | Yes | Link associated with item |
# null
## `Comment` Class
| Property | Type | Optional | Description |
| ----------------------- | ------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `commentId` | number | No | Unique identifier for the comment pin annotation. Auto generated. |
| `type` | 'text' \| 'voice' | No | This determines the comment content type. Default is 'text'. |
| `commentText` | string | No | The actual text content of the comment. |
| `commentHtml` | string | Yes | Same comment text but formatted in HTML. |
| `replaceContentHtml` | string | Yes | HTML content to replace the comment text when user accepts the comment. |
| `replaceContentText` | string | Yes | Text content to replace the comment text when user accepts the comment. |
| `commentVoiceUrl` | string | Yes | URL of the voice recording for the comment, if available. |
| `from` | User | No | The user who created this comment. |
| `to` | User\[] | Yes | List of users that were @mentioned in this comment. |
| `lastUpdated` | Date | Yes | Timestamp of when this comment was last updated. Auto generated. |
| `editedAt` | any | Yes | Timestamp of when this comment was edited. Auto generated. |
| `createdAt` | any | Yes | Timestamp of when this comment was created. Auto generated. |
| `isEdited` | boolean | Yes | Whether the comment has been edited. Auto generated. |
| `status` | 'added' \| 'updated' | No | Status of the comment indicating whether it was newly added or updated. |
| `attachments` | [Attachment](#attachment-class-properties)\[] | No | List of attachments associated with the comment. |
| `recorders` | [RecordedData](/api-reference/models/Recorder#recordeddata-class-properties)\[] | No | List of recorded data associated with the comment. |
| `reactionAnnotationIds` | string\[] | No | List of annotation IDs for reactions to the comment. |
| `taggedUserContacts` | [AutocompleteUserContactReplaceData](#autocompleteusercontactreplacedataclassproperties)\[] | No | List of users that were @mentioned in this comment. This contains addition metadata that is used to display the @mention in the UI. |
| `customList` | [AutocompleteReplaceData](#autocompletereplacedataclassproperties)\[] | No | List of custom list items added to the comment. |
| `isDraft` | boolean | No | Whether the comment is in draft state. |
## `AutocompleteUserContactReplaceData` Class
| Property | Type | Optional | Description |
| --------- | ------ | -------- | ------------------------------------------------------------------ |
| `text` | string | No | The text displayed in the comment that represents the tagged user. |
| `userId` | string | No | The user ID of the tagged user. |
| `contact` | User | Yes | The user object of the tagged user. |
## `AutocompleteReplaceData` Class
| Property | Type | Optional | Description |
| -------- | ---------------- | -------- | ------------------------------------------------------------------ |
| `text` | string | No | The text displayed in the comment that represents the custom item. |
| `custom` | AutocompleteItem | No | The custom item object associated with this text. |
## `AutocompleteItem` Class Properties
| Property | Type | Optional | Description |
| ------------- | -------------------------------- | -------- | ----------------------------------------------------------------------- |
| `id` | string | No | Unique identifier for the autocomplete item. |
| `name` | string | No | Name or label of the autocomplete item. |
| `description` | string | Yes | Additional description of the autocomplete item. |
| `icon` | `{ url?: string, svg?: string }` | Yes | Icon associated with the autocomplete item. Can be a URL or SVG string. |
| `link` | string | Yes | Optional link associated with the autocomplete item. |
## `AutocompleteData` Class
| Property | Type | Optional | Description |
| ------------- | -------------------------------- | -------- | ----------------------------------------------------------------------------- |
| `hotkey` | string | No | The hotkey or trigger for this autocomplete data. |
| `description` | string | Yes | Optional description of the autocomplete data. |
| `type` | 'custom' \| 'contact' \| 'group' | No | The type of autocomplete data. Default is 'custom'. |
| `data` | AutocompleteItem\[] | No | An array of AutocompleteItem objects containing the actual autocomplete data. |
## `Attachment` Class
| Property | Type | Optional | Description |
| -------------------------- | ------ | -------- | ----------------------------------------------------- |
| `attachmentId` | number | No | Unique identifier for the attachment. Auto-generated. |
| `name` | string | Yes | File name of the attachment. |
| `size` | number | Yes | File size of the attachment. |
| `type` | string | Yes | File type of the attachment. |
| `url` | string | Yes | Download URL of the attachment. |
| `thumbnail` | string | Yes | Thumbnail image in base64 format. |
| `thumbnailWithPlayIconUrl` | string | Yes | URL of the thumbnail with a play icon overlay. |
| `metadata` | any | Yes | Additional metadata of the attachment. |
| `mimeType` | any | Yes | MIME type of the attachment. |
# CommentAnnotation
## `CommentAnnotation` Class
| Property | Type | Optional | Description |
| ------------------------- | ------------------------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `annotationId` | string | No | Unique identifier for the comment pin annotation. Auto generated. |
| `comments` | [Comment](/api-reference/models/Comment)\[] | No | The list of all comments part of this annotation. |
| `commentCategories` | [CustomCategory](#customcategory-interface-properties)\[] | No | The list of categories that this comment pin annotation belongs to. |
| `from` | User | No | The user who created this comment pin annotation. |
| `color` | string | Yes | Color used for the comment pin annotation. |
| `resolved` | boolean | Yes | Whether the comment annotation is marked resolved. Deprecated. |
| `inProgress` | boolean | Yes | Whether the comment annotation is marked as in progress. Deprecated. |
| `lastUpdated` | any | Yes | Timestamp when the comment annotation was last updated. Auto generated. |
| `createdAt` | any | Yes | Timestamp when the comment annotation was created. Auto generated. |
| `position` | CursorPosition \| null | Yes | Cursor position relative to the comment annotation. |
| `locationId` | number \| null | Yes | Unique location id generated from provided location. |
| `location` | Location \| null | Yes | Set location to identify user on sub document. |
| `type` | string | Yes | Type of the comment annotation. |
| `selectAllContent` | boolean | Yes | If true, sets text comment annotation on all the text content. |
| `approved` | boolean | Yes | Whether the comment annotation is approved. |
| `status` | [CustomStatus](#customstatus-interface-properties) | No | Status of the comment annotation. Default: [CommentAnnotationStatusMap.OPEN](#default-status-object) |
| `annotationIndex` | number | Yes | Index of current annotation in the list. |
| `pageInfo` | [PageInfo](/api-reference/models/PageInfo) | Yes | Page information related to the comment annotation. |
| `assignedTo` | User | Yes | User to whom the comment annotation is assigned. |
| `priority` | [CustomPriority](#custompriority-interface-properties) | Yes | Priority level of the comment annotation. |
| `ghostComment` | GhostComment \| null | Yes | Placeholder for a non-existing comment. |
| `context` | any | Yes | Custom context data provided by the user. |
| `resolvedByUserId` | string | Yes | ID of the user who resolved the comment. |
| `subscribedUsers` | [CommentAnnotationSubscribedUsers](#commentannotationsubscribedusers-class-properties) | Yes | Users who explicitly subscribe to the comment. |
| `unsubscribedUsers` | [CommentAnnotationUnsubscribedUsers](#commentannotationunsubscribedusers-class-properties) | Yes | Users who explicitly unsubscribe to the comment. |
| `multiThreadAnnotationId` | string | Yes | This is the ID of the multithread annotation group it belongs to, if this was created when multithread mode was on. |
| `isDraft` | boolean | Yes | Indicates if the comment annotation is in draft state. |
| `customList` | [CustomAnnotationDropdownItem](#customannotationdropdownitem)\[] | Yes | Custom list of items for the comment annotation. |
## `CommentAnnotationSubscribedUsers` Class
| Property | Type | Optional | Description | |
| ------------ | -------- | -------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `userIdHash` | string | No | The user ID of the subscribed user. | |
| `user` | User | No | The user object of the subscribed user. | |
| `type` | 'manual' | 'auto' | No | Manual: if the user used the UI option to subscribe; Auto: When the system automatically adds the user to the subscribed list. eg: when the user creates a comment annotation. |
## `CommentAnnotationUnsubscribedUsers` Class
| Property | Type | Optional | Description | |
| ------------ | -------- | -------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `userIdHash` | string | No | The user ID of the unsubscribed user. | |
| `user` | User | No | The user object of the unsubscribed user. | |
| `type` | 'manual' | 'auto' | No | Manual: if the user used the UI option to unsubscribe; Auto: When the system automatically removes the user from the unsubscribed list. eg: when the comment where user was tagged is deleted. |
## `CustomFilter` Class
| Property | Type | Optional | Description |
| -------- | ------ | -------- | ---------------------------------------- |
| `id` | string | No | Unique identifier for the custom filter. |
| `color` | string | No | Color associated with the custom filter. |
| `name` | string | No | Name or label of the custom filter. |
## `CustomPriority` Class
| Property | Type | Optional | Description |
| ------------ | ------ | -------- | -------------------------------------------- |
| `id` | string | No | Unique identifier for the custom priority. |
| `color` | string | No | Color associated with the custom priority. |
| `name` | string | No | Name or label of the custom priority. |
| `lightColor` | string | Yes | Light color variant for the custom priority. |
## `CustomStatus` Class
| Property | Type | Optional | Description |
| ------------ | ---------- | -------- | --------------------------------------------------------- |
| `id` | string | No | Unique identifier for the custom status. |
| `color` | string | No | Color associated with the custom status. |
| `name` | string | No | Name or label of the custom status. |
| `type` | StatusType | No | Type of the status (`default`, `ongoing`, or `terminal`). |
| `lightColor` | string | Yes | Light color for the custom status. |
| `svg` | string | Yes | Raw SVG string for the custom status. |
| `iconUrl` | string | Yes | URL to an icon image for the custom status. |
## `CustomCategory` Class
| Property | Type | Optional | Description |
| -------- | ------ | -------- | ------------------------------------------ |
| `id` | string | No | Unique identifier for the custom category. |
| `color` | string | No | Color associated with the custom category. |
| `name` | string | No | Name or label of the custom category. |
## CustomAnnotationDropdownData
Represents the configuration for a custom annotation dropdown.
| Property | Type | Description |
| ----------- | ---------------------------------------------------------------- | ------------------------------------------------------------ |
| type | `multi` \| `single` | The type of the custom annotation dropdown. |
| placeholder | string | The placeholder text for the dropdown. Defaults to 'Select'. |
| data | [CustomAnnotationDropdownItem](#customannotationdropdownitem)\[] | An array of dropdown items. |
## CustomAnnotationDropdownItem
Represents an item in a custom annotation dropdown.
| Property | Type | Description |
| -------- | ------ | -------------------------------------------- |
| id | string | The unique identifier for the dropdown item. |
| label | string | The display text for the dropdown item. |
## Default Status Object
The default status object is defined in the `CommentAnnotationStatusMap` constant. It includes three predefined statuses: OPEN, IN\_PROGRESS, and RESOLVED. Each status is an object with the following structure:
```js
{
OPEN: {
id: 'OPEN',
name: 'Open',
color: '#625DF5',
lightColor: '#E7E8FA',
type: 'default',
svg: `
`
},
IN_PROGRESS: {
id: 'IN_PROGRESS',
name: 'In Progress',
color: '#ECB000',
lightColor: '#FEFCEA',
type: 'ongoing',
svg: `
`
},
RESOLVED: {
id: 'RESOLVED',
name: 'Resolved',
color: '#00C48C',
lightColor: '#E7FAF2',
type: 'terminal',
svg: `
`
},
}
```
# CommentSelectionChangeData
## `CommentSelectionChangeData` Class
| Property | Type | Optional | Description |
| ------------ | ----------------- | -------- | -------------------------------------- |
| `selected` | boolean | Yes | Whether a comment is selected |
| `annotation` | CommentAnnotation | Yes | Object data of the selected annotation |
# CommentSidebarConfig
## `FilterTypeConfig` Class
| Property | Type | Optional | Description |
| ---------------- | ------- | -------- | ------------------------------------------------------- |
| `name` | string | Yes | The name of the filter type. |
| `enable` | boolean | Yes | Enables or disables the filter type. |
| `multiSelection` | boolean | Yes | Allows multiple selections if set to true. |
| `enableGrouping` | boolean | Yes | Enables grouping within the filter type if set to true. |
## `CommentSidebarFilterConfig` Class
| Property | Type | Optional | Description |
| ------------- | ---------------- | -------- | ------------------------------------------- |
| `location` | FilterTypeConfig | Yes | Configuration for the location filter type. |
| `people` | FilterTypeConfig | Yes | Configuration for the people filter type. |
| `priority` | FilterTypeConfig | Yes | Configuration for the priority filter type. |
| `category` | FilterTypeConfig | Yes | Configuration for the category filter type. |
| `commentType` | FilterTypeConfig | Yes | Configuration for the comment type filter. |
| `version` | FilterTypeConfig | Yes | Configuration for the version filter type. |
## `CommentSidebarGroupConfig` Class
| Property | Type | Optional | Description |
| -------- | ------- | -------- | ----------------------------- |
| `enable` | boolean | Yes | Enables or disables grouping. |
| `name` | string | Yes | The name of the group. |
## `CommentSidebarFilters` Class
| Property | Type | Optional | Description |
| ---------- | ----------- | -------- | ----------------------------------------------------------------------- |
| `location` | Location\[] | Yes | Array of `Location` objects for the location filters. |
| `people` | Object\[] | Yes | Array of objects with `userId`, `email`, and `name` for people filters. |
| `priority` | string\[] | Yes | Array of priority filters. |
| `status` | string\[] | Yes | Array of status filters. |
| `category` | string\[] | Yes | Array of category filters. |
| `version` | Object\[] | Yes | Array of objects with `id` and optional `name` for version filters. |
# CommentSidebarCustomActionEventData
## `CommentSidebarCustomActionEventData` Class
| Property | Type | Optional | Description |
| -------------------- | ------------------------------------------- | -------- | --------------------------------------------------------------------------- |
| `actions` | CommentSidebarCustomActionsState | No | Map of custom action button IDs and their states |
| `data` | CommentAnnotation\[] | No | List of all unfiltered CommentAnnotations available in the comments sidebar |
| `unreadDataMap` | `{ [commentAnnotationId: string]: number }` | No | Map of comment annotation id and the number of unread comments it has |
| `systemFilteredData` | CommentAnnotation\[] | No | List of filtered CommentAnnotations based on system filters |
## `CommentSidebarCustomActionsState` Class
| Property | Type | Optional | Description |
| --------------- | ------- | -------- | ---------------------------------------------------- |
| `[key: string]` | boolean | No | Custom Action Button ID you defined in the wireframe |
# CommentSidebarData
## `CommentSidebarData` Class
| Property | Type | Optional | Description |
| ------------- | -------------------- | -------- | ----------------------------------------------- |
| `groupId` | string | Yes | ID of the group. Defaults to 'others' |
| `groupName` | string | Yes | Name of the group. Defaults to 'Others' |
| `isExpanded` | boolean | Yes | Whether the group is expanded. Defaults to true |
| `annotations` | CommentAnnotation\[] | No | List of CommentAnnotations in the group |
## `Options` Class
| Property | Type | Optional | Description |
| ---------- | ------- | -------- | ------------------------------------------- |
| `grouping` | boolean | Yes | Whether to group the data. Defaults to true |
# Velt Client Config
## `Config` Class Properties
| Property | Type | Optional | Default | Description |
| ----------------------- | -------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `urlAllowList` | string\[] | Yes | All pages | Restricts Velt features to specific pages by specifying partial URL strings. |
| `featureAllowList` | FeatureType\[] | Yes | All features | Only allows the provided Velt features to run. |
| `userPlanAllowList` | string\[] | Yes | All users | Restricts Velt features to specific user plans. |
| `userIdAllowList` | string\[] | Yes | All users | Restricts Velt features to specific users. |
| `usePrefersColorScheme` | boolean | Yes | - | If set to true, listens to changes on the `prefers-color-scheme` media query to set the global theme of Velt components. |
# ContactListScopeForOrganizationUsers
```jsx
export enum ContactListScopeForOrganizationUsers {
ALL = 'all', // Show all the contacts
ORGANIZATION = 'organization', // Show organization contacts.
ORGANIZATION_USER_GROUP = 'organizationUserGroup', // Show organization user groups.
DOCUMENT = 'document', // Show document contacts.
}
```
# CursorUser
## `CursorUser` Class Properties:
| Property | Type | Optional | Description |
| -------------- | ---------------------- | -------- | --------------------------------------------------------------------------------------- |
| `userId` | string | No | Unique user identifier that you use to identify your user. |
| `name` | string | Yes | Your user's full name. Default: Random avatar name. |
| `email` | string | Yes | Your user's email address. |
| `photoUrl` | string | Yes | Your user's display picture URL. Default: Random avatar image. |
| `comment` | string | Yes | Short comment that user can add to their live cursor. |
| `onlineStatus` | string | No | User's online status (active, inactive, offline). Auto generated. |
| `color` | string | Yes | A random color assigned to the user for the session, used on avatar border/live cursor. |
| `timestamp` | any | No | Server Timestamp. |
| `type` | string | Yes | User type. |
| `locationId` | number \| null | Yes | Unique location id from provided location. |
| `location` | Location \| null | Yes | Location to identify user on sub document. |
| `position` | CursorPosition \| null | Yes | User’s cursor position on their screen. |
| `isReadOnly` | boolean | Yes | Indicates if user is readonly. |
| `isAnonymous` | boolean | Yes | Indicates if user is anonymous and can only view comments. |
# CustomCss
## `CustomCss` Class
| Property | Type | Optional | Description |
| -------- | ------------------ | -------- | --------------------------------------------------------------------- |
| `type` | 'link' \| 'styles' | No | The type of custom CSS, either a link to a CSS file or inline styles. |
| `value` | string | No | The value of the custom CSS, either a URL or CSS styles. |
# CustomFilter
## `CustomFilter` Interface Properties
| Property | Type | Optional | Description |
| -------- | ------ | -------- | ---------------------------------------- |
| `id` | string | No | Unique identifier for the custom filter. |
| `color` | string | No | Color associated with the custom filter. |
| `name` | string | No | Name or label of the custom filter. |
## `CustomPriority` Interface Properties
| Property | Type | Optional | Description |
| ------------ | ------ | -------- | -------------------------------------------- |
| `id` | string | No | Unique identifier for the custom priority. |
| `color` | string | No | Color associated with the custom priority. |
| `name` | string | No | Name or label of the custom priority. |
| `lightColor` | string | Yes | Light color variant for the custom priority. |
## `CustomStatus` Interface Properties
| Property | Type | Optional | Description |
| ------------ | ---------- | -------- | --------------------------------------------------------- |
| `id` | string | No | Unique identifier for the custom status. |
| `color` | string | No | Color associated with the custom status. |
| `name` | string | No | Name or label of the custom status. |
| `type` | StatusType | No | Type of the status (`default`, `ongoing`, or `terminal`). |
| `lightColor` | string | Yes | Light color for the custom status. |
| `svg` | string | Yes | Raw SVG string for the custom status. |
| `iconUrl` | string | Yes | URL to an icon image for the custom status. |
## `CustomCategory` Interface Properties
| Property | Type | Optional | Description |
| -------- | ------ | -------- | ------------------------------------------ |
| `id` | string | No | Unique identifier for the custom category. |
| `color` | string | No | Color associated with the custom category. |
| `name` | string | No | Name or label of the custom category. |
# DocumentMetadata
## `DocumentMetadata` Class Properties
| Property | Type | Description |
| ------------------ | ------------------------------------------ | ------------------------------------------------ |
| `clientDocumentId` | string | The document ID set by you. |
| `documentId` | string | The document ID set by the SDK. |
| `documentName` | string | Name of the document |
| `pageInfo` | [PageInfo](/api-reference/models/PageInfo) | Additional information about the document's page |
# EditorAccessTimer
## `EditorAccessTimer` Class Properties
| Property | Type | Optional | Description |
| -------------- | --------------------------------------- | -------- | ---------------------------------------------------------- |
| `state` | `'idle' \| 'inProgress' \| 'completed'` | No | The state of the Editor Access Request timer |
| `durationLeft` | `number` | Yes | Duration left for the editor access timer to be completed. |
# null
Feature Types Enum:
```jsx
export enum Features {
AREA = 'area',
ARROW = 'arrow',
AUDIO_HUDDLE = 'audioHuddle',
COMMENT = 'comment',
CURSOR = 'cursor',
HUDDLE = 'huddle',
LIVE_STATE_SYNC = 'liveStateSync',
PRESENCE = 'presence',
TAG = 'tag',
RECORDER = 'recorder',
REWRITER = 'rewriter',
LIVE_SELECTION = 'liveSelection',
}
```
# FlockOptions
## `FlockOptions` Class Properties
| Property | Type | Optional | Description |
| -------------------------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `useHistoryAPI` | boolean | No | Indicates whether the application should use the HTML5 History API for navigation. |
| `onNavigate` | (url: PageInfo) => void | Yes | A callback function that is called when navigation occurs. It takes a `PageInfo` object as its argument, which likely contains details about the new page. |
| `disableDefaultNavigation` | boolean | No | If `true`, the application's default navigation handling is disabled, perhaps to be managed manually or by another system. |
| `darkMode` | boolean | No | A flag indicating whether the application should display in dark mode, a display preference that may be more comfortable for users in low-light conditions. |
# LiveStateData
## `LiveStateData` Class
| Property | Type | Optional | Description |
| ----------------- | ----------------------------------- | -------- | -------------------------------------------------------------------------------------------------------- |
| \`id | string | No | A unique identifier likely used for quick reference and indexing. It's an MD5 hash of `liveStateDataId`. |
| `liveStateDataId` | string | No | A unique identifier for the state data being synced. |
| `data` | string \| number \| boolean \| JSON | No | The actual data you want to synchronize across clients. |
| `lastUpdated` | any | No | A timestamp or similar data indicating the last time the state data was updated. |
| `updatedBy` | `User` | No | The user who last updated the state data. |
| `tabId` | string \| null | Yes | An identifier that could be used to associate the state data with a specific tab or instance. |
## `SingleEditorLiveStateData` Class
| Property | Type | Optional | Description |
| --------------------- | -------------- | -------- | ------------------------------------------ |
| `editor` | User \| null | Yes | The user who is currently editing, if any. |
| `requestEditorAccess` | Object \| null | Yes | Details about a request for editor access. |
| `tabId` | string \| null | Yes | The identifier of the tab, if applicable. |
## `requestEditorAccess` Class:
| Property | Type | Optional | Description |
| --------------------- | ---------------------------------------------------- | -------- | -------------------------------------------------------- |
| `user` | User | No | The user requesting editor access. |
| `requestedAt` | any | No | The timestamp when the access was requested. |
| `status` | 'pending' \| 'accepted' \| 'rejected' \| 'cancelled' | No | The status of the access request. |
| `editorAccessTimeout` | number | No | Timeout duration for the editor access. |
| `tabId` | string \| null | Yes | The identifier of the tab related to the access request. |
## `SingleEditorConfig` Class
| Property | Type | Optional | Description |
| ----------------- | ------- | -------- | --------------------------------------------------------------------------------------------- |
| `customMode` | boolean | Yes | Enables/disables custom mode. In custom mode, input elements are not disabled for the viewer. |
| `singleTabEditor` | boolean | No | Enables/disables editor mode on a single tab only. |
## `UserEditorAccess` Class
| Property | Type | Optional | Description |
| ---------------------- | ------- | -------- | ----------------------------------------------------------- |
| `isEditor` | boolean | Yes | Indicates whether the user has editor privileges. |
| `isEditorOnCurrentTab` | boolean | Yes | Indicates whether the user is an editor on the current tab. |
# null
## LiveStateDataMap Properties
| Property | Type | Optional | Description |
| ------------------------------- | ----------------------------------------------- | -------- | --------------------------------------------------------------------------------------- |
| `custom` | `{ [liveStateDataId: string]: LiveStateData; }` | Yes | Map of all unique LiveStateData set by you on the given document. |
| `default` | Object | Yes | Map of all unique LiveStateData set by the default editor on the given document. |
| `default.singleEditor` | `SingleEditorLiveStateData` | | Part of `default`, representing single editor live state data. |
| `default.autoSyncState` | Object | | Part of `default`, representing auto synchronization state. |
| `default.autoSyncState.current` | `LiveStateData` | | Part of `autoSyncState`, current live state data. |
| `default.autoSyncState.history` | `[liveStateDataId: string]: LiveStateData` | | Part of `autoSyncState`, map of historical live state data keyed by live state data ID. |
# null
## `Location` Class Properties
Here's the `Location` class information formatted as a Markdown table, including a column to indicate whether each field is optional and a note on the dynamic properties:
| Property | Type | Optional | Description |
| --------------- | ------- | -------- | ------------------------------------------------- |
| `id` | string | No | Unique identifier for the location. |
| `locationName` | string | Yes | Name of the location. |
| `version` | Version | Yes | Version information provided by the user. |
| `[key: string]` | any | Yes | Additional dynamic properties as key-value pairs. |
## Version
Here's the `Version` class information formatted as a Markdown table, including a column to indicate whether each field is optional:
| Property | Type | Optional | Description |
| -------- | ------ | -------- | ---------------------------------- |
| `id` | string | No | Unique identifier for the version. |
| `name` | string | No | Name of the version. |
# LocationMetadata
## `LocationMetadata` Class Properties
| Property | Type | Optional | Description |
| ------------ | ------------------ | -------- | ------------------------------------------------------------- |
| `locationId` | number | Yes | Unique location id generated from client location information |
| `location` | `Location` \| null | Yes | Location object provided by a client |
# MediaPreviewConfig
## `MediaPreviewConfig` Class Properties
| Property | Type | Optional | Description |
| ---------------- | ----------- | -------- | --------------------------------- |
| `audio` | Object | Yes | Configuration for audio preview |
| `audio.enabled` | boolean | Yes | Whether audio preview is enabled |
| `audio.deviceId` | string | Yes | Device ID for audio input |
| `video` | Object | Yes | Configuration for video preview |
| `video.enabled` | boolean | Yes | Whether video preview is enabled |
| `video.deviceId` | string | Yes | Device ID for video input |
| `screen` | Object | Yes | Configuration for screen preview |
| `screen.enabled` | boolean | Yes | Whether screen preview is enabled |
| `screen.stream` | MediaStream | Yes | MediaStream for screen sharing |
# null
## `Notification` Class Properties
| Property | Type | Optional | Description |
| ------------------------------------ | ----------------------------------- | -------- | ----------------------------------------------------------------------------- |
| `id` | `string` | No | Notification ID |
| `notificationSource` | `string` | No | Notification source. e.g., 'comment', 'custom', etc. |
| `actionType` | `string` | Yes | Action that triggered the notification. e.g., 'added' |
| `isUnread` | `boolean` | Yes | Whether the notification is unread for the user |
| `actionUser` | `User` | Yes | The user who triggered the action |
| `timestamp` | `number` | Yes | Timestamp of the notification |
| `displayHeadlineMessage` | `string` | Yes | The headline message of the notification |
| `displayBodyMessage` | `string` | Yes | The body message of the notification |
| `displayHeadlineMessageTemplate` | `string` | Yes | The template of the headline message |
| `displayHeadlineMessageTemplateData` | `object` | Yes | The data used to fill the headline message template |
| `forYou` | `boolean` | Yes | Whether the notification is for the current logged-in user |
| `targetAnnotationId` | `string` | Yes | ID of the annotation that triggered the notification |
| `notificationSourceData` | `any` | Yes | The data of the notification source. e.g., `CommentAnnotation` |
| `metadata` | `NotificationMetadata` | Yes | Metadata for the current notification. e.g., `documentId` |
| `notifyUsers` | `{ [emailHash: string]: boolean }` | Yes | Map of email hashes to boolean values indicating whether to notify the user |
| `notifyUsersByUserId` | `{ [userIdHash: string]: boolean }` | Yes | Map of user ID hashes to boolean values indicating whether to notify the user |
## `displayHeadlineMessageTemplateData` Object Properties
| Property | Type | Description |
| --------------- | -------- | --------------------------------------- |
| `actionUser` | `User` | The user who performed the action |
| `recipientUser` | `User` | The user receiving the notification |
| `actionMessage` | `string` | The message describing the action |
| `project` | `string` | The project related to the notification |
| `[key: string]` | `any` | Any additional custom properties |
## `NotificationMetadata` Properties
| Property | Type | Optional | Description |
| ---------------------- | ---------- | -------- | -------------------------------------------------- |
| `apiKey` | `string` | Yes | Your API key |
| `clientOrganizationId` | `string` | Yes | The organization ID that you set |
| `organizationId` | `string` | Yes | The organization ID generated by us |
| `clientDocumentId` | `string` | Yes | The document ID that you set |
| `documentId` | `string` | Yes | The document ID generated by us |
| `locationId` | `number` | Yes | The unique location ID |
| `location` | `Location` | Yes | The location object |
| `documentMetadata` | Object | Yes | Contains the complete document metadata object |
| `organizationMetadata` | Object | Yes | Contains the complete organization metadata object |
# PageInfo
## `PageInfo` Class Properties
| Property | Type | Optional | Description |
| ------------- | ------ | -------- | -------------------------------------- |
| `url` | string | Yes | URL of the webpage |
| `path` | string | Yes | Path of the webpage excluding base url |
| `baseUrl` | string | Yes | Base URL (domain) of a webpage |
| `title` | string | Yes | Title of the webpage |
| `commentUrl` | string | Yes | Reference url of a comment annotation |
| `recorderUrl` | string | Yes | Reference url of a recorder annotation |
| `screenWidth` | number | Yes | User's screen width. Auto generated. |
# PresenceUser
## `PresenceUser` Class Properties
| Property | Type | Optional | Description |
| ------------------ | ---------------- | -------- | ---------------------------------------------------------- |
| `userId` | String | No | Unique user identifier |
| `name` | String | Yes | User's full name (Default: Random avatar name) |
| `email` | String | Yes | User's email address |
| `photoUrl` | String | Yes | User's display picture URL (Default: Random avatar image) |
| `onlineStatus` | String | No | Online status (active, inactive, offline) (Auto generated) |
| `color` | String | Yes | Assigned color for the user (Auto generated) |
| `timestamp` | Any | No | Server Timestamp |
| `type` | String | Yes | User type |
| `selections` | Any | Yes | User selections |
| `documentParamsId` | Number, null | Yes | Deprecated unique document params ID |
| `documentParams` | Object, null | Yes | Deprecated document params |
| `locationId` | Number, null | Yes | Unique location ID |
| `location` | `Location`, null | Yes | Location of user on sub document |
| `isReadOnly` | Boolean | Yes | Indicates if user is readonly |
| `isAnonymous` | Boolean | Yes | If user can only view comments (Anonymous) |
| `pageInfo` | `PageInfo` | No | Information about the page |
# null
## `RecorderConfig` Class
| Property | Type | Optional | Description |
| ----------------- | -------------------------------------------------------- | -------- | ------------------------------ |
| `type` | `{ audio?: boolean, video?: boolean, screen?: boolean }` | No | Types of media to be recorded |
| `recorderOptions` | MediaRecorderOptions | Yes | Options for the media recorder |
## `RecordedData` Class
| Property | Type | Optional | Description |
| -------------------------- | -------- | -------- | -------------------------------------------------------------------------------------------------- |
| `id` | string | No | Annotation ID of recorder annotation. |
| `tag` | string | No | Recorder player tag containing recorder annotation id which can be added anywhere on the DOM. |
| `type` | string | No | Type of recorded data. Possible values are 'audio', 'video', and 'screen'. |
| `thumbnailUrl` | string | Yes | URL of the thumbnail image for the recorded data. |
| `thumbnailWithPlayIconUrl` | string | Yes | URL of the thumbnail image with a play icon overlay. |
| `videoUrl` | string | Yes | URL of the recorded video. |
| `audioUrl` | string | Yes | URL of the recorded audio. |
| `videoPlayerUrl` | string | Yes | URL of the hosted website to open video in a new tab. |
| `getThumbnailTag` | function | No | A method that returns an HTML string for displaying the thumbnail with a link to the video player. |
### `getThumbnailTag` Method
The `getThumbnailTag` method takes an optional `url` parameter and returns an HTML string. It creates an anchor tag linking to the `videoPlayerUrl` and embeds an image tag using either the provided `url`, `thumbnailWithPlayIconUrl`, or `thumbnailUrl` (in that order of preference).
# RecorderAnnotation
## `RecorderAnnotation` Class Properties
| Property | Type | Optional | Description | |
| ----------------- | ------------- | -------- | ----------------------------------------------------------------------- | - |
| `annotationId` | String | No | Unique identifier for the recorder annotation, automatically generated. | |
| `from` | `User` | No | The user who created the recorder annotation. | |
| `color` | String | Yes | Color used for the annotation. | |
| `lastUpdated` | Any | Yes | Timestamp of the last update, automatically generated. | |
| `locationId` | Number | Yes | Unique location ID from provided location. | |
| `location` | `Location` | Yes | Location to identify user on sub document. | |
| `type` | String | Yes | Type of annotation. | |
| `recordingType` | String | No | Type of recording for the annotation. | |
| `mode` | String | No | Mode of the recorder annotation, 'floating' or 'thread'. | |
| `approved` | Boolean | Yes | Indicates if the annotation is approved. | |
| `attachment` | Attachment | Yes | Attachment for recorded media. Deprecated. | |
| `attachments` | Attachment\[] | No | List of attachments for the annotation. | |
| `annotationIndex` | Number | Yes | Index of the annotation in a list. | |
| `pageInfo` | PageInfo | Yes | Information about the page where the annotation is made. | |
| `recordedTime` | Object | Yes | Recorded time details. | |
| `transcription` | Transcription | Yes | Transcription of the recorded media. | |
# RecorderData
## `RecorderDataTranscriptSegment` Class Properties
| Property | Type | Optional | Description |
| -------------------- | ------ | -------- | --------------------------------------- |
| `startTime` | String | No | Start time of the transcription segment |
| `endTime` | String | No | End time of the transcription segment |
| `startTimeInSeconds` | Number | No | Start time of the segment in seconds |
| `endTimeInSeconds` | Number | No | End time of the segment in seconds |
| `text` | String | No | Transcribed text content of the segment |
## `RecorderDataTranscription` Class Properties
| Property | Type | Optional | Description |
| -------------------- | --------------------------------- | -------- | ---------------------------------------- |
| `transcriptSegments` | `RecorderDataTranscriptSegment[]` | Yes | Array of transcription segments |
| `vttFileUrl` | String | Yes | URL to the VTT format transcription file |
| `contentSummary` | String | Yes | Summary of the transcribed content |
## `RecorderDataAsset` Class Properties
| Property | Type | Optional | Description |
| ----------------- | ---------------------------------- | -------- | ---------------------------------- |
| `url` | String | No | URL to the recorded media |
| `mimeType` | String | Yes | MIME type of the recorded media |
| `fileName` | String | Yes | Name of the recorded file |
| `fileSizeInBytes` | Number | Yes | Size of the recorded file in bytes |
| `fileFormat` | 'mp3' \| 'mp4' \| 'webm' \| String | Yes | The format/extension of the file |
| `thumbnailUrl` | String | Yes | URL to the thumbnail image |
## `RecorderData` Class Properties
| Property | Type | Optional | Description |
| --------------- | --------------------------- | -------- | ------------------------------------------------- |
| `assets` | `RecorderDataAsset[]` | No | Array of recording assets with metadata |
| `transcription` | `RecorderDataTranscription` | No | Transcription data including segments and summary |
# RewriterAnnotation
## `RewriterAnnotation` Class Properties
| Property | Type | Optional | Description |
| ------------------------ | ----------------------- | -------- | ------------------------------------------------------------------------------------- |
| `annotationId` | String | No | Unique identifier for the rewriter annotation, automatically generated. |
| `from` | `User` | No | The user who created this rewriter annotation. |
| `color` | String | Yes | Color used for the rewriter annotation. |
| `lastUpdated` | Any | Yes | Timestamp when the rewriter annotation was last updated, automatically generated. |
| `documentParamsId` | Number \| null | Yes | Unique document params ID, deprecated, use `locationId` instead. |
| `documentParams` | `Location` \| null | Yes | Document params to identify user on sub document, deprecated, use `location` instead. |
| `locationId` | Number \| null | Yes | Unique location ID generated from provided location. |
| `location` | Location \| null | Yes | Set location to identify user on sub document. |
| `type` | String | Yes | Type of annotation. |
| `rewriterType` | String | No | Type of rewriter for the annotation, either 'generic' or 'copywriter'. |
| `targetTextRange` | TargetTextRange \| null | Yes | Selected text range of rewriter annotation. |
| `annotationIndex` | Number | Yes | Index of the current annotation in the list of annotations. |
| `pageInfo` | `PageInfo` | Yes | Information about the page where the annotation is made. |
| `selectedRewriterOption` | String | Yes | Selected rewriter option used in the annotation. |
# SyncVideoPlayer
## `SyncVideoPlayer` Class Properties
| Property | Type | Optional | Description |
| ------------------ | -------------------- | -------- | ----------------------------------------------------- |
| `playerId` | String | Yes | The identifier for the video player instance. |
| `src` | String | Yes | The source URL of the video. |
| `sources` | String\[] | Yes | An array of source URLs for the video. |
| `lastUpdated` | Number | Yes | The timestamp of when the player was last updated. |
| `lastUpdatedBy` | `User` | Yes | The user who last updated the player. |
| `lastUpdatedEvent` | String | Yes | The name of the event that triggered the last update. |
| `playerState` | SyncVideoPlayerState | No | The state object of the video player. |
## `SyncVideoPlayerState` Class Properties
| Property | Type | Optional | Description |
| ------------- | ------- | -------- | -------------------------------------------- |
| `playing` | Boolean | Yes | Indicates if the video is currently playing. |
| `currentTime` | Number | Yes | The current playback time of the video. |
| `muted` | Boolean | Yes | Indicates if the video is muted. |
| `volume` | Number | Yes | The volume level of the video. |
| `speed` | Number | Yes | The playback rate of the video. |
# null
## `Toast` Class Properties
| Property | Type | Optional | Default Value | Description |
| ---------- | -------------------- | -------- | ------------- | ------------------------------------------------------------------------ |
| `id` | Number | Yes | None | A unique identifier for the toast notification. |
| `message` | String | No | None | The message content displayed in the toast notification. |
| `type` | 'success' \| 'error' | No | None | The type of toast notification, indicating success or error. |
| `duration` | Number | Yes | 3000 | The length of time the toast notification is displayed, in milliseconds. |
# null
## `Transcription` Class Properties
| Property | Type | Optional | Description |
| ------------------ | ------ | -------- | ----------------------------------------------------- |
| `from` | User | No | The user who created the transcription. |
| `lastUpdated` | Number | Yes | Timestamp of when the transcription was last updated. |
| `transcriptedText` | String | Yes | The text that has been transcribed. |
# null
## `User` Class Properties
| Property | Type | Optional | Description |
| ---------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `userId` | string | No | Unique user identifier used to identify your user. |
| `name` | string | Yes | The full name of your user. Defaults to a random avatar name if not provided. |
| `photoUrl` | string | Yes | The display picture URL of your user. Defaults to a random avatar image if not provided. |
| `email` | string | Yes | Required for sending email or Slack notifications to users about comments and mentions. |
| `plan` | string | Yes | The product plan the user is on. |
| `organizationId` | string | Yes | Autogenerated organizationId based on the organizationId you provide. |
| `clientorganizationId` | string | Yes | The original organizationId provided by you. |
| `color` | string | Yes | A color assigned to the user for the current session, used for avatar border, live cursor, selection etc. |
| `textColor` | string | Yes | Used in the text color of the user's intial when photoUrl is not present. |
| `type` | string | Yes | The type of user. |
| `isReadOnly` | boolean | Yes | Indicates if the user has read-only access. |
| `isAnonymous` | boolean | Yes | Indicates if the user is anonymous and can only view comments. |
| `isGuest` | boolean | Yes | Indicates if the user is a guest. |
| `isAdmin` | boolean | Yes | Use this to set the user as an admin. You also need to ensure that the jwt token you generate also has this property set to true. |
| `groupId` | string | Yes | \[DEPRECATED] A domain name or identifier used to cluster a group of users who work together. |
| `clientGroupId` | string | Yes | \[DEPRECATED] The original groupId provided by the user. |
| `initial` | string | Yes | First letter of the user's first name. |
## `UserOptions` Class Properties
| Property | Type | Optional | Description |
| ----------------- | ------- | -------- | ---------------------------------------------------------------------------------------------- |
| `replaceContacts` | boolean | Yes | If set to true, it will replace the user's personal and group contacts with the ones provided. |
| `authToken` | string | Yes | The authentication token of the user. |
# UserContactSelectedPayload
| Property | Type | Optional | Description |
| ----------------------- | ----------- | -------- | ------------------------------------- |
| `contact` | UserContact | No | Selected user contact details. |
| `isOrganizationContact` | boolean | No | Is user part of organization contact. |
| `isDocumentContact` | boolean | No | Is user part of document contact. |
| `documentAccessType` | string | No | Document access type. |
# UserContactUs
## `UserContactUs` Class Properties
| Property | Type | Optional | Description |
| ------------- | ---------------------------- | -------- | ------------------------------------------------ |
| `id` | `string` | Yes | Unique identifier of the feedback. |
| `apiKey` | `string` \| `null` | Yes | API key of the client. |
| `emailId` | `string` | Yes | Email address of the feedback provider. |
| `message` | `string` | Yes | Content of the user's feedback message. |
| `from` | `User` \| `null` | Yes | User who submitted the feedback. |
| `lastUpdated` | `any` | Yes | Timestamp of when the feedback was last updated. |
| `metadata` | `DocumentMetadata` \| `null` | Yes | Metadata associated with the document. |
| `pageInfo` | `PageInfo` | Yes | Information about the user's current page. |
# UserFeedback
## `UserFeedback` Class Properties
| Property | Type | Optional | Description |
| ------------- | ---------------------------- | -------- | ------------------------------------------------ |
| `id` | `string` | Yes | Unique identifier of the feedback. |
| `apiKey` | `string` \| `null` | Yes | API key of the client. |
| `emailId` | `string` | Yes | Email address of the feedback provider. |
| `message` | `string` | Yes | Content of the user's feedback message. |
| `from` | `User` \| `null` | Yes | User who submitted the feedback. |
| `lastUpdated` | `any` | Yes | Timestamp of when the feedback was last updated. |
| `metadata` | `DocumentMetadata` \| `null` | Yes | Metadata associated with the document. |
| `pageInfo` | `PageInfo` | Yes | Information about the user's current page. |
# Add Comment Annotations
POST https://api.velt.dev/v1/commentannotations/add
Use this API to add comment annotations to a document within an organization.
* You can add comments on an elemement, text or page.
* You can provide HTML or text content.
* Additional filters can be applied using location IDs.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Location ID
Location Name
Target Element
Element DOM Id
Target Text. Provide this if you want to add comment annotation on the provided text content.
Occurrence. Provide this if you want to add comment annotation on a text content.
Select All Content. Provide this if you want to select and add comment annotation on the entire text content of the target elementId.
Array of Comment Data
Comment content in plain text string
Comment content in HTML string
User object from whom the comment is added
Status
Type
Default
Ongoing
Terminal
Light Color
Raw SVG of the icon
Icon URL
User object to whom the comment is assigned
Custom key/value metadata object
Priority
Priority ID
Priority Color
Priority Name
Priority Light Color
## **Example Requests**
#### Add comment annotation by organizationId, documentId and location
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"commentAnnotations": [
{
"location": {
"id": "yourLocationId",
"locationName": "yourLocationName"
},
"targetElement": {
"elementId": "yourElementId",
"targetText": "Your Target Text",
"occurrence": 1,
"selectAllContent": false
},
"commentData": [
{
"commentText": "Sample Comment",
"commentHtml": "
Hello
",
"from": {
"userId": "yourUserId",
"name": "yourUserName",
"email": "yourUserEmail",
}
}
]
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Comment Annotation addition successfully.",
"data": {
"-O0mpUziLcBwzREvZKs6": {
"success": true,
"annotationId": "-O0mpUziLcBwzREvZKs6",
"commentIds": [
126535
],
"message": "Added Successfully"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Comment Annotation addition successful.",
"data": {
"annotationId": "yourAnnotationId",
"commentIds": [
333130
]
}
}
}
```
# Delete Comment Annotations
POST https://api.velt.dev/v1/commentannotations/delete
Use this API to delete comment annotations from a document within an organization.
Additional filters can be applied using location IDs, annotation IDs, or user IDs.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Array of Location IDs
Array of Annotation IDs
Array of User IDs
## **Example Requests**
#### 1. Delete annotations by organizationId and documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId"
}
}
```
#### 2. Delete annotations by organizationId, documentId and locationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
]
}
}
```
#### 3. Delete annotations by organizationId, documentId, locationIds and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"userIds": [
"yourUserId"
]
}
}
```
#### 4. Delete annotations by organizationId, documentId and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId"
]
}
}
```
#### 5. Delete annotations by organizationId, documentId and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
]
}
}
```
#### 6. Delete annotations by organizationId, documentId, locationIds and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
]
}
}
```
#### 7. Delete annotations by documentId. This will work if the document was created without an organization.
```JSON
{
"data": {
"documentId": "yourDocumentId"
}
}
```
#### 8. Delete annotations by documentId and locationIds. This will work if the document was created without an organization.
```JSON
{
"data": {
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
]
}
}
```
#### 9. Delete annotations by documentId and userIds. This will work if the document was created without an organization.
```JSON
{
"data": {
"documentId": "yourDocumentId",
"userIds": [
"yourUserId"
]
}
}
```
#### 10. Delete annotations by documentId and annotationIds. This will work if the document was created without an organization.
```JSON
{
"data": {
"documentId": "yourDocumentId",
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
]
}
}
```
#### 11. Delete annotations by documentId, locationIds, and userIds. This will work if the document was created without an organization.
```JSON
{
"data": {
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"userIds": [
"yourUserId"
]
}
}
```
#### 12. Delete annotations by documentId, locationIds, and annotationIds. This will work if the document was created without an organization.
```JSON
{
"data": {
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Annotations deleted successfully.",
"data": {
"yourAnnotationId1": {
"success": true,
"id": "yourAnnotationId",
"message": "Deleted Successfully"
},
"yourAnnotationId2": {
"success": false,
"id": "yourAnnotationId2",
"message": "Annotation not found."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Annotations deleted successfully.",
"data": {
"yourAnnotationId": {
"success": true,
"id": "yourAnnotationId",
"message": "Deleted Successfully"
}
}
}
}
```
# Get Comment Annotations
POST https://api.velt.dev/v1/commentannotations/get
Use this API to retrieve comment annotations from a document within an organization.
Additional filters can be applied using location IDs, annotation IDs, or user IDs.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Array of Location IDs. Limit: Only 30 IDs can be passed at a time.
Array of Annotation IDs. Limit: Only 30 IDs can be passed at a time.
Array of User IDs. Limit: Only 30 IDs can be passed at a time.
## **Example Requests**
#### 1. Get annotations by organizationId and documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
}
}
```
#### 2. Get annotations by organizationId, documentId, and locationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
}
}
```
#### 3. Get annotations by organizationId, documentId, locationIds, and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"userIds": [
"yourUserId"
],
}
}
```
#### 4. Get annotations by organizationId, documentId, and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId"
],
}
}
```
#### 5. Get annotations by organizationId, documentId, and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
],
}
}
```
#### 6. Get annotations by organizationId, documentId, locationIds, and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
],
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Annotations fetched successfully.",
"data": [
{
"annotationId": "yourAnnotationId",
"comments": [
{
"commentId": 123456,
"commentText": "This is a sample comment text.",
"commentHtml": "
",
"from": {
"userId": "user123",
"name": "John Doe",
"email": "john.doe@example.com"
},
"lastUpdated": "2023-06-15T10:30:00Z",
"type": "text",
}
],
"from": {
"userId": "user123",
"name": "John Doe",
"email": "john.doe@example.com"
},
"color": "#00FF00",
"createdAt": "2023-06-15T10:30:00Z",
"lastUpdated": "2023-06-15T10:30:00Z",
"status": {
"id": "OPEN",
"name": "Open",
"color": "#0000FF",
"type": "default"
}
},
null // null is returned only if you provided an annotationId that doesn't exist
]
}
}
```
# Get Comment Annotations v2
POST https://api.velt.dev/v2/commentannotations/get
Use this API to retrieve comment annotations from a document within an organization.
Additional filters can be applied using location IDs, annotation IDs, or user IDs.
# Endpoint
`POST https://api.velt.dev/v2/commentannotations/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Array of Location IDs. Limit: Only 30 IDs can be passed at a time.
Array of Annotation IDs. Limit: Only 30 IDs can be passed at a time.
Array of User IDs. Limit: Only 30 IDs can be passed at a time.
Number of items to be retrieved per page. Default: 1000.
Page token retrieved from previous API call.
Order of results based on `lastUpdated` timestamp. Options: `asc` or `desc`. Default: `desc`.
Status ID of the annotations to filter on.
## **Example Requests**
#### 1. Get annotations by organizationId and documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"pageSize": 10,
"pageToken": "1720441573192",
"statusId": "OPEN"
}
}
```
#### 2. Get annotations by organizationId, documentId, and locationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"pageSize": 10,
"pageToken": "1720441573192",
"statusId": "OPEN"
}
}
```
#### 3. Get annotations by organizationId, documentId, locationIds, and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"userIds": [
"yourUserId"
],
"pageSize": 10,
"pageToken": "1720441573192",
"statusId": "OPEN"
}
}
```
#### 4. Get annotations by organizationId, documentId, and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId"
],
"pageSize": 10,
"pageToken": "1720441573192",
"statusId": "OPEN"
}
}
```
#### 5. Get annotations by organizationId, documentId, and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
],
"pageSize": 10,
"pageToken": "1720441573192",
"statusId": "OPEN"
}
}
```
#### 6. Get annotations by organizationId, documentId, locationIds, and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
],
"pageSize": 10,
"pageToken": "1720441573192",
"statusId": "OPEN"
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Annotations fetched successfully.",
"data": [
{
"annotationId": "yourAnnotationId",
"comments": [
{
"commentId": 123456,
"commentText": "This is a sample comment text.",
"commentHtml": "
",
"from": {
"userId": "user123",
"name": "John Doe",
"email": "john.doe@example.com"
},
"lastUpdated": "2023-06-15T10:30:00Z",
"type": "text",
}
],
"from": {
"userId": "user123",
"name": "John Doe",
"email": "john.doe@example.com"
},
"color": "#00FF00",
"createdAt": "2023-06-15T10:30:00Z",
"lastUpdated": "2023-06-15T10:30:00Z",
"status": {
"id": "OPEN",
"name": "Open",
"color": "#0000FF",
"type": "default"
}
},
null // null is returned only if you provided an annotationId that doesn't exist
],
"nextPageToken": "pageToken"
}
}
```
# Update Comment Annotations
POST https://api.velt.dev/v1/commentannotations/update
Use this API to update comment annotations in a document within an organization.
Additional filters can be applied using location IDs, annotation IDs, or user IDs.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Array of Location IDs
Array of User IDs. These are the users who created the comment annotation.
Array of Annotation IDs
Location ID
Location Name
Target Element
Element DOM Id
Target Text. Provide this if you want to add comment annotation on the provided text content.
Occurrence. Provide this if you want to add comment annotation on a text content.
Select All Content. Provide this if you want to select and add comment annotation on the entire text content of the target elementId.
Array of Comment Data
Comment content in plain text string
Comment content in HTML string
User object from whom the comment is added
User object from whom the Comment Annotation is added
Status
Type
Default
Ongoing
Terminal
Light Color
Raw SVG of the icon
Icon URL
User object to whom the comment is assigned
Custom key/value metadata object
Priority
Priority ID
Priority Color
Priority Name
Priority Light Color
## **Example Requests**
#### 1. Update all comment annotations by organizationId and documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"updatedData" : {
"status": {
"type": "ongoing"
}
}
}
}
```
#### 2. Update comment annotations by organizationId, documentId and locationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"updatedData" : {
"status": {
"type": "ongoing"
}
}
}
}
```
#### 3. Update annotations by organizationId, documentId, locationIds and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"userIds": [
"yourUserId"
],
"updatedData" : {
"status": {
"type": "ongoing"
}
}
}
}
```
#### 4. Update comment annotations by organizationId, documentId and userIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId"
],
"updatedData" : {
"status": {
"type": "ongoing"
}
}
}
}
```
#### 5. Update comment annotations by organizationId, documentId and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
],
"updatedData" : {
"status": {
"type": "ongoing"
}
}
}
}
```
#### 6. Update comment annotations by organizationId, documentId, locationIds and annotationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationIds": [
"locationx"
],
"annotationIds": [
"yourAnnotationId1",
"yourAnnotationId2"
],
"updatedData" : {
"status": {
"type": "ongoing"
}
}
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Annotations updated successfully.",
"data": [
"yourAnnotationId1",
"yourAnnotationId2",
"yourAnnotationId3",
"yourAnnotationId4",
"yourAnnotationId5",
"yourAnnotationId6",
"yourAnnotationId7"
]
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Annotations updated successfully.",
"data": [
"yourAnnotationId1",
"yourAnnotationId2",
"yourAnnotationId3",
"yourAnnotationId4",
"yourAnnotationId5",
"yourAnnotationId6",
"yourAnnotationId7"
]
}
}
```
# Add Comments
POST https://api.velt.dev/v1/commentannotations/comments/add
Use this API to add comments within a specific CommentAnnotation.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/comments/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Comment Annotation ID
Array of Comment Data
Comment content in plain text string
Comment content in HTML string
User object from whom the comment is added
## **Example Requests**
#### 1. Add comment in a CommentAnnotation by organizationId, documentId, and annotationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationId": "yourAnnotationId",
"commentData": [
{
"commentText": "Sample Comment",
"commentHtml": "
Hello
",
"from": {
"userId": "yourUserId"
}
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Comment(s) added successfully.",
"data": [
778115
]
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Comment(s) added successfully.",
"data": [
778115
]
}
}
```
# Delete Comments
POST https://api.velt.dev/v1/commentannotations/comments/delete
Use this API to delete comments within a specific CommentAnnotation.
Additional filters can be applied using comment IDs.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/comments/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Comment Annotation ID
Array of Comment IDs
## **Example Requests**
#### 1. Delete all comments of a CommentAnnotation by organizationId, documentId, and annotationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationId": "yourAnnotationId"
}
}
```
#### 2. Delete specific comments of a CommentAnnotation by organizationId, documentId, annotationId and commentIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationId": "yourAnnotationId",
"commentIds": [
153783,
607395
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Comments(s) deleted successfully.",
"data": {
"153783": {
"success": true,
"id": 153783,
"message": "Deleted successfully"
},
"607395": {
"success": false,
"id": 607395,
"message": "Not found"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Comments(s) deleted successfully.",
"data": {
"153783": {
"success": true,
"id": 153783,
"message": "Deleted successfully"
}
}
}
}
```
# Get Comments
POST https://api.velt.dev/v1/commentannotations/comments/get
Use this API to retrieve comments in a specific CommentAnnotation.
Additional filters can be applied using comment IDs.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/comments/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Comment Annotation ID
User IDs
Array of Comment IDs
## **Example Requests**
#### 1. Get all comments with a CommentAnnotation by organizationId, documentId, and annotationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationId": "yourAnnotationId"
}
}
```
#### 2. Get specific comments of a CommentAnnotation by organizationId, documentId, annotationId and commentIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationId": "yourAnnotationId",
"commentIds": [
153783,
607395
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Comments(s) retrieved successfully.",
"data": [
{
"commentHtml": "
",
"commentId": 153783,
"commentText": "Sample Comment Text",
"from": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"lastUpdated": "2024-06-20T09:53:42.258Z",
"status": "added",
"type": "text"
}
]
}
}
```
# Update Comments
POST https://api.velt.dev/v1/commentannotations/comments/update
Use this API to update comments within a specific CommentAnnotation.
# Endpoint
`POST https://api.velt.dev/v1/commentannotations/comments/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Comment Annotation ID
Comment IDs
Comment data
Comment content in plain text string
Comment content in HTML string
User object from whom the comment is added
## **Example Requests**
#### Update comment in a CommentAnnotation by organizationId, documentId, annotationId and commentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"annotationId": "yourAnnotationId",
"commentIds": [
153783,
607395
],
"updatedData": {
"commentText": "Sample Updated Comment",
"commentHtml": "
Hello Updated
"
}
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Comment updated successfully.",
"data": {
"607395": {
"success": true,
"id": 607395,
"message": "Updated successfully"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Comment updated successfully.",
"data": {
"607395": {
"success": true,
"id": 607395,
"message": "Updated successfully"
}
}
}
}
```
# Add Documents
POST https://api.velt.dev/v1/organizations/documents/add
Use this API to add documents with metadata to an organization.
# Endpoint
`POST https://api.velt.dev/v1/organizations/documents/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Document objects
## **Example Requests**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documents": [
{
"documentId": "yourDocumentId",
"documentName": "Your Document Name"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Document(s) added successfully.",
"data": {
"yourDocumentId": {
"success": true,
"id": "8121657101517513",
"message": "Added Successfully"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Document(s) added successfully.",
"data": {
"yourDocumentId": {
"success": true,
"id": "8121657101517513",
"message": "Added Successfully"
}
}
}
}
```
# Delete Documents
POST https://api.velt.dev/v1/organizations/documents/delete
Use this API to delete specific documents from an organization.
# Endpoint
`POST https://api.velt.dev/v1/organizations/documents/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Document IDs
## **Example Requests**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentIds": ["yourDocumentId1", "yourDocumentId2"]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Document(s) deleted successfully.",
"data": {
"yourDocumentId1": {
"success": true,
"id": "6737987049068973",
"message": "Deleted Successfully"
},
"yourDocumentId2": {
"success": true,
"id": "2131443384150904",
"message": "Document does not exist"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Document(s) deleted successfully.",
"data": {
"yourDocumentId1": {
"success": true,
"id": "6737987049068973",
"message": "Deleted Successfully"
}
}
}
}
```
# Get Documents
POST https://api.velt.dev/v1/organizations/documents/get
Use this API to retrieve specific documents or all documents from an organization.
# Endpoint
`POST https://api.velt.dev/v1/organizations/documents/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Document IDs. Limit: Only 30 IDs can be passed at a time.
## **Example Requests**
#### 1. Get all documents from organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
}
}
```
#### 2. Get specific documents from organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentIds": ["yourDocumentId1", "yourDocumentId2"],
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Document(s) retrieved successfully.",
"data": [
{
"documentName": "yourDocumentName",
"disabled": false,
"accessType": "public",
"id": "yourDocumentId",
}
]
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Document(s) retrieved successfully.",
"data": [
{
"documentName": "yourDocumentName",
"disabled": false,
"accessType": "public",
"id": "yourDocumentId",
}
]
}
}
```
# Get Documents v2
POST https://api.velt.dev/v2/organizations/documents/get
Use this API to retrieve specific documents or all documents from an organization.
# Endpoint
`POST https://api.velt.dev/v2/organizations/documents/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Document IDs. Limit: Only 30 IDs can be passed at a time.
Number of items to be retrieved per page. Default: 1000.
Page token retrieved from previous API call.
## **Example Requests**
#### 1. Get all documents from organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
#### 2. Get specific documents from organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentIds": ["yourDocumentId1", "yourDocumentId2"],
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Document(s) retrieved successfully.",
"data": [
{
"documentName": "yourDocumentName",
"disabled": false,
"accessType": "public",
"id": "yourDocumentId",
}
],
"pageToken": "nextPageToken"
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Document(s) retrieved successfully.",
"data": [
{
"documentName": "yourDocumentName",
"disabled": false,
"accessType": "public",
"id": "yourDocumentId",
}
],
"pageToken": "nextPageToken"
}
}
```
# Update Access for Documents
POST https://api.velt.dev/v1/organizations/documents/access/update
Use this API to update the access type for a single or multiple documents at once.
You can update the default access type for all the documents associated with your API Key in [console](https://console.velt.dev/dashboard/config/appconfig).
# Endpoint
`https://api.velt.dev/v1/organizations/documents/access/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Document IDs
Access type for the documents. Allowed values: `organizationPrivate`, `restricted`, `public`.
[Learn more](/key-concepts/access-control/overview).
## **Example Requests**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentIds": ["yourDocumentId1, yourDocumentId2"],
"accessType": "organizationPrivate"
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Updated access for documents successfully.",
"data": {
"yourDocumentId": {
"success": true,
"accessType": "organizationPrivate",
"message": "Document access type updated."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Updated access for documents successfully.",
"data": {
"yourDocumentId": {
"success": true,
"accessType": "organizationPrivate",
"message": "Document access type updated."
}
}
}
}
```
# Update Disabled State for Documents
POST https://api.velt.dev/v1/organizations/documents/access/disablestate/update
Use this API to enable or disable both read and write access for all users.
Let's say your customer's trial or subscription has ended and you want to disable their access to the Velt data, you could use this to diable access to specific documents.
# Endpoint
`https://api.velt.dev/v1/organizations/documents/access/disablestate/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body
#### Params
Organization ID
Array of Document IDs
Whether to disable read and write access to the specified documents. Allowed values: `true`, `false`
## **Example Requests**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentIds": ["yourDocumentId"],
"disabled": true
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Updated disable state for documents successfully.",
"data": {
"yourDocumentId": {
"success": true,
"disabled": true,
"message": "Document disabled state updated."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Updated disable state for documents successfully.",
"data": {
"yourDocumentId": {
"success": true,
"disabled": true,
"message": "Document disabled state updated."
}
}
}
}
```
# Update Documents
POST https://api.velt.dev/v1/organizations/documents/update
Use this API to update metadata of documents within an organization.
# Endpoint
`POST https://api.velt.dev/v1/organizations/documents/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Document objects
## **Example Requests**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documents": [
{
"documentId": "yourDocumentId",
"documentName": "Your Document Name"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Document(s) updated successfully.",
"data": {
"yourDocumentId": {
"success": true,
"id": "8121657101517513",
"message": "Updated Successfully"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Document(s) updated successfully.",
"data": {
"yourDocumentId": {
"success": true,
"id": "8121657101517513",
"message": "Updated Successfully"
}
}
}
}
```
# Delete Data by Location
POST https://api.velt.dev/v1/organizations/documents/locations/data/delete
Delete all data associated with a specific location
Use this API to delete all data (comments, recordings, or any other feature data) associated with a specific location.
# Endpoint
`POST https://api.velt.dev/v1/organizations/documents/locations/data/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Your API key
Your Auth Token
Document ID
The location object containing the data to be deleted
## **Example Request**
```JSON
{
"data": {
"apiKey": "YOUR_API_KEY",
"authToken": "YOUR_AUTH_TOKEN",
"documentId": "YOUR_DOCUMENT_ID",
"location": {
"id": "scene_1",
"locationName": "White scene"
}
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Data deleted successfully.",
"data": null
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Data deleted successfully.",
"data": null
}
}
```
# Update Location
POST https://api.velt.dev/v1/organizations/documents/locations/update
Update a Location's metadata
Use this API to update a Location's metadata.
# Endpoint
`POST https://api.velt.dev/v1/organizations/documents/locations/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Your API key
Your Auth Token
Document ID
The current location object
The updated location object
When true, merges new location fields with existing ones. When false, completely replaces the old location object.
Default: false
## **Example Request**
```JSON
{
"data": {
"apiKey": "YOUR_API_KEY",
"authToken": "YOUR_AUTH_TOKEN",
"documentId": "YOUR_DOCUMENT_ID",
"migrate": {
"oldLocation": {
"id": "scene_1",
"locationName": "Untitled scene"
},
"newLocation": {
"id": "scene_1",
"locationName": "White scene"
}
},
"merge": true
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Location updated.",
"data": null
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Location updated.",
"data": null
}
}
```
# Add Notifications
POST https://api.velt.dev/v1/notifications/add
Use this API to add notifications.
# Endpoint
`POST https://api.velt.dev/v1/notifications/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
User who took the action
Display Headline Message Template
Display Headline Message Template Data (Optional)
User who took the action
User who was directly affected by the action
Any custom field with string value
Display Body Message
Array of Notify Users
Any custom object to be stored with the notification.
When the user clicks on the notification, this data will be sent in the callback.
## **Example Request**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"actionUser": {
"userId": "yourUserId",
"name": "User Name",
"email": "user@example.com"
},
"displayHeadlineMessageTemplate": "This is main template, you can pass variables using curly brackets like this: {actionUser}, {recipientUser}, {yourCustomVariableWithStringValue}",
"displayHeadlineMessageTemplateData": {
"actionUser": {
"userId": "yourUserId",
"name": "User Name",
"email": "user@example.com"
},
"recipientUser": {
"userId": "recipientUserId",
"name": "Recipient Name",
"email": "recipient@example.com"
},
"yourCustomField": "Variable will be replaced with this text"
},
"displayBodyMessage": "This is body message (Secondary message)",
"notifyUsers": [
{
"email": "test@example.com",
"userId": "testingUserId"
},
{
"userId": "yourUserId",
"name": "User Name",
"email": "user@example.com"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Notification added successfully.",
"data": {
"success": true,
"message": "Notification added successfully."
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Notification added successfully.",
"data": {
"success": true,
"message": "Notification added successfully."
}
}
}
```
# Delete Notifications
POST https://api.velt.dev/v1/notifications/delete
Use this API to delete notifications.
# Endpoint
`POST https://api.velt.dev/v1/notifications/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID (Optional)
Location ID (Optional)
User ID (Optional)
Array of Notification IDs (Optional)
## **Example Requests**
#### 1. Delete by organizationId and documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId"
}
}
```
#### 2. Delete by organizationId, documentId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 3. Delete by organizationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId"
}
}
```
#### 4. Delete by organizationId, userId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 5. Delete by organizationId, documentId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId"
}
}
```
#### 6. Delete by organizationId, documentId, userId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 7.Delete by organizationId, documentId and locationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId"
}
}
```
#### 8. Delete by organizationId, documentId, locationId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 9. Delete by organizationId, locationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"locationId": "yourLocationId",
"userId": "yourUserId",
}
}
```
#### 10. Delete by organizationId, locationId, userId, and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"locationId": "yourLocationId",
"userId": "yourUserId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 11. Delete by organizationId, documentId, locationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId",
"userId": "yourUserId",
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Notification(s) deleted successfully.",
"data": {
"8955243231506071": {
"success": true,
"message": "Notification deleted."
}
}
}
}
```
#### When some notifications are not found
```JSON
{
"result": {
"status": "success",
"message": "Notification(s) deleted successfully.",
"data": {
"89552432315060712": {
"success": false,
"message": "Failed to delete notification."
},
"8955243231506071": {
"success": true,
"message": "Notification deleted."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Notification(s) deleted successfully.",
"data": {
"8955243231506071": {
"success": true,
"message": "Notification deleted."
}
}
}
}
```
# Get Notifications
POST https://api.velt.dev/v1/notifications/get
Use this API to retrieve notifications.
# Endpoint
`POST https://api.velt.dev/v1/notifications/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID
Location ID
User ID
Array of Notification IDs. Limit: Only 30 items can be passed at a time.
## **Example Requests**
#### 1. Get by organizationId, documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId"
}
}
```
#### 2. Get by organizationId, documentId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 3. Get by organizationId, documentId and locationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId"
}
}
```
#### 4. Get by organizationId, documentId, locationId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 5. Get by organizationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId"
}
}
```
#### 6. Get by organizationId, userId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 7. Get by organizationId, documentId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId"
}
}
```
#### 8. Get by organizationId, locationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"locationId": "yourLocationId"
}
}
```
#### 9. Get by organizationId, documentId, locationId, and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId",
"locationId": "yourLocationId"
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Notification(s) retrieved successfully.",
"data": [
{
"id": "notificationId",
"notificationSource": "custom",
"notificationSourceData": {}, //The data of the notification source. e.g., CommentAnnotation
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"displayBodyMessage": "This is body message (Secondary message)",
"displayHeadlineMessageTemplate": "This is main template, you can pass variables using curly brackets like this: {actionUser}, {recipientUser}, {yourCustomVariableWithStringValue}",
"displayHeadlineMessageTemplateData": {
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"recipientUser": {
"email": "recipient@example.com",
"name": "Recipient Name",
"userId": "recipientUserId"
},
"yourCustomVariableWithStringValue": "Variable will be replaced with this text"
},
"metadata": {
"apiKey": "yourApiKey",
"documentId": "yourDocumentId",
"organizationId": "yourOrganizationId"
},
"notifyUsers": {
"yourNotifyUserId": true
},
"notifyUsersByUserId": {
"yourNotifyUserById": true
},
"timestamp": 1722409519944
}
]
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Notification(s) retrieved successfully.",
"data": [
{
"id": "notificationId",
"notificationSource": "custom",
"notificationSourceData": {}, //The data of the notification source. e.g., CommentAnnotation
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"displayBodyMessage": "This is body message (Secondary message)",
"displayHeadlineMessageTemplate": "This is main template, you can pass variables using curly brackets like this: {actionUser}, {recipientUser}, {yourCustomVariableWithStringValue}",
"displayHeadlineMessageTemplateData": {
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"recipientUser": {
"email": "recipient@example.com",
"name": "Recipient Name",
"userId": "recipientUserId"
},
"yourCustomVariableWithStringValue": "Variable will be replaced with this text"
},
"metadata": {
"apiKey": "yourApiKey",
"documentId": "yourDocumentId",
"organizationId": "yourOrganizationId"
},
"notifyUsers": {
"yourNotifyUserId": true
},
"notifyUsersByUserId": {
"yourNotifyUserById": true
},
"timestamp": 1722409519944
}
]
}
}
```
# Get Notifications v2
POST https://api.velt.dev/v2/notifications/get
Use this API to retrieve notifications.
# Endpoint
`POST https://api.velt.dev/v2/notifications/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID. Either pass this or userId.
Location ID
User ID. Either pass this or documentId.
Array of Notification IDs. Limit: Only 30 items can be passed at a time.
Number of items to be retrieved per page. Default: 1000.
Page token retrieved from previous API call.
Order of the notifications based on timestamp. Default: desc.
## **Example Requests**
#### 1. Get by organizationId, documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
#### 2. Get by organizationId, documentId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 3. Get by organizationId, documentId and locationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
#### 4. Get by organizationId, documentId, locationId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 5. Get by organizationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
#### 6. Get by organizationId, userId and notificationIds
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"notificationIds": [
"yourNotificationId"
]
}
}
```
#### 7. Get by organizationId, documentId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
#### 8. Get by organizationId, locationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"locationId": "yourLocationId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
#### 9. Get by organizationId, documentId, locationId, and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId",
"locationId": "yourLocationId",
"pageSize": 20,
"pageToken": "8740648311207869"
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Notification(s) retrieved successfully.",
"data": [
{
"id": "notificationId",
"notificationSource": "custom",
"notificationSourceData": {}, //The data of the notification source. e.g., CommentAnnotation
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"displayBodyMessage": "This is body message (Secondary message)",
"displayHeadlineMessageTemplate": "This is main template, you can pass variables using curly brackets like this: {actionUser}, {recipientUser}, {yourCustomVariableWithStringValue}",
"displayHeadlineMessageTemplateData": {
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"recipientUser": {
"email": "recipient@example.com",
"name": "Recipient Name",
"userId": "recipientUserId"
},
"yourCustomVariableWithStringValue": "Variable will be replaced with this text"
},
"metadata": {
"apiKey": "yourApiKey",
"documentId": "yourDocumentId",
"organizationId": "yourOrganizationId"
},
"notifyUsers": {
"yourNotifyUserId": true
},
"notifyUsersByUserId": {
"yourNotifyUserById": true
},
"timestamp": 1722409519944
}
],
"pageToken": "nextPageToken"
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Notification(s) retrieved successfully.",
"data": [
{
"id": "notificationId",
"notificationSource": "custom",
"notificationSourceData": {}, //The data of the notification source. e.g., CommentAnnotation
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"displayBodyMessage": "This is body message (Secondary message)",
"displayHeadlineMessageTemplate": "This is main template, you can pass variables using curly brackets like this: {actionUser}, {recipientUser}, {yourCustomVariableWithStringValue}",
"displayHeadlineMessageTemplateData": {
"actionUser": {
"email": "user@example.com",
"name": "User Name",
"userId": "yourUserId"
},
"recipientUser": {
"email": "recipient@example.com",
"name": "Recipient Name",
"userId": "recipientUserId"
},
"yourCustomVariableWithStringValue": "Variable will be replaced with this text"
},
"metadata": {
"apiKey": "yourApiKey",
"documentId": "yourDocumentId",
"organizationId": "yourOrganizationId"
},
"notifyUsers": {
"yourNotifyUserId": true
},
"notifyUsersByUserId": {
"yourNotifyUserById": true
},
"timestamp": 1722409519944
}
],
"pageToken": "nextPageToken"
}
}
```
# Update Notifications
POST https://api.velt.dev/v1/notifications/update
Use this API to update notifications.
# Endpoint
`POST https://api.velt.dev/v1/notifications/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document ID (Optional)
Location ID
User ID (Optional)
Notifications object
Notification ID
User who took the action
Display Headline Message Template
Display Headline Message Template Data
User who took the action
User who was directly affected by the action
Any custom field with string value
Display Body Message
Any custom object to be stored with the notification.
When the user clicks on the notification, this data will be sent to in the callback.
## **Example Requests**
#### 1. Update by organizationId and documentId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"notifications": [
{
"id": "yourNotificationId",
"displayBodyMessage": "This is body message (Secondary message)",
}
]
}
}
```
#### 2. Update by organizationId, documentId and locationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"locationId": "yourLocationId",
"notifications": [
{
"id": "yourNotificationId",
"displayBodyMessage": "This is body message (Secondary message)",
}
]
}
}
```
#### 3. Update by organizationId, documentId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId",
"notifications": [
{
"id": "yourNotificationId",
"displayBodyMessage": "This is body message (Secondary message)",
}
]
}
}
```
#### 4. Update by organizationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userId": "yourUserId",
"notifications": [
{
"id": "yourNotificationId",
"displayBodyMessage": "This is body message (Secondary message)",
}
]
}
}
```
#### 5. Update by organizationId, documentId, locationId and userId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userId": "yourUserId",
"locationId": "yourLocationId",
"notifications": [
{
"id": "yourNotificationId",
"displayBodyMessage": "This is body message (Secondary message)",
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Notification(s) updated successfully.",
"data": {
"5471488637912692": {
"success": true,
"message": "Notification updated."
}
}
}
}
```
#### When some notifications are not found
```JSON
{
"result": {
"status": "success",
"message": "Notification(s) updated successfully.",
"data": {
"5471488637912692": {
"success": false,
"message": "Failed to update notification."
},
"5471488637912693": {
"success": true,
"message": "Notification updated."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Notification(s) updated successfully.",
"data": {
"5471488637912692": {
"success": true,
"message": "Notification updated."
}
}
}
}
```
# Add User Groups
POST https://api.velt.dev/v1/organizations/usergroups/add
Use this API to add organization user groups to a specific organization.
# Endpoint
`POST https://api.velt.dev/v1/organizations/usergroups/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Array of Organization User Group objects
## **Example Request**
#### Add organization user group in a specific organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"organizationUserGroups": [
{
"groupId": "engineering",
"groupName": "Engineering"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Organization User Groups added successfully.",
"data": {
"yourGroupId": {
"success": true,
"id": "77ab6767b022ad0323ba39c24f12cc95",
"message": "Organization user group added."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Organization User Groups added successfully.",
"data": {
"yourGroupId": {
"success": true,
"id": "77ab6767b022ad0323ba39c24f12cc95",
"message": "Organization user group added."
}
}
}
}
```
# Add Users to Groups
POST https://api.velt.dev/v1/organizations/usergroups/users/add
Use this API to add users to a specific organization user group.
# Endpoint
`POST https://api.velt.dev/v1/organizations/usergroups/users/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Organization User Group ID
Array of User IDs
## **Example Requests**
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"organizationUserGroupId": "yourGroupId",
"userIds": ["yourUserId1"]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Added organization users to group successfully.",
"data": {
"yourUserId1": {
"success": true,
"organizationUserGroupId": "yourGroupId",
"message": "User added to organization user group."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Added organization users to group successfully.",
"data": {
"yourUserId1": {
"success": true,
"organizationUserGroupId": "yourGroupId",
"message": "User added to organization user group."
}
}
}
}
```
# Delete Users from Groups
POST https://api.velt.dev/v1/organizations/usergroups/users/delete
Use this API to delete users from a specific organization user group.
# Endpoint
`POST https://api.velt.dev/v1/organizations/usergroups/users/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Organization User Group ID
Array of User IDs
## **Example Requests**
#### Delete specific users from group
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"organizationUserGroupId": "yourGroupId",
"userIds": ["yourUserId1"]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Deleted users from group successfully.",
"data": {
"yourUserId1": {
"success": true,
"organizationUserGroupId": "yourGroupId",
"message": "User deleted from organization user group."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Deleted users from group successfully.",
"data": {
"yourUserId1": {
"success": true,
"organizationUserGroupId": "yourGroupId",
"message": "User deleted from organization user group."
}
}
}
}
```
# Add Organizations
POST https://api.velt.dev/v1/organizations/add
Use this API to add new organizations and its metadata.
# Endpoint
`POST https://api.velt.dev/v1/organizations/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of Organization objects
## **Example Requests**
#### Add organization
```JSON
{
"data": {
"organizations": [
{
"organizationId": "yourOrganizationId",
"organizationName": "Your Organization Name"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Organization(s) added successfully.",
"data": {
"yourOrganizationId": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Added Successfully"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Organization(s) added successfully.",
"data": {
"yourOrganizationId": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Added Successfully"
}
}
}
}
```
# Delete Organizations
POST https://api.velt.dev/v1/organizations/delete
Use this API to delete specific organization(s) data by their IDs.
# Endpoint
`POST https://api.velt.dev/v1/organizations/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of Organization IDs
## **Example Requests**
#### Delete specific organization
```JSON
{
"data": {
"organizationIds": [
"yourOrganizationId1",
"yourOrganizationId2"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Organization(s) deleted successfully.",
"data": {
"yourOrganizationId1": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Deleted Successfully"
},
{
"yourOrganizationId2": {
"success": false,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Organization does not exist"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Organization(s) deleted successfully.",
"data": {
"yourOrganizationId": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Deleted Successfully"
}
}
}
}
```
# Get Organizations
POST https://api.velt.dev/v1/organizations/get
Use this API to retrieve specific organizations by organization IDs.
# Endpoint
`POST https://api.velt.dev/v1/organizations/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of Organization IDs (Optional).
Limit: Only 30 IDs can be passed at a time.
If this is not provided, all organizations will be returned.
## **Example Requests**
```JSON
{
"data": {
"organizationIds": [
"yourOrganizationId"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Organization(s) retrieved successfully.",
"data": [
{
"id": "yourOrganizationId",
"organizationName": "Your Organization Name",
"disabled": false,
// other metadata fields may be included here
}
// ... more organizations if multiple were retrieved
]
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Organization(s) retrieved successfully.",
"data": [
{
"id": "yourOrganizationId",
"organizationName": "Your Organization Name",
"disabled": false,
// other metadata fields may be included here
}
// ... more organizations if multiple were retrieved
]
}
}
```
# Get Organizations v2
POST https://api.velt.dev/v2/organizations/get
Use this API to retrieve specific organizations by organization IDs.
# Endpoint
`POST https://api.velt.dev/v2/organizations/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of Organization IDs (Optional).
Limit: Only 30 IDs can be passed at a time.
If this is not provided, all organizations will be returned.
Number of items to be retrieved per page (Optional). Default: 1000.
Page token retrieved from previous API call. (Optional)
## **Example Requests**
```JSON
{
"data": {
"organizationIds": [
"yourOrganizationId"
],
"pageSize": 1000,
"pageToken": "pageToken"
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Organization(s) retrieved successfully.",
"data": [
{
"id": "yourOrganizationId",
"organizationName": "Your Organization Name",
"disabled": false,
// other metadata fields may be included here
}
// ... more organizations if multiple were retrieved
],
"nextPageToken": "pageToken"
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Organization(s) retrieved successfully.",
"data": [
{
"id": "yourOrganizationId",
"organizationName": "Your Organization Name",
"disabled": false,
// other metadata fields may be included here
}
// ... more organizations if multiple were retrieved
],
"nextPageToken": "pageToken"
}
}
```
# Update Disabled State for Organizations
POST https://api.velt.dev/v1/organizations/access/disablestate/update
Use this API to enable or disable both read and write access for all documents for all users.
Let's say your customer's trial or subscription has ended and you want to disable their access to the Velt data, you could use this to disable access to the entire organization data.If organization does not exist, it will be created.
# Endpoint
`POST https://api.velt.dev/v1/organizations/access/disablestate/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of Organization IDs
Whether to disable read and write access to the specified organizations. Allowed values: `true`, `false`.
## **Example Requests**
```JSON
{
"data": {
"organizationIds": ["yourOrganizationId1","yourOrganizationId2"],
"disabled": true
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Updated disable state for Organization(s) successfully.",
"data": {
"yourOrganizationId1": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Updated disable state for organization Successfully"
},
"yourOrganizationId2": {
"success": false,
"id": "44e0132f4c6b0d453f18df42d2263b4e",
"message": "Organization does not exist"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Updated disable state for Organization(s) successfully.",
"data": {
"yourOrganizationId": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Updated disable state for organization Successfully"
}
}
}
}
```
# Update Organizations
POST https://api.velt.dev/v1/organizations/update
Use this API to update existing organization(s) metadata.
# Endpoint
`POST https://api.velt.dev/v1/organizations/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of Organization objects
## **Example Requests**
```JSON
{
"data": {
"organizations": [
{
"organizationId": "yourOrganizationId",
"organizationName": "Your Updated Organization Name"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "Organization(s) updated successfully.",
"data": {
"yourOrganizationId": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Updated Successfully"
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Organization(s) updated successfully.",
"data": {
"yourOrganizationId": {
"success": true,
"id": "02cf91e5e7a5f4c0b600c84cf248384b",
"message": "Updated Successfully"
}
}
}
}
```
# Add Users
POST https://api.velt.dev/v1/users/add
Use this API to add Users to:
1. **Organization:** This will provide them access to all the documents in the organization. It will also show users in the contact list of the organization.
2. **Document:** This will provide them access to the specified document. It will also show users in the contact list of the document. If you pass the `documentId`, then the users will be added to the document.
If organization does not exist, it will be created. If User's `initial` is not provided in the User object, then it will be automatically created using the name field.
# Endpoint
`POST https://api.velt.dev/v1/users/add`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document IDs. Provide this if you want to add users to a specific document.
Array of [User](/api-reference/models/User) objects.
## **Example Requests**
#### 1. Add users to a specific organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"users": [
{
"userId": "yourUserId1",
"name": "User Name",
"email": "user@email.com"
}
]
}
}
```
#### 2. Add users to a specific document within an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"users": [
{
"userId": "yourUserId1",
"name": "User Name",
"email": "user@email.com"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "User(s) processed successfully.",
"data": {
"yourUserId1": {
"success": true,
"id": "4c250058149d6c9fb8c894c9ef29c790",
"message": "User added."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "User(s) processed successfully.",
"data": {
"yourUserId1": {
"success": true,
"id": "4c250058149d6c9fb8c894c9ef29c790",
"message": "User added."
}
}
}
}
```
# Delete All User Data (GDPR)
POST https://api.velt.dev/v1/users/data/delete
Remove All User data from Velt.
Use this API to remove all user data from Velt. This will:
* remove their access from all the documents and data in the organization.
* remove them from @mention contact dropdown list.
* remove them from @mentions where they were tagged.
* remove all feature data created by the user. eg: comments, reactions, notifications etc.
# Endpoint
`POST https://api.velt.dev/v1/users/data/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Array of user Ids.
## **Example Request**
```JSON
{
"data": {
"userIds": [
"yourUserId1",
"yourUserId2"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "User(s) deleted successfully.",
"data": {
"yourUserId1": {
"success": true,
"message": "User removed."
}
}
}
}
```
#### User(s) Not Found
```JSON
{
"result": {
"status": "success",
"message": "Deleted user data successfully.",
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "Deleted user data successfully.",
}
}
```
# Delete Users
POST https://api.velt.dev/v1/users/delete
Remove Users from an Organization or a Document.
Use this API to remove Users from:
1. **Organization:** This will remove their access from all the documents and data in the organization. It will also remove these users from the contact list of the organization.
2. **Document:** This will remove their access from the specified document. It will also remove these users from the contact list of the document. If you pass the `documentId`, then the users will be removed from the document.
# Endpoint
`POST https://api.velt.dev/v1/users/delete`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### Params
Organization ID
Document IDs. Provide this if you want to delete users from a specific document.
Array of user Ids.
## **Example Requests**
#### 1. Delete users in a specific organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userIds": [
"yourUserId1"
]
}
}
```
#### 2. Delete users in a specific document within an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId1"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "User(s) deleted successfully.",
"data": {
"yourUserId1": {
"success": true,
"message": "User removed."
}
}
}
}
```
#### User(s) Not Found
```JSON
{
"result": {
"status": "success",
"message": "User(s) deleted successfully.",
"data": {
"yourUserId1": {
"success": true,
"message": "User removed."
},
{
"yourUserId2": {
"success": false,
"message": "User does not exist."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "User(s) deleted successfully.",
"data": {
"yourUserId1": {
"success": true,
"message": "User removed."
}
}
}
}
```
# Get Users
POST https://api.velt.dev/v1/users/get
Use this API to retrieve users based on various filters such as organization ID, document ID, organization user group IDs or user IDs. You can use these filters in various combinations to get the desired users. Some examples are shown below.
# Endpoint
`POST https://api.velt.dev/v1/users/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### **Params**
Organization ID
Document ID
Array of User IDs. Limit: Only 30 items can be passed at a time.
Array of Organization User Group IDs. Only 30 items can be passed at a time.
## **Example Requests**
#### 1. Get users by organizationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId"
}
}
```
#### 2. Get users by documentId within an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId"
}
}
```
#### 3. Get users by specific user IDs in an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userIds": [
"yourUserId1",
"yourUserId2"
]
}
}
```
#### 4. Get users by specific user IDs in the given organization and document
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId1",
"yourUserId2"
]
}
}
```
#### 5. Get users by organization and organization user group IDs
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"organizationUserGroupIds": [
"yourOrganizationUserGroupId"
]
}
}
```
#### 6. Get users by organization, organization user group IDs and user IDs
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userIds": [
"yourUserId1",
"yourUserId2"
],
"organizationUserGroupIds": [
"yourOrganizationUserGroupId"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "User(s) retrieved successfully.",
"data": [
{
"email": "userEmail@domain.com",
"name": "userName",
"userId": "yourUserId"
}
]
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "Error retrieving user(s).",
"status": "ERROR_CODE"
}
}
```
```js
{
"result": {
"status": "success",
"message": "User(s) retrieved successfully.",
"data": [
{
"email": "userEmail@domain.com",
"name": "userName",
"userId": "yourUserId"
}
]
}
}
```
# Get Users v2
POST https://api.velt.dev/v2/users/get
Use this API to retrieve users based on various filters such as organization ID, document ID, organization user group IDs or user IDs. You can use these filters in various combinations to get the desired users. Some examples are shown below.
# Endpoint
`POST https://api.velt.dev/v2/users/get`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### **Params**
Organization ID
Document ID
Array of User IDs. Limit: Only 30 items can be passed at a time.
Array of Organization User Group IDs. Only 30 items can be passed at a time.
Number of items to be retrieved per page. Default: 1000.
Page token retrieved from previous API call.
## **Example Requests**
#### 1. Get users by organizationId
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"pageSize": 1000,
"pageToken": "pageToken"
}
}
```
#### 2. Get users by documentId within an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId"
}
}
```
#### 3. Get users by specific user IDs in an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userIds": [
"yourUserId1",
"yourUserId2"
]
}
}
```
#### 4. Get users by specific user IDs in the given organization and document
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"userIds": [
"yourUserId1",
"yourUserId2"
]
}
}
```
#### 5. Get users by organization and organization user group IDs
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"organizationUserGroupIds": [
"yourOrganizationUserGroupId"
]
}
}
```
#### 6. Get users by organization, organization user group IDs and user IDs
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"userIds": [
"yourUserId1",
"yourUserId2"
],
"organizationUserGroupIds": [
"yourOrganizationUserGroupId"
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "User(s) retrieved successfully.",
"data": [
{
"email": "userEmail@domain.com",
"name": "userName",
"userId": "yourUserId"
}
],
"pageToken": "pageToken"
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "Error retrieving user(s).",
"status": "ERROR_CODE"
}
}
```
```js
{
"result": {
"status": "success",
"message": "User(s) retrieved successfully.",
"data": [
{
"email": "userEmail@domain.com",
"name": "userName",
"userId": "yourUserId"
}
],
"pageToken": "pageToken"
}
}
```
# Update Users
POST https://api.velt.dev/v1/users/update
Use this API to update user metadata based on various filters such as organization ID, document ID, and user IDs.
You can use these filters in various combinations to get the desired results.
The user metadata such as name, email etc can be updated.
# Endpoint
`POST https://api.velt.dev/v1/users/update`
# Headers
Your API key.
Your [Auth Token](/security/auth-tokens).
# Body Example
#### **Params**
Organization ID
Document ID (Optional)
Array of [User](/api-reference/models/User) objects.
## **Example Requests**
#### 1. Update users in a specific organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"users": [
{
"userId": "yourUserId1",
"name": "User Name",
"email": "user@email.com"
}
]
}
}
```
#### 2. Update users in a specific document within an organization
```JSON
{
"data": {
"organizationId": "yourOrganizationId",
"documentId": "yourDocumentId",
"users": [
{
"userId": "yourUserId1",
"name": "User Name",
"email": "user@email.com"
}
]
}
}
```
# Response
#### Success Response
```JSON
{
"result": {
"status": "success",
"message": "User(s) processed successfully.",
"data": {
"yourUserId1": {
"success": true,
"id": "7d87015b055a168b098cf05b870e40ff",
"message": "User updated."
}
}
}
}
```
#### Some User(s) Not Found
```JSON
{
"result": {
"status": "success",
"message": "User(s) processed successfully.",
"data": {
"yourUserId1": {
"success": true,
"id": "7d87015b055a168b098cf05b870e40ff",
"message": "User updated."
},
"yourUserId2": {
"success": false,
"id": "ad22d93b49ad990d2b3d582d08d7768a",
"message": "User does not exist."
}
}
}
}
```
#### Failure Response
```JSON
{
"error": {
"message": "ERROR_MESSAGE",
"status": "INVALID_ARGUMENT"
}
}
```
```js
{
"result": {
"status": "success",
"message": "User(s) processed successfully.",
"data": {
"yourUserId1": {
"success": true,
"id": "7d87015b055a168b098cf05b870e40ff",
"message": "User updated."
}
}
}
}
```
# Customize Behavior
## 1. Getting the Arrow Element
To get access to the Arrow Element APIs, you will first need to get the Arrow Element object from the client.
```jsx
const arrowElement = client.getArrowElement();
```
```jsx
const arrowElement = Velt.getArrowElement();
```
## 2. Set which elements Arrows can be added to
You can use the `allowedElementIds()` property to set an allowed list of elements the `Arrows` feature can be added to.
```jsx
```
```jsx
```
API Methods:
You can use the `arrowElement.allowedElementIds()` method to set an allowed list of elements the `Arrows` feature can be added to.
```jsx
arrowElement.allowedElementIds(['ALLOWED_ID_1', 'ALLOWED_ID_2']);
```
```jsx
arrowElement.allowedElementIds(['ALLOWED_ID_1', 'ALLOWED_ID_2']);
```
## 3. Dark Mode
Whether dark mode is enabled.
`Default: false`
```js
```
```js
```
# Custom Button
## Custom Arrow Button
If you want to replace the default arrow button with your own custom button, you can pass it in as a child component.
```js React / Next.js
import { VeltArrowTool } from '@veltdev/react';
function YourComponent() {
return (
//custom arrow button goes here
)
}
```
```html HTML
```
# null
We offer several parts which can be used like classes. Full list below.
The component is encapsulated in Shadow DOM, which is isolated from the normal DOM.
Set whatever CSS rules you want.
The part lets you target a specific element within a Shadow DOM.
Reference the table below to see what parts we expose.
Alternatively, you can directly inspect the component HTML to see what parts are available.
| property | description |
| ------------------ | ----------------------------------------- |
| `container` | Targets the comment tool container |
| `button-container` | Targets the comment tool button container |
| `button-icon` | Targets the comment tool button SVG icon |
```css Tool
velt-arrow-tool::part(button-icon) {
width: 1.5rem;
height: 1.5rem;
}
```
# Slots
Provide a template for the Arrow Tool.
Target the `button` slot with your own custom template.
```js React / Next.js
import {
VeltArrowTool
} from '@veltdev/react';
export default function App() {
return (
<>
>
);
}
```
```html HTML
Arrow documentation
```
# null
To update CSS variables for the Arrow Tool, please refer to [Global Styles](/global-styles/global-styles)
# Arrows
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=arrows)
# Setup
Import the `Arrow` components
```js
import { VeltArrows, VeltArrowTool } from '@veltdev/react';
```
Place the `VeltArrows` component at the root of your app.
```js
```
Place the `VeltArrowsTool` component wherever you want the invite button to appear.
```js
```
Place the `` component at the root of your app.
```html
```
Place the component wherever you want the invite button to appear.
```html
```
```js React / Next.js
import { VeltArrows, VeltArrowTool } from '@veltdev/react';
function YourComponent() {
return (
)
}
```
```html HTML
Collaboration App
```
# Customize Behavior
## 1. Comment click event
* Listen for click events on comments in the sidebar to trigger actions like navigation.
* The event callback provides access to the clicked comment's annotation object, which includes `location` and `context` data.
* Use this data to update your app's state and navigate to the comment's location.
The event handler receives an object with the following properties:
| Property | Type | Description |
| ----------------- | ----------------- | ------------------------------------------------ |
| `documentId` | string | ID of the document containing the comment |
| `location` | Object | Location details of the comment |
| `targetElementId` | string | DOM ID of the element the comment is attached to |
| `context` | Object | Custom context data associated with the comment |
| `annotation` | CommentAnnotation | The full comment annotation object |
```js
// event handler for when a comment is clicked on
const onCommentClick = (event) => {
//handle custom navigation by getting location if you have used Locations
const { pageId } = event.location;
//handle custom navigation by getting context if you have used addContext()
const { pageId } = event.context;
yourNavigateToPageMethod(pageId);
};
```
```js
// event handler for when a comment is clicked on
const onCommentClick = (event) => {
//handle custom navigation by getting location if you have used Locations
const { pageId } = event.location;
//handle custom navigation by getting context if you have used addContext()
const { pageId } = event.context;
yourNavigateToPageMethod(pageId);
};
const commentElement = document.querySelector('velt-comments-sidebar');
commentElement.addEventListener('onCommentClick', onCommentClick);
```
## 2. Custom filtering, sorting and grouping
* Here is the overview on how it works:
* Enable custom actions in the comments sidebar.
* Add action buttons to the sidebar wireframe.
* Implement callback and event handlers to handle custom filtering, sorting, and grouping logic.
* Set custom filtered data in the comments sidebar.
* Here are the steps to implement it:
Using Props:
```jsx
```
Using API:
```javascript
commentElement.enableSidebarCustomActions();
commentElement.disableSidebarCustomActions();
```
Using Props:
```html
```
Using API:
```javascript
commentElement.enableSidebarCustomActions();
commentElement.disableSidebarCustomActions();
```
* Specify an `id` to the action button. This can be used to identify which button was clicked/toggled in the callback or lifecycle events.
* There are two types of action buttons available:
* `toggle`: This button has on and off states. This can be used to enable `AND` logic.
* `default`: This button has only one state. This can be used to take action on click eg: sort, filter or group by a field.
```jsx
```
```html
```
* Set default custom sorting and filtering options in the comments sidebar.
* When the sidebar is initialized, the default actions will be applied.
* The keys of the object are the IDs of the action buttons you defined in the wireframe.
```javascript
commentElement.setCommentSidebarDefaultCustomActions({
'all': true,
'unread': false,
'mentions': false,
'current-clip': false
});
```
* Handle lifecycle and click events to implement custom filtering, sorting, and grouping logic:
* `onCommentSidebarActionButtonClick`: Triggered when an action button is clicked.
* `onCommentSidebarInit`: Triggered when the comments sidebar is initialized.
* `onCommentSidebarData`: Triggered when the comments sidebar data is updated.
* The events/lifecycle methods return a [CommentSidebarCustomActionEventData](/api-reference/models/CommentSidebarCustomActionEventData) object containing:
* `actions`: Map of custom action button states
* `data`: All unfiltered comment annotations
* `unreadDataMap`: Map of unread comment counts by annotation ID
* `systemFilteredData`: Comment annotations filtered by system filters
```jsx
import { useCommentSidebarActionButtonClick, useCommentSidebarInit, useCommentSidebarData } from '@veltdev/react';
const commentSidebarActionButtonClickData: CommentSidebarCustomActionEventData = useCommentSidebarActionButtonClick();
const commentSidebarInitData: CommentSidebarCustomActionEventData = useCommentSidebarInit();
const commentSidebarData: CommentSidebarCustomActionEventData = useCommentSidebarData();
useEffect(() => {
if (commentSidebarActionButtonClick) {
// Custom Filtering | Sorting | Grouping Logic
}
}, [commentSidebarActionButtonClickData]);
useEffect(() => {
if (commentSidebarInit) {
// Custom Filtering | Sorting | Grouping Logic
}
}, [commentSidebarInitData]);
useEffect(() => {
if (commentSidebarData) {
// Custom Filtering | Sorting | Grouping Logic
}
}, [commentSidebarData]);
```
```javascript
const commentElement = Velt.getCommentElement();
commentElement.onCommentSidebarActionButtonClick().subscribe((data) => {
// Custom Filtering | Sorting | Grouping Logic
});
commentElement.onCommentSidebarInit().subscribe((data) => {
// Custom Filtering | Sorting | Grouping Logic
});
commentElement.onCommentSidebarData().subscribe((data) => {
// Custom Filtering | Sorting | Grouping Logic
});
```
* Once you have applied your custom filtering, sorting, and grouping logic, create the data an **array** of [CommentSidebarData](/api-reference/models/CommentSidebarData) objects and set it in the comments sidebar.
* Use the `options` parameter to control if you want to group the comments or not.
```jsx
const options = {
grouping: false
};
const customFilterData = [
{
"groupId": "group1",
"groupName": "Group 1",
"isExpanded": true,
"annotations": [
{
...annotation1
},
{
...annotation2
},
]
}
];
const commentElement = client.getCommentElement();
commentElement.setCommentSidebarData(customFilterData, options);
```
```javascript
const options = {
grouping: false
};
const customFilterData = [
{
"groupId": "group1",
"groupName": "Group 1",
"isExpanded": true,
"annotations": [
{
...annotation1
},
{
...annotation2
},
]
}
];
const commentElement = Velt.getCommentElement();
commentElement.setCommentSidebarData(customFilterData, options);
```
## 3. Add composer to sidebar with Page mode
* This adds a composer in the sidebar where users can add comments without attaching them to any specific element.
`Default: false`
```jsx
```
```jsx
```
## 4. Embed Sidebar in your component
* By default, the sidebar will open up from the right corner of the page.
* With embed mode, you can add the sidebar in your component and it will take up the full width and height of its container.
* When in embed mode, the sidebar will not have the close button. You need to implement your own open and close functionality on the host component.
```jsx
```
```jsx
```
## 5. Exclude comments from certain locations
* Use this to filter out comments from certain locations. These comments will not be displayed in the sidebar.
Using Props:
```jsx
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.excludeLocationIdsFromSidebar(['location1', 'location2']);
```
Using Props:
```jsx
```
Using API:
```jsx
const commentElement = Velt.getCommentElement();
commentElement.excludeLocationIdsFromSidebar(['location1', 'location2']);
```
## 6. Floating mode
* This makes the sidebar open in an overlay panel over the sidebar button float over the page content.
```jsx
```
```jsx
```
## 7. Change sidebar open position
* Change the default direction where the sidebar opens from.
* Options: `left` or `right`
* Default: `right`
```jsx
```
```jsx
```
## 8. Enable/Disable Sidebar URL Navigation
* By default, clicking a comment in the sidebar doesn't automatically update the page URL where the comment was added.
* Use this to enable automatic URL navigation when clicking comments in the sidebar.
* If your app's state is more complex, you might need to listen for `onCommentClick` events and implement custom navigation logic.
Default: `false`
Using Props:
```jsx
```
Using API method:
```js
const commentElement = client.getCommentElement();
// to enable sidebar url navigation
commentElement.enableSidebarUrlNavigation();
// to disable sidebar url navigation
commentElement.disableSidebarUrlNavigation();
```
Using Props:
```js
```
Using API method:
```js
const commentElement = Velt.getCommentElement();
commentElement.enableSidebarUrlNavigation();
commentElement.disableSidebarUrlNavigation();
```
## 9. Open/close the Comments Sidebar
```jsx
const commentElement = client.getCommentElement();
commentElement.openCommentSidebar(); // opens the comments side bar
commentElement.closeCommentSidebar(); // closes the comments side bar
commentElement.toggleCommentSidebar(); // toggles the comments side bar
```
```jsx
const commentElement = client.getCommentElement();
commentElement.openCommentSidebar(); // opens the comments side bar
commentElement.closeCommentSidebar(); // closes the comments side bar
commentElement.toggleCommentSidebar(); // toggles the comments side bar
```
## 10. Enable/disable "this page" suffix
* Adds "(this page)" suffix to the group name when the current location matches the group's location.
Default: `false`
```jsx
```
```jsx
```
## 11. System Filters Customization
* Customize the available system filters:
* `location`
* `people`
* `priority`
* `category`
* You can rename, disable, configure grouping, and multi-select behavior of the filters as needed.
```jsx
const filterConfig = {
location: {
enable: true,
name: "Pages", // change the display name of the filter
enableGrouping: true, // whether to enable grouping based on location filter
multiSelection: true, // whether to enable multiselection for the filter
order: ['locationId1', 'locationId2', 'locationId3'] // change the order of the filter options
},
people: {
enable: true,
name: "People", // change the display name of the filter
enableGrouping: true, // whether to enable grouping based on people filter
multiSelection: true, // whether to enable multiselection for the filter
},
priority: {
enable: true,
name: "Priority", // change the display name of the filter
enableGrouping: false, // whether to enable grouping based on priority filter
multiSelection: true, // whether to enable multiselection for the filter
},
category: {
enable: true,
name: "Category", // change the display name of the filter
enableGrouping: true, // whether to enable grouping based on category filter
multiSelection: true, // whether to enable multiselection for the filter
}
}
```
```jsx
const filterConfig = {
location: {
enable: true, // enable/disable location filter
name: "Pages", // customize location filter heading
enableGrouping: true, // to enable/disable grouping based on location filter
multiSelection: true, // to enable/disable multiple selection
order: ['locationId1', 'locationId2', 'locationId3'] // pass array of location ids here
},
people: {
enable: true, // enable/disable people filter
name: "People", // customize people filter heading
enableGrouping: true, // to enable/disable grouping based on people filter
multiSelection: true, // to enable/disable multiple selection
},
priority: {
enable: true, // enable/disable priority filter
name: "Priority", // customize priority filter heading
enableGrouping: false, // to enable/disable grouping based on priority filter
multiSelection: true, // to enable/disable multiple selection
},
category: {
enable: true, // enable/disable category filter
name: "Category", // customize category filter heading
enableGrouping: true, // to enable/disable grouping based on category filter
multiSelection: true, // to enable/disable multiple selection
}
};
const commentsSidebar = document.querySelector(`velt-comments-sidebar`);
commentsSidebar?.setAttribute("filter-config", JSON.stringify(filterConfig));
```
## 12. System Grouping Customization
* Enable/disable the option to group comments in the sidebar with the `group config` attribute:
```jsx
const groupConfig = {
enable: true, // whether to enable group by option
name: "Custom Group By", // change the display name of the group by option in the filter panel
};
```
```jsx
const groupConfig = {
enable: true, // whether to enable group by option
name: "Custom Group By", // change the display name of the group by option in the filter panel
};
const commentsSidebar = document.querySelector(`velt-comments-sidebar`);
commentsSidebar?.setAttribute("group-config", JSON.stringify(groupConfig));
```
## 13. System Sorting Customization
* Change the default sorting order of Comments in the Sidebar.
* Default: `desc`
There are three options for sorting:
* `asc` - to show comments in ascending order of when they were last updated
* `desc` - to show comments in descending order of when they were last updated
* `none` - to show comments in the sequence they were added
```jsx
```
```jsx
```
## 14. Apply System Filters Programmatically
* Programmatically set the system filters on the sidebar:
```jsx
const filters = {
location: [
{ id: 'location1Id' }, // id field is required
{ id: 'location2Id' }, // id field is required
],
people: [
{ userId: 'userIdToFilter' }, // userId or
{ email: 'userEmailToFilter' } // user email is required
],
priority: ['P0', 'P1', 'P2'], // default supported values
status: ['OPEN', 'IN_PROGRESS', 'RESOLVED'], // default supported values
category: ['bug', 'feedback', 'question'], // default supported values
};
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.setCommentSidebarFilters(filters);
```
```jsx
const filters = {
location: [
{ id: 'location1Id' }, // id field is required
{ id: 'location2Id' }, // id field is required
],
people: [
{ userId: 'userIdToFilter' }, // userId or
{ email: 'userEmailToFilter' } // user email is required
],
priority: ['P0', 'P1', 'P2'], // default supported values
status: ['OPEN', 'IN_PROGRESS', 'RESOLVED'], // default supported values
category: ['bug', 'feedback', 'question'], // default supported values
};
const commentsSidebar = document.querySelector(`velt-comments-sidebar`);
commentsSidebar?.setAttribute("filters", JSON.stringify(filters));
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.setCommentSidebarFilters(filters);
```
## 15. Filter panel layout customization
* Change the layout of the filter panel in the sidebar.
* Options: `menu` or `bottomSheet`
`Default: bottomSheet`
```jsx
```
```jsx
```
## 16. Focused Thread Mode
* In this mode, when you click on a comment in the sidebar, it opens the thread in an expanded view within the sidebar itself.
* Other threads and actions like filters, search etc. are hidden behind a back button.
* Enabling this mode also adds a navigation button in the comment dialog. Clicking it will navigate to the comment and trigger a callback.
* If you had previously used a wireframe for the comment dialog, you will need to add the [navigation button wireframe](/async-collaboration/comments/customize-ui/comment-dialog/subcomponents/header) and the [focused thread wireframe](/async-collaboration/comments-sidebar/customize-ui/comments-sidebar/overview).
```jsx
```
**Handling the navigation button click:**
```jsx
// event handler for when a comment is clicked on
const onCommentNavigationButtonClick = (event) => {
console.log('onCommentNavigationButtonClick', event);
//handle custom navigation by getting location if you have used Locations
const { pageId } = event.location;
//handle custom navigation by getting context if you have used addContext()
const { pageId } = event.context;
yourNavigateToPageMethod(pageId);
};
```
```html
```
**Handling the navigation button click:**
```javascript
const commentSidebarElement = document.querySelector('velt-comments-sidebar');
commentSidebarElement.addEventListener('onCommentNavigationButtonClick', (s) => {
console.log('onCommentNavigationButtonClick', s.detail);
});
```
# Overview
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default comments sidebar component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sidebar-light.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sidebar-breakdown-light.png)
```jsx React / Next.js
{/* Skeleton */}
{/* Header - Contains search, status filters, close button etc */}
{/* Custom Action Button - Use to define custom filter, sorting, grouping */}
{/* Filter - The filter panel that contains the default system filters */}
{/* List - The list of comments that appear in the sidebar */}
{/* Empty Placeholder - The placeholder that appears when there are no comments */}
{/* Reset Filter Button - Not included in default component */}
{/* Page Mode Composer - The composer that appears when the page mode is enabled */}
{/* Focused Thread - This only appears when focused thread mode is enabled */}
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
### Example
```jsx
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-sidebar-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sidebar-breakdown-dark.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
To enable Dark Mode for comments sidebar:
### Example
```js
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```js
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# null
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar that represents the placeholder when there are no Comments
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-placeholder.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar that represents the filter that is used to filter what Comments appear in the Sidebar
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-filter.png)
```jsx React / Next.js
{/* Title */}
{/* Close Button */}
{/* Location */}
{/* People */}
{/* Category */}
{/* Priority */}
{/* CommentType */}
{/* Versions */}
{/* GroupBy */}
{/* Done Button */}
```
```HTML HTML
/velt-comments-sidebar-filter-people-wireframe>
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Category
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-category.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Close button
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-close.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Comment Type filter
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/comment-type-filter.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Done Button
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-done.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
Used to customize all the filter option items of the Comments Sidebar Filter at once
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-item.png)
## Subcomponents that don't support children
* `VeltCommentsSidebarWireframe.Search`
* `VeltCommentsSidebarWireframe.Filter.Item.Count`
* `velt-comments-sidebar-search-wireframe`
* `velt-comments-sidebar-filter-item-count-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Groupby filter option
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-groupby.png)
```jsx eact / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Location filter
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-location.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the People filter
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-people.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Priority filter
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-priority.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Title
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-title.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar Filter that represents the Version filter
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-versions.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
{/* Back Button to back to default view with all threads */}
{/* Container that contains the comment dialog */}
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar that represents the Header of the Sidebar
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-header.png)
## Subcomponents that don't support children
* `VeltCommentsSidebarWireframe.Search`
* `VeltCommentsSidebarWireframe.Status`
* `velt-comments-sidebar-search-wireframe`
* `velt-comments-sidebar-status-wireframe`
```jsx React / Next.js
{/* Not shown for embedded comments sidebar */}
{/* Not included in the default component */}
{/* Not included in the default component */}
{/* Not included in the default component */}
```
```HTML
```
# Overview
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
This component is not included in the default Comments Sidebar component. You need to explicitly add it to the sidebar wireframe.
```jsx React / Next.js
```
```HTML HTML
```
# Overview
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
* This enables actions like `Mark all read` and `Mark all resolved` in the sidebar. By default it's not enabled. You need to explicitly add the wireframe to the sidebar.
```jsx React / Next.js
```
```HTML HTML
```
# Overview
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
* This enables minimal filtering and sorting dropdown in the sidebar. By default it's not enabled. You need to explicitly add the wireframe to the sidebar. It includes options like:
* Filter by `All`
* Filter by `Unread`
* Filter by `Read`
* Filter by `Resolved`
* Sort by `Unread`
* Sort by `Last Updated Timestamp`
```jsx React / Next.js
Sort date
Sort unread
Filter all
Filter unread
Filter read
Filter resolved
```
```HTML HTML
Sort by date
Sort by unread
Unread or Read
Unread only
Read only
Resolved
```
# Overview
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/comments-sidebar-dropdown.png)
```jsx React / Next.js
```
```HTML HTML
```
# Content
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-status-content.png)
```jsx React / Next.js
```
```HTML HTML
```
# Trigger
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/filter-status-trigger.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar that represents the List of Comments in the Sidebar
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/list.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
You can customize the Comment Dialog that appears inside the Sidebar with this subcomponent
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
You can customize the Comment Dialog that appears in the Sidebar by adding a Comment Dialog Wireframe inside this subcomponent and modifying it to your liking.
```jsx React / Next.js
...
```
```HTML HTML
...
```
# null
The subcomponent of the Comments Sidebar List that represents the Group
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar that represents the Composer that appears in Page Mode.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-composer.png)
This subcomponent does not support children
```jsx React / Next.js
```
```HTML HTML
```
# null
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comments Sidebar that represents the Skeleton loader that appears when the Sidebar is first loading.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-skeleton.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
## Variants
Here are the variants that you can use in Comments Sidebar:
* `variant`: This is the variant for the entire Comments Sidebar.
* `dialogVariant`: This is the variant for the Comment Dialog that appears in the Comments Sidebar.
* `pageModeComposerVariant`: This is the variant for the Comment Composer that appears in the Comments Sidebar in page mode.
* `focusedThreadDialogVariant`: This is the variant for the Comment Dialog that appears when a focused thread mode is enabled.
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The button that is used to open the Comments Sidebar panel.
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default sidebar button looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sidebar-button-light.png)
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### Example
```jsx
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sidebar-button-dark.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
To enable Dark Mode for sidebar button:
### Example
```js
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```js
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# null
The subcomponent on the Sidebar Button that shows the total
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-button-count.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Sidebar Button that shows the total
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/sidebar-button-icon.png)
```jsx React / Next.js
```
```HTML HTML
```
# Comments Sidebar
Provide a toggleable sidebar to view and filter comments.
The Velt SDK contains 4 components that can be used to control the Comments Sidebar functionality:
* `VeltComments` - Used to enable the Comments feature site wide
* `VeltCommentsSidebar` - The Sidebar that holds all existing comments
* `VeltSidebarButton` - A button that toggles the `VeltCommentsSidebar`on and off
* `VeltCommentTool` - A button that turns on the Comments functionality
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=sidebar)
# Setup
Import the Comments Sidebar Components.
```jsx
import {
VeltProvider,
VeltCommentsSidebar,
VeltSidebarButton,
VeltCommentTool
} from '@veltdev/react';
```
Add the `VeltComments` and `VeltCommentsSidebar` components to the root of your app.
```jsx
```
Add the Sidebar button to toggle the sidebar. Add the `VeltCommentTool` component to leave comments.
```jsx
```
This is completely optional and you can toggle the sidebar in the comment dialog as well.
Test it out by opening the sidebar.
You should be able to click the `All comments` link in a comment dialog box on the bottom.
Place the `` component at the root of your app.
```html
```
Place the `` component wherever you want the toggle button to appear.
```html
```
```jsx React / Next.js
import {
VeltProvider,
VeltCommentsSidebar,
VeltSidebarButton,
VeltCommentTool
} from '@veltdev/react';
export default function App() {
return (
{/* Add VeltComments to the root of your app provider */}
{/* Add VeltCommentsSidebar to the root of your app provider */}
{/* Add VeltCommentTool wherever you want it to appear */}
{/* Add VeltCommentSideBarButton wherever you want it to appear */}
);
}
```
```html HTML
Collaboration App
```
# @Mentions
## 1. Update Contact List
* By default, the contact list is generated using the users in the organization and the document.
* However, if you do not want to use that feature or want to provide a custom list of contacts, you can use this method.
* By default, it will overwrite the current contact list. You can merge the provided contacts with the existing list by passing the merge flag as `{merge:true}`.
* This method will only update the contact list in the current user session. It doens't update the user contacts in the database or change the access control.
```jsx
const contactElement = useContactUtils();
useEffect(() => {
contactElement.updateContactList([{userId: 'userId1', name: 'User Name', email: 'user1@velt.dev'}], {merge: false});
}, [contactElement]);
```
```jsx
const contactElement = client.getContactElement();
contactElement.updateContactList([{userId: 'userId1', name: 'User Name', email: 'user1@velt.dev'}], {merge: false});
```
```jsx
const contactElement = Velt.getContactElement();
contactElement.updateContactList([{userId: 'userId1', name: 'User Name', email: 'user1@velt.dev'}], {merge: false});
```
## 2. Callback on Contact Selection
* This event is triggered when a contact is selected from the contact dropdown in the Comment Dialog.
* Use the event object to determine if the selected contact has access to the document using fields like `isOrganizationContact`, `isDocumentContact` and `documentAccessType`.
* If the selected contact doesn't have access to the document, you can show an invite dialog to the user to invite the contact to the document.
The returned data will be in the following schema:
```jsx
export class UserContactSelectedPayload {
contact!: UserContact; // Selected Contact.
isOrganizationContact!: boolean; // Is user part of organization contact.
isDocumentContact!: boolean; // Is user part of document contact.
documentAccessType!: string; // Document access type.
}
```
```jsx
const selectedContact = useContactSelected();
useEffect(() => {
console.log('selectedContact: ', selectedContact);
}, [selectedContact]);
```
```jsx
const contactElement = client.getContactElement();
contactElement.onContactSelected().subscribe((selectedContact: any) => {
console.log('selectedContact : ', selectedContact);
});
```
```jsx
const contactElement = Velt.getContactElement();
contactElement.onContactSelected().subscribe((selectedContact: any) => {
console.log('selectedContact: ', selectedContact);
});
```
## 3. Enable @here feature
* This allows you to notify all the users explicitly added to the current document.
* It won't notify users in the organization who are not explicitly added to the document.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/enable-@-here.png)
```jsx
const contactElement = useContactUtils();
useEffect(() => {
contactElement.enableAtHere();
contactElement.disableAtHere();
}, [contactElement]);
```
```jsx
const contactElement = client.getContactElement();
contactElement.enableAtHere();
contactElement.disableAtHere();
```
```jsx
const contactElement = Velt.getContactElement();
contactElement.enableAtHere();
contactElement.disableAtHere();
```
## 4. Change @here label to custom text
* This allows you to modify the default text of the @here feature. eg: @all, @everyone, @team, etc.
Using Props:
```jsx
```
Using API Method:
```jsx
const contactElement = useContactUtils();
useEffect(() => {
contactElement.setAtHereLabel('@all');
}, [contactElement]);
```
Using Props:
```jsx
```
Using API Method:
```jsx
const contactElement = client.getContactElement();
contactElement.setAtHereLabel('@all');
```
Using Props:
```jsx
```
Using API Method:
```jsx
const contactElement = Velt.getContactElement();
contactElement.setAtHereLabel('@all');
```
## 5. Change @here description
* Customize the description that appears for the @here mention.
Using Props:
```jsx
```
Using API Method:
```jsx
const contactElement = useContactUtils();
useEffect(() => {
contactElement.setAtHereDescription('Notify all users in this document');
}, [contactElement]);
```
Using Props:
```jsx
```
Using API Method:
```jsx
const contactElement = client.getContactElement();
contactElement.setAtHereDescription('Notify all users in this document');
```
Using Props:
```html
```
Using API Method:
```javascript
const contactElement = Velt.getContactElement();
contactElement.setAtHereDescription('Notify all users in this document');
```
## 6. Enable user @mentions
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/at-mentions.png)
Whether user @mentions are enabled.
`Default: true`
**Using props:**
```jsx
```
**Using API Method:**
```jsx
const contactElement = useContactUtils();
useEffect(() => {
contactElement.enableUserMentions();
contactElement.disableUserMentions();
}, [contactElement]);
```
**Using props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = client.getCommentElement();
commentElement.enableUserMentions(); // to enable user mentions
commentElement.disableUserMentions(); // to disable user mentions
```
**Using props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableUserMentions(); // to enable user mentions
commentElement.disableUserMentions(); // to disable user mentions
```
## 7. Update Contact List Visibility For Organization Users
* Sometimes you may want to show only certain types of contacts in the contact dropdown.
* By default, organization users will see all contacts in the organization, any user groups and any contacts added to the document.
* Using this method, you can restrict the contacts shown in the dropdown to only certain types.
* This only affects the Organization Users and not the Document Users. Document Users will always only see contacts added to the document.
Here are the available options:
* `all`: Show all the contacts
* `organization`: Show organization contacts.
* `organizationUserGroup`: Show organization user groups.
* `document`: Show document contacts.
```jsx
const contactElement = client.getContactElement();
contactElement.updateContactListScopeForOrganizationUsers(['all', 'organization', 'organizationUserGroup', 'document']);
```
```jsx
const contactElement = Velt.getContactElement();
contactElement.updateContactListScopeForOrganizationUsers(['all', 'organization', 'organizationUserGroup', 'document']);
```
# Action Methods
## 1. Add comment on selected text
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/addCommentOnSelectedText.png)
By default, when you highlight over any text in `textMode` a Comment Tool button will appear. Clicking the button will add a comment on the highlighted text.
If you want to trigger the comment using an API method call instead of clicking the button, you can do the following:
```jsx
const commentElement = client.getCommentElement();
// to add comment on selected text
commentElement.addCommentOnSelectedText();
```
```jsx
const commentElement = Velt.getCommentElement();
// to add comment on selected text
commentElement.addCommentOnSelectedText();
```
## 2. Add Comments on specific elements
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/addCommentOnElement.png)
Adds a Comment on a specific element by ID.
To add a comment on a specific element through an API method, use the `addCommentOnElement()` method and pass in an object with the schema shows in the example:
```jsx
const element = {
"targetElement": {
"elementId": "element_id", // optional (pass elementId if you want to add comment on a specific element)
"targetText": "target_text", // optional (pass targetText if you want to add comment on a specific text)
"occurrence": 1, // optional (default: 1) This is relevant for text comment. By default, we will attach comment to the first occurence of the target text in your document. You can change this to attach your comment on a more specific text.
"selectAllContent": true, // Set to `true` if you want to select all the text content of the target element.
},
"commentData": [
{
"commentText": "This is awesome! Well done.", // To set plain text content
"commentHtml": "This is test comment.", // To set HTML formatted content
"replaceContentText": "This is new comment", // provide this replaceContentText to replace current text with
"replaceContentHtml": "This is new comment.", // If replacement text contains html formatted text, then provide it here
}
],
"status": "open", // optional (default: open)
}
const commentElement = client.getCommentElement();
commentElement.addCommentOnElement(element);
```
```jsx
const element = {
"targetElement": {
"elementId": "element_id", // optional (pass elementId if you want to add comment on a specific element)
"targetText": "target_text", // optional (pass targetText if you want to add comment on a specific text)
"occurrence": 1, // optional (default: 1) This is relevant for text comment. By default, we will attach comment to the first occurence of the target text in your document. You can change this to attach your comment on a more specific text.
"selectAllContent": true, // Set to `true` if you want to select all the text content of the target element.
},
"commentData": [
{
"commentText": "This is awesome! Well done.", // To set plain text content
"commentHtml": "This is test comment.", // To set HTML formatted content
"replaceContentText": "This is new comment", // provide this replaceContentText to replace current text with
"replaceContentHtml": "This is new comment.", // If replacement text contains html formatted text, then provide it here
}
],
"status": "open", // optional (default: open)
}
const commentElement = Velt.getCommentElement();
commentElement.addCommentOnElement(element);
```
## 3. Add Manual Comment
* This feature is particularly useful for complex UIs where you need precise control over the placement of Comment Pins.
* Using this you can manually set the position of Comment Annotations.
* Handle click events on your canvas/document and use the this method to create a comment with custom metadata.
```jsx
const context = {
postion: {x: 200, y: 100},
};
const commentElement = client.getCommentElement();
const config: ManualCommentAnnotationConfig = {
context: context, // your context here
};
commentElement.addManualComment(config);
```
```jsx
const context = {
postion: {x: 200, y: 100},
};
const commentElement = Velt.getCommentElement();
const config: ManualCommentAnnotationConfig = {
context: context, // your context here
};
commentElement.addManualComment(config);
```
## 4. Turning on and off Comment Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-mode-on-off.png)
Turns Comment mode on or off.
When you click on the comment tool, it turns on comment mode and user can attach comment to any element on the DOM. Using this method you can programatically turn on the commenting mode.
```jsx
const commentElement = client.getCommentElement();
commentElement.enableCommentMode();
commentElement.disableCommentMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableCommentMode();
commentElement.disableCommentMode();
```
## 5. Select CommentAnnotation by Annotation ID
* Use this to programatically select a comment annotation by its id.
* Example: If the user opens a comment url from an email notification,
you can use this open the comment dialog after your page has finished rendering.
```jsx
const commentElement = client.getCommentElement();
commentElement.selectCommentByAnnotationId("COMMENT_ANNOTATION_ID");
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.selectCommentByAnnotationId("COMMENT_ANNOTATION_ID");
```
## 6. Scroll the page directly to the comment element
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/scroll-on-click.gif)
This will scroll the page to the element directly. This will work if the element is present on the DOM.
```jsx
const commentElement = client.getCommentElement();
commentElement.scrollToCommentByAnnotationId('annotationId')
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.scrollToCommentByAnnotationId('annotationId')
```
## 7. Delete selected comment
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/deleteSelectedComment.png)
To delete a comment using an API method, use the `deleteSelectedComment()` method.
```jsx
if (client) {
const commentElement = client.getCommentElement();
commentElement.deleteSelectedComment();
}
```
```jsx
if (Velt) {
const commentElement = Velt.getCommentElement();
commentElement.deleteSelectedComment();
}
```
## 8. Get the Xpath of the DOM element on which the comment was added
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/getElementRefByAnnotationId.png)
This will return the Xpath of the DOM element on which the comment was added.
```jsx
const commentElement = client.getCommentElement();
let elementRef = commentElement.getElementRefByAnnotationId('annotationId')
```
```jsx
const commentElement = Velt.getCommentElement();
let elementRef = commentElement.getElementRefByAnnotationId('annotationId')
```
## 9. Update Comment Dialog Position
* Sometimes when you manually set the position of the Comment Pin, the Comment Dialog might not position itself near the pin in certain scenarios like scrolling, zooming the page when the comment dialog is open.
* Use this to manually trigger an update. The dialog will reposition itself near the pin.
```jsx
const commentElement = client.getCommentElement();
commentElement.updateCommentDialogPosition();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.updateCommentDialogPosition();
```
# Add Custom Lists
You can add custom lists at two levels: a. on the CommentAnnotation and b. on the Comment.
## 1. Add Custom Lists on Comment Annotation
* Add a custom dropdown list at the Comment Annotation level.
* Use this to add custom tags or categories to the comment.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/comment-annotation-custom-list.png)
```jsx
let customList = [
{ id: 'violent', label: 'Violent' },
{ id: 'inappropriate', label: 'Inappropriate' },
{ id: 'robbery', label: 'Robbery' },
{ id: 'nsfw', label: 'NSFW' },
];
const customListDataOnCommentAnnotation = {
type: 'multi', // choose from 'multi' or 'single'
placeholder: 'Select a category',
data: customList, // your customList data here
};
```
**Using Props:**
```jsx
```
**API Method:**
```jsx
const commentElement = useCommentUtils();
commentElement.createCustomListDataOnAnnotation(customListDataOnCommentAnnotation);
```
**Using Props:**
```jsx
```
**API Method:**
```jsx
const commentElement = client.getCommentElement();
commentElement.createCustomListDataOnAnnotation(customListDataOnCommentAnnotation);
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.createCustomListDataOnAnnotation(customListDataOnCommentAnnotation);
```
## 2. Add Custom Lists on Comment
You can have custom dropdown lists appear when certain `hotkeys` are pressed.
When you press a hotkey inside the Comment Dialog composer, it will open a dropdown list of items that you can select.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/custom-list-1.png)
Selecting an item frop the dropdown list will add a chip that inside the comment text.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/custom-list-2.png)
Make sure the hotkey is a single character such as `#` or `/`
* Create the list of data will be shown when the `hotkey` is pressed. Eg: When the user presses `#`, the list of files or links from other parts of your app is shown.
* The items in the list must be in the following schema:
```jsx
export class AutocompleteItem {
id!: string; // Unique identifier
name!: string; // Item name. This appears as the main item text in the UI.
description?: string; // Item description. This appears as the secondary item text in the UI.
icon?: { url?: string, svg?: string }; // Item icon. This appears as the icon in the UI.
link?: string; // Item link. You can use this to open a link when the item is clicked. Check the event listener below for more details.
}
```
```jsx
let customList = [
{
id: '1',
name: 'File 1',
description: 'File Description 1',
icon: {url: 'https://cdn-icons-png.flaticon.com/512/9496/9496432.png'}
},
{
id: '2',
name: 'File 2',
description: 'File Description 2',
icon: {url: 'https://cdn-icons-png.flaticon.com/512/11471/11471469.png'}
},
{
id: '3',
name: 'File 3',
description: 'File Description 3',
icon: {url: 'https://cdn-icons-png.flaticon.com/512/2656/2656402.png'}
}
];
const customListDataOnComment = {
hotkey: 'UNIQUE_HOTKEY', // only single charater is allowed. eg: '#'
type: 'custom',
data: customList, // your customList data here
};
```
**Using Props:**
```jsx
```
**API Method:**
```jsx
const commentElement = useCommentUtils();
commentElement.createCustomListDataOnComment(customListDataOnComment);
```
**Using Props:**
```jsx
```
**API Method:**
```jsx
const commentElement = client.getCommentElement();
commentElement.createCustomListDataOnComment(customListDataOnComment);
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.createCustomListDataOnComment(customListDataOnComment);
```
* After the comment is saved, the item will be rendered as a chip on the comment content.
* When the user clicks on it, you will get an event callback with the data of the clicked chip (AutocompleteItem).
This event will also be triggered when the user clicks on the contact chips added via the @mentions feature.
```jsx
let autocompleteChipData = useAutocompleteChipClick();
```
```jsx
const autocompleteElement = client.getAutocompleteElement();
const subscription = autocompleteElement.onAutocompleteChipClick().subscribe(_data => {
console.log(_data);
})
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
```jsx
const autocompleteElement = Velt.getAutocompleteElement();
const subscription = autocompleteElement.onAutocompleteChipClick().subscribe(_data => {
console.log(_data);
})
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
# Add Custom Metadata
## 1. Add Custom Metadata on Comment Annotation
Custom metadata allows you to add extra information to comments, enhancing their functionality. Here's what you can do with it:
* Render additional data on comments
* Position comment pins manually
* Create custom UI components
* Enable comment filtering on custom data
To add custom metadata, use the `event.addContext()` method when a comment is added. This method accepts an object with key-value pairs.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/addContext.png)
Using Props:
```js
yourMethod(event)} />
const yourMethod = (event) => {
event?.addContext({ customKey: 'customValue' });
}
```
Using Hooks:
```jsx
import { useCommentAddHandler} from '@veltdev/react';
export default function YourDocument() {
const commentAddEvent = useCommentAddHandler();
useEffect(() => {
console.log('commentAddEvent', commentAddEvent);
commentAddEvent?.addContext({ customKey: 'customValue' });
}, [commentAddEvent]);
return (
)
}
```
Using API method:
```js
const commentElement = client.getCommentElement();
commentElement.onCommentAdd().subscribe((event) => {
event?.addContext({ customKey: 'customValue' });
});
```
Using Event listener:
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onCommentAdd', (event) => {
console.log('*** onCommentAdd ***');
console.log(event.detail);
event.detail?.addContext({ customKey: 'customValue' });
});
```
Using API method:
```js
const commentElement = Velt.getCommentElement();
commentElement.onCommentAdd().subscribe((event) => {
event?.addContext({ customKey: 'customValue' });
});
```
## 2. Update Custom Metadata on Comment Annotation
You can update the custom metadata associated with a comment annotation using the `updateContext` method. This method is available in two scenarios:
1. **In the `onCommentUpdate` event callback:**
Use this to update the context when a comment is modified.
2. **Via the `updateContext` API method:**
Utilize this method to update the context of a comment annotation at any time. For example, you might use this when the name of the dashboard containing the comment annotation changes.
### #1. onCommentUpdate event
The `updateContext` method accepts two parameters:
* The new metadata object
* An optional `updateContextConfig` object. Specify how the new metadata should be applied:
* `{ merge: true }`: Merges the new metadata with the existing metadata
* `{ merge: false }` or omitted: Replaces the existing metadata entirely (default behavior)
Using Props:
```js
yourMethod(event)} />
const yourMethod = (event) => {
console.log('commentUpdateEvent', event);
event?.updateContext({ customKey: 'customValue' }, { merge: true });
}
```
Using Hooks:
```jsx
import { useCommentUpdateHandler} from '@veltdev/react';
export default function YourDocument() {
const commentUpdateEvent = useCommentUpdateHandler();
useEffect(() => {
console.log('commentUpdateEvent', commentUpdateEvent);
commentUpdateEvent?.updateContext({ customKey: 'customValue' }, { merge: true });
}, [commentUpdateEvent]);
return (
)
}
```
Using API:
```js
const commentElement = client.getCommentElement();
commentElement.onCommentUpdate().subscribe((event) => {
console.log('commentUpdateEvent', event);
event?.updateContext({ customKey: 'customValue' }, { merge: true });
});
```
Using Event Listener:
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onCommentUpdate', (event) => {
console.log('*** onCommentUpdate ***');
console.log(event.detail);
event.detail?.updateContext({ customKey: 'customValue' }, { merge: true });
});
```
Using API method:
```js
const commentElement = Velt.getCommentElement();
commentElement.onCommentUpdate().subscribe((event) => {
console.log('commentUpdateEvent', event);
event?.updateContext({ customKey: 'customValue' }, { merge: true });
});
```
### #2. commentElement.updateContext
The `commentElement.updateContext()` method accepts three parameters:
* The Comment Annotation ID
* The new metadata object
* An optional `updateContextConfig` object. Specify how the new metadata should be applied:
* `{ merge: true }`: Merges the new metadata with the existing metadata
* `{ merge: false }` or omitted: Replaces the existing metadata entirely (default behavior)
Using API:
```js
const updatedContext = { customKey: 'customValue' };
const updateContextConfig = { merge: true };
const commentElement = client.getCommentElement();
commentElement.updateContext(COMMENT_ANNOTATION_ID, updatedContext, updateContextConfig);
```
Using API method:
```js
const updatedContext = { customKey: 'customValue' };
const updateContextConfig = { merge: true };
const commentElement = client.getCommentElement();
commentElement.updateContext(COMMENT_ANNOTATION_ID, updatedContext, updateContextConfig);
```
# Event Handlers
## 1. Take action when a comment is added
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/onCommentAdd.png)
Using Props:
```js
yourMethod(event)} />
const yourMethod = (event) => {
event?.addContext({ customKey: 'customValue' });
}
```
Using Hooks:
```jsx
import { useCommentAddHandler} from '@veltdev/react';
export default function YourDocument() {
const commentAddEvent = useCommentAddHandler();
useEffect(() => {
console.log('commentAddEvent', commentAddEvent);
}, [commentAddEvent]);
return (
)
}
```
Using API:
```js
const commentElement = client.getCommentElement();
commentElement.onCommentAdd().subscribe((event) => {
console.log('commentAddEvent', event);
});
```
Using Event listener:
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onCommentAdd', (event) => {
console.log('*** onCommentAdd ***');
console.log(event.detail);
event.detail?.addContext({ customKey: 'customValue' });
});
```
Using API method:
```js
const commentElement = Velt.getCommentElement();
commentElement.onCommentAdd().subscribe((event) => {
event?.addContext({ customKey: 'customValue' });
});
```
**onCommentAdd Event Data Schema**
| Field Name | Type | Description |
| ------------------ | ----------------- | ------------------------------------------------------------------- |
| addContext | Function | Use this to set custom data on the comment |
| annotation | CommentAnnotation | The annotation that is associated with the comment that was updated |
| documentId | string | The document ID where the comment was added |
| location | Object | The location where the comment was added |
| targetAnnotationId | string | The id of the target annotation |
## 2. Take action when comment is updated
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/onCommentUpdate.png)
Using Props:
```js
yourMethod(event)} />
const yourMethod = (event) => {
console.log('commentUpdateEvent', event);
}
```
Using Hooks:
```jsx
import { useCommentUpdateHandler} from '@veltdev/react';
export default function YourDocument() {
const commentUpdateEvent = useCommentUpdateHandler();
useEffect(() => {
console.log('commentUpdateEvent', commentUpdateEvent);
}, [commentUpdateEvent]);
return (
)
}
```
Using API:
```js
const commentElement = client.getCommentElement();
commentElement.onCommentUpdate().subscribe((event) => {
console.log('commentUpdateEvent', event);
});
```
Using Event Listener:
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onCommentUpdate', (event) => {
console.log('*** onCommentUpdate ***');
console.log(event.detail);
});
```
Using API method:
```js
const commentElement = Velt.getCommentElement();
commentElement.onCommentUpdate().subscribe((event) => {
console.log('commentUpdateEvent', event);
});
```
**onCommentUpdate Event Data Schema**
| Field Name | Type | Description |
| ------------------ | ----------------- | -------------------------------------------------------------------------- |
| annotation | CommentAnnotation | The annotation that is associated with the comment that was updated |
| type | string | The type of comment that was updated |
| targetAnnotationId | string | The ID of the target annotation that contains the comment that was updated |
| targetCommentId | number | The ID of the target comment that was updated |
## 3. Listen to changes in comment mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/onCommentModeChange.png)
The comment mode is toggled on and off when you click on the Comment Tool.
The `useCommentModeState()` hook can be used to get the Comment mode without having to subscribe to changes. When the Comment mode changes, the hook return value will update.
The subscription is automatically unsubscribed when the component dismounts.
```jsx
import { useCommentModeState } from '@veltdev/react';
export default function YourDocument() {
let commentModeState = useCommentModeState()
return (
Comment Mode is turned on: {commentModeState}
)
}
```
To subscribe to changes in the comment mode, use the `onCommentModeChange()` method , as a property on `VeltCommentTool`:
```jsx
onCommentModeChange(mode)} />
```
API method:
```jsx
let subscription = commentElement.onCommentModeChange().subscribe((mode) => {
//mode contains the state after change
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
API method:
```jsx
const commentElement = Velt.getCommentElement();
let subscription = commentElement.onCommentModeChange().subscribe((mode) => {
//mode contains the state after change
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 4. Listen to Comment Selection changes
The `useCommentSelectionChangeHandler` hook can be used to subscribe to Comment selection changes.
```jsx
import React, { useEffect } from 'react';
import { useCommentSelectionChangeHandler } from '@veltdev/react';
function YourComponent() {
const commentSelectionChange = useCommentSelectionChangeHandler();
useEffect(() => {
console.log('commentSelectionChange', commentSelectionChange);
}, [commentSelectionChange]);
return (
<>
Selected Comment: {commentSelectionChange.annotation.id}
>
);
}
```
The `onCommentSelectionChange()` method can be used to listen Comment selection changes.
```jsx
const onCommentSelectionChange = (data) => {
console.log('onCommentSelectionChange', data);
}
onCommentSelectionChange(data)} />
```
Callback response schema:
```jsx
export class CommentSelectionChangeData {
selected!: boolean;
annotation!: CommentAnnotation;
}
```
**API Methods:**
```jsx
const commentElement = client.getCommentElement();
let subscription = commentElement.onCommentSelectionChange().subscribe((data) => {
console.log('onCommentSelectionChange: ', data);
});
```
**To unsubscribe from the subscription:**
```jsx
subscription?.unsubscribe()
```
The `onCommentSelectionChange()` method can be used to listen Comment selection changes.
```jsx
```
**Callback response schema:**
```jsx
export class CommentSelectionChangeData {
selected!: boolean;
annotation!: CommentAnnotation;
}
```
**API Methods:**
```jsx
const commentElement = Velt.getCommentElement();
let subscription = commentElement.onCommentSelectionChange().subscribe((data) => {
console.log('onCommentSelectionChange: ', data);
});
```
**To unsubscribe from the subscription:**
```jsx
subscription?.unsubscribe()
```
## 5. Listen to Comment Copy Link Event
You can add a callback method for when a comment link is copied. This can be useful for tracking when a comment link is copied or showing a confirmation toast to the user.
Using Hooks:
```jsx
const commentLink = useCommentCopyLinkHandler();
useEffect(() => {
console.log('commentLink', commentLink);
}, [commentLink]);
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.onCopyLink().subscribe((commentLink) => {
console.log(commentLink);
});
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.onCopyLink().subscribe((commentLink) => {
console.log(commentLink);
});
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.onCopyLink().subscribe((commentLink) => {
console.log(commentLink);
});
```
# General Controls
## 1. Allow comments only on certain elements
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/comment-area.gif)
Provide a list of element DOM IDs, class names, or query selectors where commenting should be allowed.
Comments will be disabled for all other elements. Note, this does not impact `Popover` mode.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.allowedElementIds['some-element'];
commentElement.allowedElementClassNames(["class-name-1", "class-name-2"]);
commentElement.allowedElementQuerySelectors(["#id1.class-name-1"]);
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.allowedElementIds['some-element'];
commentElement.allowedElementClassNames(["class-name-1", "class-name-2"]);
commentElement.allowedElementQuerySelectors(["#id1.class-name-1"]);
```
## 2. Disable comments on certain elements
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/comment-area.gif)
Disable certain elements from being commented on.
Add the `data-velt-comment-disabled` attribute to elements where you want to disable commenting.
```html
```
## 3. Enable or Disable the Comment Tool button
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-disabled.png)
Whether the Comment Tool button is Enabled.
`Default: true`
When the Comment Tool is disabled, it can not be used to leave comments. Other ways to leave comments, such as highlighting text, will also be disabled.
```jsx
{/* `true` to enable adding comments, `false` to disable adding comments */}
```
```js
```
Using API methods:
```js
const commentElement = client.getCommentElement();
// to enable adding comments
commentElement.enableCommentTool();
// to disable adding comments
commentElement.disableCommentTool();
```
```js
const commentElement = Velt.getCommentElement();
// to enable adding comments
commentElement.enableCommentTool();
// to disable adding comments
commentElement.disableCommentTool();
```
## 4. Ghost comments
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/ghost-comment-pin.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/ghost-comment-dialog-box.png)
Whether to show ghost comments on the DOM.
`Default: false`
Ghost comments are comments that were once linked to certain content on the DOM but that content is no longer available. If this is on, we display ghost comments in gray, close to where they were originally positioned on the DOM.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableGhostComments();
commentElement.disableGhostComments();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableGhostComments();
commentElement.disableGhostComments();
```
## 5. Ghost comments indicator
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/ghost-comment-sidebar.png)
Whether to show ghost comment labels in the comment sidebar.
`Default: true`
Ghost comments are always shown in the comments sidebar so that users can see the history of all comments. If this is on, we show a label on the comment in the sidebar indicating that the original content on which this comment was added is no longer available. This sets better expectations with the users.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableGhostCommentsIndicator();
commentElement.disableGhostCommentsIndicator();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableGhostCommentsIndicator();
commentElement.disableGhostCommentsIndicator();
```
## 6. Enabling Sidebar Button on Comment Dialog
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sidebar-button-on-comment-dialog.png)
Whether the Sidebar Button on Comment Dialogs show up.
`Default: true`
By Default, each Comment Dialog has a button at the bottom that will open the Comments Sidebar when clicked.
To disable it, you can set it to false:
```jsx
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableSidebarButtonOnCommentDialog()
commentElement.disableSidebarButtonOnCommentDialog()
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableSidebarButtonOnCommentDialog()
commentElement.disableSidebarButtonOnCommentDialog()
```
## 7. Subscribing to Sidebar Button Clicks on Comment Dialog
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/async-collaboration/comments/customize-behavior/images/customization/onSidebarOnCommentDialogClick.png)
You can use the `useCommentDialogSidebarClickHandler()` hook to add an event handler to the `onSidebarButtonOnCommentDialogClick` event in a cleaner way.
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
import { useCommentDialogSidebarClickHandler} from '@veltdev/react';
export default function YourDocument() {
const commentDialogSidebarClickEvent = useCommentDialogSidebarClickHandler();
useEffect(() => {
console.log('CommentDialog Sidebar button clicked');
}, [commentDialogSidebarClickEvent]);
return (
)
}
```
If you want to subscribe to clicks on the Sidebar Button at the bottom of the Comment Dialog you can do so by passing a callback to the `onSidebarButtonOnCommentDialogClick` event handler.
```jsx
yourMethod(event)} />
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
let subscription = commentElement.onSidebarButtonOnCommentDialogClick().subscribe((event) => yourMethod(event));
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
If you want to subscribe to clicks on the Sidebar Button at the bottom of the Comment Dialog you can do so by passing a callback to the `onSidebarButtonOnCommentDialogClick` event handler.
```jsx
```
API Methods:
```jsx
const commentElement = Velt.getCommentElement();
let subscription = commentElement.onSidebarButtonOnCommentDialogClick().subscribe((event) => yourMethod(event));
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 8. Comments iframe support
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/iframe-comment.png)
To support comments on top of an iframe, add the `data-velt-iframe-container="true"` attribute in the container element of the iframe.
```html
```
## 9. Enable Hotkeys
Whether Hotkeys are enabled or not. For now, the only hotkey supported is pressing `c` to enable `comment mode`.
`Default: false`
To enable hotkeys, set the `hotkey` attribute to `true`.
```html
```
```html
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
// to enable hotkeys
commentElement.enableHotkey();
// to disable hotkeys
commentElement.disableHotkey();
```
```jsx
const commentElement = Velt.getCommentElement();
// to enable hotkeys
commentElement.enableHotkey();
// to disable hotkeys
commentElement.disableHotkey();
```
## 10. Add Comment support on PDF Viewers
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/pdf-comments.png)
To support comments on top of a pdf viewer, add the `data-velt-pdf-viewer="true"` attribute in the container element of the pdf viewer.
```jsx
```
## 11. Filter out Comments at specific Locations
Use this to filter out Comments at a specific Location for certain Users.
```jsx
const locationIds = ['location1', 'location2']; // list of location ids
client.excludeLocationIds(locationIds);
```
To reset it, you can pass an empty array:
```jsx
client.excludeLocationIds([]);
```
## 12. Enable Comment DOM Change Detection
By default, we skip `Comment` `DOM Change Detection` when users are in `Comment Mode` to improve performance.
`Default: false`
However, you can turn `Comment` `DOM Change Detection` back on with the `changeDetectionInCommentMode` property.
This will make `Comment's` reposition themselves if the DOM happens to change while in `Comment Mode`.
```jsx
```
However, you can turn `Comment` `DOM Change Detection` back on with the `change-detection-in-comment-mode` property.
This will make `Comment's` reposition themselves if the DOM happens to change while in `Comment Mode`.
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
// To enable change detection when comment mode is on
commentElement.enableChangeDetectionInCommentMode();
// To disable change detection when comment mode is on
commentElement.disableChangeDetectionInCommentMode();
```
```jsx
const commentElement = Velt.getCommentElement();
// To enable change detection when comment mode is on
commentElement.enableChangeDetectionInCommentMode();
// To disable change detection when comment mode is on
commentElement.disableChangeDetectionInCommentMode();
```
## 13. Submit Comment on Enter Key Press
By default, pressing `enter` will add a new line and pressing `shift` + `enter` will submit a comment.
If you want to change this default behavior so that pressing `enter` will submit a comment, you can set the `enterKeyToSubmit` property to `true`.
```jsx
```
```jsx
// API methods
const commentElement = client.getCommentElement();
// To enable enter key to submit a comment
commentElement.enableEnterKeyToSubmit();
// To disable enter key to submit a comment
commentElement.disableEnterKeyToSubmit();
```
```jsx
```
```jsx
// API methods
const commentElement = Velt.getCommentElement();
// To enable enter key to submit a comment
commentElement.enableEnterKeyToSubmit();
// To disable enter key to submit a comment
commentElement.disableEnterKeyToSubmit();
```
## 14. Enable/Disable Area Comments
Area comments allows users to draw a rectangle and attach a comment to it.
Use this to enable or disable area comments.
`Default: true`
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/enable-disable-area-comments.png)
Using Props:
```jsx
```
Using API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableAreaComment();
commentElement.disableAreaComment();
```
Using Props:
```jsx
```
Using API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableAreaComment();
commentElement.disableAreaComment();
```
## 15. Enable/Disable Private Comment Mode
* Private comment mode enables admin users to add comments that are only visible to other admin users.
* Use this to enable or disable private comment mode.
`Default: false`
Using Props:
```jsx
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePrivateComments();
commentElement.disablePrivateComments();
```
Using Props:
```jsx
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePrivateCommentMode();
commentElement.disablePrivateCommentMode();
```
## 16. Enable/Disable deleting comments when backpsace key is pressed
* Use this to enable or disable deleting comments when backpsace key is pressed.
`Default: enabled`
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDeleteOnBackspace();
commentElement.disableDeleteOnBackspace();
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDeleteOnBackspace();
commentElement.disableDeleteOnBackspace();
```
## 17. Enable Delete Reply Confirmation
You can enable a confirmation dialog before deleting a reply in comment threads. This feature helps prevent accidental deletions and improves user experience.
Using Props:
```jsx
```
Using API:
```javascript
const commentElement = client.getCommentElement();
commentElement.enableDeleteReplyConfirmation();
commentElement.disableDeleteReplyConfirmation();
```
Using Props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.enableDeleteReplyConfirmation();
commentElement.disableDeleteReplyConfirmation();
```
## 18. Source ID for Duplicate Elements
* When you have multiple elements with the same DOM ID, you can use the `sourceId` attribute to control which element displays the comment dialog when adding a new comment.
* By default, comments appear on all matching elements.
* This is useful in cases where you have multiple instances of the same data component on a page and want the comment to appear on each instance, such as Popover comments on a table.
* You can randomly generate the `sourceId`. It just needs to be unique for each element in the current session.
```jsx
```
```html
```
## 19. Sort Inline Comments
* Change the default sorting order of Comments in the Inline Comments Section.
* Default: `none`
There are three options for sorting:
* `asc`: Show comment annotations in ascending order of when they were last updated
* `desc`: Show comment annotations in descending order of when they were last updated
* `none`: Show comment annotations in the sequence they were added
```jsx
```
```html
```
## 20. Comment Player Timeline Offset
* Allows comment bubbles to be positioned relative to both parent and child video clips by specifying an offset value.
* Default: `0`
```jsx
```
```html
```
# Modes
## 1. Freestyle mode
For a complete setup guide for Freestyle mode, [read here](/async-collaboration/comments/setup/freestyle).
Whether Freestyle Mode is enabled.
`Default: true`
```jsx
{/* Freestyle mode is enabled by default when you use VeltComments}
```
```jsx
```
## 2. Inbox mode
For a complete setup guide for Inbox mode, [read here](/async-collaboration/comments/setup/inbox).
Whether Inbox Mode is enabled.
`Default: false`
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableInboxMode();
commentElement.disableInboxMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableInboxMode();
commentElement.disableInboxMode();
```
## 3. Popover mode
For a complete setup guide for Popover mode, [read here](/async-collaboration/comments/setup/popover).
Whether Popover Mode is enabled.
`Default: false`
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePopoverMode();
commentElement.disablePopoverMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enablePopoverMode();
commentElement.disablePopoverMode();
```
## 4. Stream mode
For a complete setup guide for Stream mode, [read here](/async-collaboration/comments/setup/stream).
Whether Stream Mode is enabled.
`Default: false`
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableStreamMode();
commentElement.disableStreamMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableStreamMode();
commentElement.disableStreamMode();
```
## 5. Text mode
For a complete setup guide for Text mode, [read here](/async-collaboration/comments/setup/text).
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment.png)
Whether Text Mode is enabled.
`Default: true`
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableTextComments();
commentElement.disableTextComments();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableTextComments();
commentElement.disableTextComments();
```
## 6. Persistent comment mode
Whether Persistent comment mode is enabled.
`Default: false`
When Persistent comment mode is enabled, you can continue leave additional comments after finishing a comment.
When it is disabled, you will need to reclick the Comment Tool every time when you want to make a comment.
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePersistentCommentMode();
commentElement.disablePersistentCommentMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enablePersistentCommentMode();
commentElement.disablePersistentCommentMode();
```
## 7. Inline Comment Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment-opened.png)
Whether In-line comment mode is enabled.
When In-line comment mode is enabled, comments will appear under the text they are associated with in the DOM, instead of as a pop up window.
`Default: false`
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableInlineCommentMode();
commentElement.disableInlineCommentMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableInlineCommentMode();
commentElement.disableInlineCommentMode();
```
## 8. Multithread for Comments
* By default comments are single threaded.
* You can make it multithreaded by setting `multiThread` prop to `true`.
* If you had previously used a wireframe for the comment dialog, you will need to add the [multithread wireframe](/async-collaboration/comments/customize-ui/multithread-comment-dialog/overview).
* Default: `false`
```jsx
```
```html
```
## 9. Multithread for Inline Comments Section
* By default [inline comment section](/async-collaboration/comments/setup/inline-comments) is multithreaded.
* You can make it single threaded by setting `multiThread` prop to `false`.
* Default: `true`
```jsx
```
```html
```
# Multimedia Features
## 1. File attachments
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/file-attachment.png)
Whether file attachments are enabled.
`Default: true`
When this is on, users can attach image files to their comments. Users can download or delete an attachment. Users can attach multiple files at once.
Currently we support `.png`, `.jpg`, `.gif` (static & animated), `.svg` file types up to 15MB per file.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableAttachments();
commentElement.disableAttachments();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableAttachments();
commentElement.disableAttachments();
```
## 2. Set Recorder Media Options
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/recorder.png)
Set the Recorder media options within Comments: (audio, screen, video, all, or none).
`Default: "audio"`
To set the Recorder media options, pass in a comma separated string of the features you want to enable.
To enable only the **screen** option:
```jsx
```
```jsx
```
To enable the **video** and **screen** options:
```jsx
```
```jsx
```
To enable all options:
```jsx
```
```jsx
```
To disable all options:
```jsx
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.setAllowedRecordings("video"); // set video mode only
commentElement.setAllowedRecordings("audio,screen"); // set audio and screen mode only
commentElement.setAllowedRecordings("all"); // set all modes
commentElement.setAllowedRecordings("none"); // disable all modes
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setAllowedRecordings("video"); // set video mode only
commentElement.setAllowedRecordings("audio,screen"); // set audio and screen mode only
commentElement.setAllowedRecordings("all"); // set all modes
commentElement.setAllowedRecordings("none"); // disable all modes
```
## 3. Enable Reactions
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/reactions.png)
Whether emoji reactions are enabled.
`Default: true`
```jsx
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableReactions();
commentElement.disableReactions();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableReactions();
commentElement.disableReactions();
```
## 4. Disable Recording summaries
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/transcript-comment.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/transcript-summary.png)
Whether `Recording` summaries in `Comments` are enabled.
`Default: true`
```jsx
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
// to show recording summary
commentElement.enableRecordingSummary();
// to hide recording summary
commentElement.disableRecordingSummary();
```
```jsx
const commentElement = Velt.getCommentElement();
// to show recording summary
commentElement.enableRecordingSummary();
// to hide recording summary
commentElement.disableRecordingSummary();
```
## 5. Disable Recording countdown
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/recording-countdown.gif)
Whether the Recorder countdown is enabled.
`Default: true`
```jsx
```
```jsx
```
API Methods:
```jsx
// API method - comment element
const commentElement = client.getCommentElement();
// To enable recording countdown
commentElement.enableRecordingCountdown();
// To disable recording countdown
commentElement.disableRecordingCountdown();
// API method - recorder element
const recorderElement = client.getRecorderElement();
// To enable recording countdown
recorderElement.enableRecordingCountdown();
// To disable recording countdown
recorderElement.disableRecordingCountdown();
```
```jsx
// API method - comment element
const commentElement = Velt.getCommentElement();
// To enable recording countdown
commentElement.enableRecordingCountdown();
// To disable recording countdown
commentElement.disableRecordingCountdown();
// API method - recorder element
const recorderElement = Velt.getRecorderElement();
// To enable recording countdown
recorderElement.enableRecordingCountdown();
// To disable recording countdown
recorderElement.disableRecordingCountdown();
```
## 6. Custom Reactions
You can set custom reactions by passing a map that contains information about the reactions you want to add.
The map keys should be the reaction ID, and the map value should contain an object with either an `url`, `svg`, or `emoji` field to represent the reaction icon you want to use.
```jsx
const customReactions = {
"URL_EMOJI_ID": {
"url": "EMOJI_URL"
},
"SVG_EMOJI_ID": {
"svg": "EMOJI_SVG"
},
"TEXT_EMOJI_ID": {
"emoji": "🤣" // emoji as a text
}
};
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
const customReactions = {
"URL_EMOJI_ID": {
"url": "EMOJI_URL"
},
"SVG_EMOJI_ID": {
"svg": "EMOJI_SVG"
},
"TEXT_EMOJI_ID": {
"emoji": "🤣" // emoji as a text
}
}
commentElement.setCustomReactions(customReactions);
```
```jsx
const commentElement = Velt.getCommentElement();
const customReactions = {
"URL_EMOJI_ID": {
"url": "EMOJI_URL"
},
"SVG_EMOJI_ID": {
"svg": "EMOJI_SVG"
},
"TEXT_EMOJI_ID": {
"emoji": "🤣" // emoji as a text
}
}
commentElement.setCustomReactions(customReactions);
```
## 7. enableRecordingTranscription
Controls whether to enable AI transcription for recordings.
Default: `enabled`
**Using Props:**
```jsx
```
**Using API Methods:**
```javascript
// Using comment element
const commentElement = client.getCommentElement();
commentElement.enableRecordingTranscription();
commentElement.disableRecordingTranscription();
// Or using recorder element
const recorderElement = client.getRecorderElement();
recorderElement.enableRecordingTranscription();
recorderElement.disableRecordingTranscription();
```
**Using Props:**
```html
```
**Using API Methods:**
```javascript
// Using comment element
const commentElement = Velt.getCommentElement();
commentElement.enableRecordingTranscription();
commentElement.disableRecordingTranscription();
// Or using recorder element
const recorderElement = Velt.getRecorderElement();
recorderElement.enableRecordingTranscription();
recorderElement.disableRecordingTranscription();
```
# Retrieve Comments
## 1. To retrieve comments in the frontend
To retreive all comments in the frontend, use the `useCommentAnnotations()` hook. The hook will return an array that contains all current comments.
Whenever there is a change to the comments structure, the hook return value will be updated to the latest change.
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
import { useCommentAnnotations } from '@veltdev/react';
export default function YourDocument() {
let commentAnnotations = useCommentAnnotations()
return (
{commentAnnotations.map(x =>
{x.annotationId}
)}
)
}
```
By default, `useCommentAnnotations()` will return data for the current `documentId` and `location`.
If you pass in a `documentId` as an argument, it will return all comments in the given `documentId`.
If you pass in a `documentId` as the first argument and a `location` object as the second argument, it will return all comments in the given `documentId` and `location`.
```jsx
import { useCommentAnnotations } from '@veltdev/react';
export default function YourDocument() {
let commentAnnotations = useCommentAnnotations('my-document-id', { id:'my-location-id',locationName:"MyLocationName"})
return (
{commentAnnotations.map(x =>
{x.annotationId}
)}
)
}
```
To retrieve all comments in the frontend, use the `commentElement.getAllCommentAnnotations()` method and subscribe with a callback function.
Whenever there is a change to the comments structure, the callback function will be triggered and will contain all current comments in an array.
```js
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations().subscribe((comments) => {
// Do something with comments
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
By default, `getAllCommentAnnotations` will return data for the current `documentId` and `location`.
If you pass in a `documentId` to `getAllCommentAnnotations` as an argument, it will return all comments in the given `documentId`.
If you pass in a `documentId` as the first argument and a `location` object as the second argument, it will return all comments in the given `documentId` and `location`.
```js
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations('my-document-id', { id:'my-location-id',locationName:"MainVideoPlayer",timestamp:120}).subscribe((comments) => {
// Get comments with my-document-id and my-locaiton-id
});
}
```
To retrieve all comments in the frontend, call getCommentElement() on the Velt client and then `subscribe()` to `getAllCommentAnnotations()` and pass in a callback function.
Whenever there is a change to the comments structure, the callback function will be triggered and will contain all current comments in an array.
```js
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations().subscribe((comments) => {
// Do something with comments
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
By default, `getAllCommentAnnotations` will return data for the current `documentId` and `location`.
If you pass in a `documentId` to `getAllCommentAnnotations` as an argument, it will return all comments in the given `documentId`.
If you pass in a `documentId` as the first argument and a `location` object as the second argument, it will return all comments in the given `documentId` and `location`.
```js
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations('my-document-id', { id:'my-location-id',locationName:"MainVideoPlayer",timestamp:120}).subscribe((comments) => {
// Get comments with my-document-id and my-locaiton-id
});
}
```
## 2. To retrieve comments in the backend
For this you should use our Webhook service. Let's say you want to be notified whenever a comment is added or updated, you can provide us an endpoint and we will send the comment data to that end point as and when there is an update. You can then process it further. Note that you cannot retrieve historical comments using this.
You can enable and configure webhook in your Velt Console as shown below. After you enable this, you need to provide an endpoint url. We will make a post request to that endpoint to send the comment data.
To read more about how to configure webhooks, check out the [webhooks documentation](/webhooks/overview).
## 3. Get Count of Unread CommentAnnotations On Current Document
You can get the number of `CommentAnnotations` that have at least 1 unread `Comment` on the current `Document` by using the `useUnreadCommentAnnotationCountOnCurrentDocument()` hook:
```jsx
const count = useUnreadCommentAnnotationCountOnCurrentDocument();
useEffect(() => {
console.log(count, 'countObj')
}, [count])
```
You can get the number of `CommentAnnotations` that have at least 1 unread `Comment` on the current `Document` by subscribing to the `getUnreadCommentAnnotationCountOnCurrentDocument()` method:
```jsx
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getUnreadCommentAnnotationCountOnCurrentDocument().subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can get the number of `CommentAnnotations` that have at least 1 unread `Comment` on the current `Document` by subscribing to the `getUnreadCommentAnnotationCountOnCurrentDocument()` method:
```jsx
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getUnreadCommentAnnotationCountOnCurrentDocument().subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 4. Get Count Of Unread Comments On Current Document
You can get the number of unread `Comments` on the current `Document` by using the `useUnreadCommentCountOnCurrentDocument()` hook:
```jsx
const count = useUnreadCommentCountOnCurrentDocument();
useEffect(() => {
console.log(count, 'countObj')
}, [count])
```
You can get the number of unread `Comments` on the current `Document` by using the `getUnreadCommentCountOnCurrentDocument()` method:
```jsx
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getUnreadCommentCountOnCurrentDocument().subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can get the number of unread `Comments` on the current `Document` by using the `getUnreadCommentCountOnCurrentDocument()` method:
```jsx
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getUnreadCommentCountOnCurrentDocument().subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 5. Get Count Of Unread CommentAnnotations By Location Id
You can get the number of `CommentAnnotations` with at least 1 unread `Comment` by `Location Id` by using the `useUnreadCommentAnnotationCountByLocationId()` hook:
```jsx
const count = useUnreadCommentAnnotationCountByLocationId(locationId);
useEffect(() => {
console.log(count, 'countObj')
}, [count])
```
You can get the number of `CommentAnnotations` with at least 1 unread `Comment` by `Location Id` by using the `getUnreadCommentAnnotationCountByLocationId` method:
```jsx
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getUnreadCommentAnnotationCountByLocationId(locationId).subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can get the number of `CommentAnnotations` with at least 1 unread `Comment` by `Location Id` by using the `getUnreadCommentAnnotationCountByLocationId` method:
```jsx
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getUnreadCommentAnnotationCountByLocationId(locationId).subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 6. Get Count Of Unread Comments By Location Id
You can get the number of unread `Comments` by `Location Id` by using the `useUnreadCommentCountByLocationId()` hook:
```jsx
const count = useUnreadCommentCountByLocationId(locationId);
useEffect(() => {
console.log(count, 'countObj')
}, [count])
```
You can get the number of unread `Comments` by `Location Id` by using the `getUnreadCommentCountByLocationId()` method:
```jsx
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getUnreadCommentCountByLocationId(locationId).subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can get the number of unread `Comments` by `Location Id` by using the `getUnreadCommentCountByLocationId()` method:
```jsx
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getUnreadCommentCountByLocationId(locationId).subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 7. Get Count Of Unread Comments By Annotation Id
You can get the number of unread `Comments` by annotation id by using the `useUnreadCommentCountByAnnotationId()` hook:
```jsx
const count = useUnreadCommentCountByAnnotationId(annotationId);
useEffect(() => {
console.log(count, 'countObj')
}, [count])
```
You can get the number of unread `Comments` by annotation id by subscribing to the `getUnreadCommentCountByAnnotationId()` method:
```jsx
if (client) {
const commentElement = client.getCommentElement();
let subscription = commentElement.getUnreadCommentCountByAnnotationId(annotationId).subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can get the number of unread `Comments` by annotation id by subscribing to the `getUnreadCommentCountByAnnotationId()` method:
```jsx
if (Velt) {
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getUnreadCommentCountByAnnotationId(annotationId).subscribe((countObj) => {
console.log(countObj);
});
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 8. Get Selected Comments
* Get the currently selected comment annotations.
* This returns an array of [`CommentAnnotation`](/api-reference/models/CommentAnnotation) objects.
```jsx
const commentElement = client.getCommentElement();
const subscription = commentElement.getSelectedComments().subscribe((selectedComments) => {
console.log('Selected comments:', selectedComments);
});
```
Unsubscribe from the subscription when you're done:
```jsx
subscription?.unsubscribe()
```
```js
const commentElement = Velt.getCommentElement();
const subscription = commentElement.getSelectedComments().subscribe((selectedComments) => {
console.log('Selected comments:', selectedComments);
});
```
Unsubscribe from the subscription when you're done:
```js
subscription?.unsubscribe()
```
## 9. Get Comment Annotation by ID
* Retrieve a specific comment annotation by its ID.
* By default, it will return the comment annotation for the current `documentId`.
* If you pass in a `documentId`, it will return the comment annotation for the given `documentId`.
Using Hooks:
```jsx
const annotation = useCommentAnnotationById({
annotationId: '-O6W3jD0Lz3rxuDuqQFx', // AnnotationID
documentId: 'document-id' // DocumentId (Optional)
});
useEffect(() => {
console.log('annotation', annotation);
}, [annotation]);
```
Using API:
```javascript
const commentElement = client.getCommentElement();
let subscription = commentElement.getCommentAnnotationById({
annotationId: '-O6W3jD0Lz3rxuDuqQFx', // AnnotationID
documentId: 'document-id' // DocumentId (Optional)
}).subscribe((annotation) => {
console.log('annotation', annotation);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getCommentAnnotationById({
annotationId: '-O6W3jD0Lz3rxuDuqQFx', // AnnotationID
documentId: 'document-id' // DocumentId (Optional)
}).subscribe((annotation) => {
console.log('annotation', annotation);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
# Task Management Controls
## 1. AI auto categorization of comments
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/ai-categories.png)
Whether AI auto-categorization of comments is enabled.
`Default: false`
We use AI to analyze your comment content and auto-categorize it so users can filter comments easily. You can provide a list of custom categories that we should use to categorize the comments (shown below).
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableAutoCategorize();
commentElement.disableAutoCategorize();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableAutoCategorize();
commentElement.disableAutoCategorize();
```
## 2. Set Custom Categories
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/custom-categories.png)
Pass custom categories in the `customCategory` prop.
`Default categories: Question, Feedback, Bug, Other.`
With custom categories, you can replace the default categories with your own values.
These categories are used in the `Comments Sidebar` to filter comments by category. The AI `autoCategorize` feature uses the list of categories to determine the closest category to choose from.
The input format to the `customCategory` prop should be an array of objects with an `id`, `name`, and `color`.
The `color` property is used to set the category pill background color.
```js
```
Pass custom categories in the `custom-category`.
`Default categories: Question, Feedback, Bug, Other.`
With custom categories, you can replace the default categories with your own values.
These categories are used in the `Comments Sidebar` to filter comments by category. The AI `auto-categorize` uses the list of categories to determine the closest category to choose from.
The input format to the `custom-category` should be an array of objects with an `id`, `name`, and `color`.
The `color` property is used to set the category pill background color.
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.setCustomCategory([
{
"id": "bug",
"name": "Bug",
"color": "red"
}
])
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setCustomCategory([
{
"id": "bug",
"name": "Bug",
"color": "red"
}
])
```
Make sure to have at least 2 categories set.
## 3. Priority
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/priority-default.png)
Whether to enable setting priority on comments.
`Default: false`
When this is on, users can assign a priority to each comment & filter comment by priority in the sidebar. You can customize the list of priority options as shown later on this page in the Set Custom Priorities section.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePriority();
commentElement.disablePriority();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enablePriority();
commentElement.disablePriority();
```
## 4. Set Custom Priorities
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/custom-priority.png)
Pass custom priorities in the `customPriority` prop.
`Default priorities: P0, P1, P2`
With custom priorities, you can replace the default priorities with your own values. These priorities are also used in the comment sidebar to filter comments by priority.
This will work if you have enabled the priority feature.
The `color` property is used to set the priority pill background color.
The `lightColor` property sets the background color of the filter.
```js
```
Pass custom priorities in the `custom-priority`.
`Default priorities: P0, P1, P2`
With custom priorities, you can replace the default priorities with your own values. These priorities are also used in the comment sidebar to filter comments by priority.
This will work if you have enabled the priority feature.
The `color` property is used to set the priority pill background color.
The `lightColor` property sets the background color of the filter.
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.setCustomPriority([
{
"id": "low",
"name": "Low",
"color": "red",
"lightColor": "pink",
},
])
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setCustomPriority([
{
"id": "low",
"name": "Low",
"color": "red",
"lightColor": "pink",
},
])
```
Make sure to have at least 2 categories set.
## 5. Status
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/status-default.png)
Whether to enable the default status dropdown & filters.
`Default: true`
When this is on, users can assign a status to each comment & filter comment by status in the sidebar. You can customize the list of status options as shown below on this page.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableStatus();
commentElement.disableStatus();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableStatus();
commentElement.disableStatus();
```
## 6. Set Custom Statuses
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/custom-status.png)
Set custom statuses in the `customStatus` prop.
`Default statuses: Open, In Progress, Resolved`
With custom statuses, you can replace the default statuses with your own values. These statuses are also used in the comment sidebar to filter comments by status.
Setting the Status type using the `type` property:
* `default`: This will be the default status assigned to each comment.
* `ongoing`: This is treated as an intermediary status, you can add as many statuses with type ongoing as you want.
* `terminal`: This represents a status that is completed. Comments with this status type are no longer shown in the DOM.
Setting the Status Icon using the `svg` property:
* You can pass in a serialized SVG. We automatically parse and colorize SVGs. If instead you pass in an icon URL, you will have to colorize each icon yourself to match the status color.
```js
" // Pass in a serialized SVG
}
]}/>
```
Set custom statuses in the `custom-status` prop.
`Default statuses: Open, In Progress, Resolved`
With custom statuses, you can replace the default statuses with your own values. These statuses are also used in the comment sidebar to filter comments by status.
Setting the Status type using the `type` property:
* `default`: This will be the default status assigned to each comment.
* `ongoing`: This is treated as an intermediary status, you can add as many statuses with type ongoing as you want.
* `terminal`: This represents a status that is completed. Comments with this status type are no longer shown in the DOM.
Setting the Status Icon using the `svg` property:
* You can pass in a serialized SVG. We automatically parse and colorize SVGs. If instead you pass in an icon URL, you will have to colorize each icon yourself to match the status color.
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.setCustomStatus([
{
"id": "open",
"name": "Open",
"color": "white",
"lightColor":"green",
"type": "default",
"svg": "" // Pass in a serialized SVG
}
])
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setCustomStatus([
{
"id": "open",
"name": "Open",
"color": "white",
"lightColor":"green",
"type": "default",
"svg": "" // Pass in a serialized SVG
}
])
```
Make sure to have at least 2 categories set.
## 7. Resolve button
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/resolve-button.png)
Whether to show resolve button on comments.
`Default: true`
This adds a tick mark button on the top right corner of the comment dialog. Clicking on this button will mark the comment as resolved.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableResolveButton();
commentElement.disableResolveButton();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableResolveButton();
commentElement.disableResolveButton();
```
## 8. Moderator mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/moderator-mode.png)
Whether comments require moderator approval.
`Default: false`
By default, when a user adds a comment it is visible to all authenticated users on the same `document`. Moderator mode makes visibility of all comments private to only `admin` users and the comment author. Admin users will see an approve button on the comment dialog. Once approved the comment will be visible to all users who can access the `document`.
You can set some users as `admin` by setting the `isAdmin` property in the User object, during the `identify()` call.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableModeratorMode();
commentElement.disableModeratorMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableModeratorMode();
commentElement.disableModeratorMode();
```
## 9. Ability to accept or reject comments
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/accept-reject.png)
Whether to enable suggestion mode to accept or reject comments.
`Default: false`
To accept comments, set the `suggestionMode` attribute to `true`.
```js
```
To accept comments, set the `suggestion-mode` attribute to `true`.
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableSuggestionMode();
commentElement.disableSuggestionMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableSuggestionMode();
commentElement.disableSuggestionMode();
```
## 10. Listen to `onCommentAccept` and `onCommentReject` events
To listen to comments that are Accepted or Rejected in Suggestion mode, pass callbacks to the `onCommentAccept` and onCommentReject\` event handlers.
```js
yourMethod(event)}/>
yourMethod(event)}/>
```
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onCommentAccept', (event) => {
console.log('*** onCommentAccept ***');
console.log(event.detail);
});
veltCommentsTag?.addEventListener('onCommentReject', (event) => {
console.log('*** onCommentReject ***');
console.log(event.detail);
});
```
Response:
```js
{
"annotationId": "ANNOTATION_ID",
"actionUser": {
// user object
},
"actionType": "accept", // accept or reject
"annotation": {
// raw annotation object
},
"replaceContentHtml": "HTML_FORMATTED_TEXT_TO_REPLACE", // optional
"replaceContentText": "PLAIN_TEXT_TO_REPLACE", // optional
}
```
## 11. Sign In button
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/sign-in-button.png)
Whether to enable Sign In button on comment dialog when user is anonymous or signed out.
`Default: false`
This allows anonymous or signed out users to still read the comments but encourages them to sign in if they want to respond to the comments.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableSignInButton();
commentElement.disableSignInButton();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableSignInButton();
commentElement.disableSignInButton();
```
## 12. onSignIn
When the user clicks on the sign in button, we will emit an `onSignIn` event that you can handle with your own sign in method.
No data is passed with the event.
```js
yourSignInMethod()} />
```
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onSignIn', (event) => {
console.log('*** onCommentSignIn ***');
console.log(event.detail);
});
```
## 13. Enable/Disable private comment mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/private-comment.png)
Whether private comment mode is enabled.
`Default: false`
Admin users can enable `private comment mode`. Once `private comment mode` is enabled, all the newly added comment annotations by the admin user will be private comments by default, meaning only other admins can see it.
To enable private comment mode, set the `privateCommentMode` attribute to `true`:
```html
```
To enable private comment mode, set the `private-comment-mode` attribute to `true`:
```html
```
API Methods:
API to enable/disable private comment mode:
```jsx
const commentElement = client.getCommentElement();
// To enable private comment mode
commentElement.enablePrivateCommentMode();
// To disable private comment mode
commentElement.disablePrivateCommentMode();
```
```jsx
const commentElement = Velt.getCommentElement();
// To enable private comment mode
commentElement.enablePrivateCommentMode();
// To disable private comment mode
commentElement.disablePrivateCommentMode();
```
## 14. Enable Minimap
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/minimap.png)
* The minimap shows a bar on the edge of the screen with indicators that show where comments exist.
* Use this to enable/disable the minimap. By default it's disabled.
* It can be positioned `left` or `right`. By default, it's positioned on the right side of the screen.
**Option a. Enable using config:**
```jsx
```
**API Method:**
```jsx
const commentElement = client.getCommentElement();
commentElement.enableMinimap();
commentElement.disableMinimap();
```
```jsx
```
**API Method:**
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableMinimap();
commentElement.disableMinimap();
```
**Option b. Enable using Minimap Component:**
This offers greater flexibility to customize and position the minimap.
```jsx
{/* scrollable content */}
```
```jsx
```
## 15. Restrict Resolve Action to Admin Users Only
* Restrict the resolve action to admin users and the comment author only.
**Using props:**
```jsx
```
**Using API:**
```javascript
const commentElement = client.getCommentElement();
// To enable resolve status access admin only
commentElement.enableResolveStatusAccessAdminOnly();
// To disable resolve status access admin only
commentElement.disableResolveStatusAccessAdminOnly();
```
**Using props:**
```html
```
**Using API:**
```javascript
const commentElement = Velt.getCommentElement();
// To enable resolve status access admin only
commentElement.enableResolveStatusAccessAdminOnly();
// To disable resolve status access admin only
commentElement.disableResolveStatusAccessAdminOnly();
```
## 16. Toggle "Seen By" Feature
Control whether the "Seen By" feature is enabled for comments. When enabled, it shows which users have seen each comment.
Default: `true`
**Using Props:**
```jsx
```
**Using API:**
```jsx
const commentElement = client.getCommentElement();
commentElement.enableSeenByUsers();
commentElement.disableSeenByUsers();
```
**Using Props:**
```html
```
**Using API:**
```javascript
const commentElement = Velt.getCommentElement();
commentElement.enableSeenByUsers();
commentElement.disableSeenByUsers();
```
# UI/UX Controls
## 1. Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/dark-light-mode.png)
Whether dark mode is enabled.
`Default: false`
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
## 2. Mobile Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/mobile-mode.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/mobile-mode-composer.png)
Whether mobile mode is enabled.
When mobile mode is enabled and the screen width is small enough, comment windows will appear fixed to the bottom of the screen and full width instead of the usual popup window.
`Default: false`
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableMobileMode();
commentElement.disableMobileMode();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableMobileMode();
commentElement.disableMobileMode();
```
## 3. Enable / Disable Scroll to Comment on Click
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/scroll-on-click.gif)
Whether, users will be scrolled to the location of a `Comment` when it is clicked.
`Default: true`
By default, users will be redirected to a `Comment` if the comment id is provided in the url. But sometimes this experience is annoying, so we have provided a way to disable the option to automatically scroll users to the location of the `Comment`.
To disable the feature, set `scrollToComment` to `false`.
```html
```
To disable the feature, set `scroll-to-comment` to `false`.
```html
```
API methods:
```jsx
const commentElement = client.getCommentElement();
// To enable scroll to component
commentElement.enablescrollToComment();
// To disable scroll to component
commentElement.disablescrollToComment();
```
```jsx
const commentElement = Velt.getCommentElement();
// To enable scroll to component
commentElement.enablescrollToComment();
// To disable scroll to component
commentElement.disablescrollToComment();
```
## 4. Enable pin highlighter outline
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/pin-highlighter-outline.png)
Wheter the pin highlighter outline is enabled or not.
`Default: true`
```jsx
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableCommentPinHighlighter(); // to enable comment pin highlight
commentElement.disableCommentPinHighlighter(); // to disable comment pin highlight
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableCommentPinHighlighter(); // to enable comment pin highlight
commentElement.disableCommentPinHighlighter(); // to disable comment pin highlight
```
## 5. Disable ShadowDOM
Whether the ShadowDOM is enabled or not on certain components.
`Default: enabled`
By default, a ShadowDOM is used with certain components to ensure that your application's CSS does not interfere with the styling of the SDK components.
If you want your application's CSS to affect the styling of the SDK components, you can disable the ShadowDOM.
To disable ShadowDOM on all Comment features:
```jsx
```
```jsx
```
To disable ShadowDOM on individual features:
```jsx
```
```jsx
```
API methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
## 6. Expand Composer to show Attachments Bar
By default, the `Composer` in the `Comments Dialog` only shows the text input box and does not show the actions bar until the `Composer` is clicked on or the user starts typing.
You can modify this behavior by setting the `Composer Mode` prop to `"expanded"`. This will make the actions bar always visible.
To keep the default behavior you can set the property to `"default"`.
`Default: "default"`
```jsx
```
```jsx
```
## 7. Set Custom Cursor
You can set custom mouse cursor when the comment mode is on.
The custom cursor image must be **32 x 32 pixels**.
```jsx
```
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.setPinCursorImage(BASE64_IMAGE_STRING);
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setPinCursorImage(BASE64_IMAGE_STRING);
```
## 8. Show/Hide comments on DOM
Whether comments are shown on the DOM.
`Default: true`
By default, all the comments will be visible on DOM whenever we are able to detect to elements for that. But users can hide it from DOM if required.
There are 2 ways to show/hide comments on DOM:
Configuring attributes on the React Component:
```js
{/* `true` to show comments, `false` to hide comments */}
```
```js
```
Using API methods:
```js
const commentElement = client.getCommentElement();
// to show comments on DOM
commentElement.showCommentsOnDom();
// to hide comments on DOM
commentElement.hideCommentsOnDom();
```
```js
const commentElement = Velt.getCommentElement();
// to show comments on DOM
commentElement.showCommentsOnDom();
// to hide comments on DOM
commentElement.hideCommentsOnDom();
```
## 9. Comment dialog on hover
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/show-dialog-on-hover.gif)
Whether the comment dialog shows on hover over the comment pin or the target element.
`Default: true`
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDialogOnHover();
commentElement.disableDialogOnHover();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDialogOnHover();
commentElement.disableDialogOnHover();
```
## 10. Comment dialog on target element click
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/hover-cell-comment.gif)
Whether the comment dialog opens when target element is clicked. This is relevant only for Popover mode.
`Default: true`
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDialogOnTargetElementClick();
commentElement.disableDialogOnTargetElementClick();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDialogOnTargetElementClick();
commentElement.disableDialogOnTargetElementClick();
```
## 11. Floating comment dialog
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/floating-comment-dialog.gif)
Whether floating comment dialog is shown next to comment pin on hover or click.
`Default: true`
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableFloatingCommentDialog();
commentElement.disableFloatingCommentDialog();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableFloatingCommentDialog();
commentElement.disableFloatingCommentDialog();
```
## 12. Enable Popover mode triangle
Whether the popover triangle appears when Popover Mode is enabled.
`Default: true`
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePopoverTriangleComponent();
commentElement.disablePopoverTriangleComponent();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enablePopoverTriangleComponent();
commentElement.disablePopoverTriangleComponent();
```
## 13. Show resolved comments on DOM
Whether to show resolved comments on the DOM.
`Default: false`
```jsx
```
```html
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
// To show resolved comments on dom
commentElement.showResolvedCommentsOnDom();
// To hide resolved comments on dom
commentElement.hideResolvedCommentsOnDom();
```
```jsx
const commentElement = Velt.getCommentElement();
// To show resolved comments on dom
commentElement.showResolvedCommentsOnDom();
// To hide resolved comments on dom
commentElement.hideResolvedCommentsOnDom();
```
## 14. Comment index
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/index-indicator.png)
Whether comment index is enabled.
`Default: false`
This appears in the comment sidebar and on the comment pins. When this is on, we show a small icon indicating the comment index in the order of creation date. This enables users to find and navigate to the desired comment quickly.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableCommentIndex();
commentElement.disableCommentIndex();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableCommentIndex();
commentElement.disableCommentIndex();
```
## 15. Device Type Info
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/device-info.png)
Whether device type indicator is enabled.
`Default: false`
When this is on, we show additional information in the `Comment Thread` indicating which device the comment was created on. This is useful especially for design tools, where additional context is needed for debugging issues.
```js
```
```js
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDeviceInfo();
commentElement.disableDeviceInfo();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDeviceInfo();
commentElement.disableDeviceInfo();
```
## 16. Device Indicator on Comment Pins
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/device-info-pin.png)
Whether the device type indicator on `Comment Pins` is enabled.
`Default: false`
When this is on, we show a small device type icon on the `Comment Pin` indicating which device the comment was created on. This is useful especially for design tools, where additional context is needed for debugging issues.
```js
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDeviceIndicatorOnCommentPins();
commentElement.disableDeviceIndicatorOnCommentPins();
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDeviceIndicatorOnCommentPins();
commentElement.disableDeviceIndicatorOnCommentPins();
```
## 17. Enable Verbose Mode for Unread Comments
Whether `verbose` mode is enabled for unread `Comments`.
`Default: 'minimal'`
Unread `Comments` can be in `minimal` mode or `verbose` mode.
In `minimal` mode, a small red dot indicator appears for unread `Comments`.
In `verbose` mode, a larger badge with the text "UNREAD" will appear for unread `Comments`.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/unread-compare.png)
```jsx
```
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.setUnreadIndicatorMode("verbose"); // use badge with text UNREAD
commentElement.setUnreadIndicatorMode("minimal"); // use small red dot indicator
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setUnreadIndicatorMode("verbose"); // use badge with text UNREAD
commentElement.setUnreadIndicatorMode("minimal"); // use small red dot indicator
```
## 18. Show Unread Comments Count on Comment Bubble Component
Whether to show unread or total comment replies count on Comment Bubble Component.
`Default: 'total'`
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-bubble.png)
`commentCountType`: This prop allows you to decide which count to display.
* `total`: Shows the total number of replies. (default)
* `unread`: Shows the number of unread replies.
```jsx
```
`comment-count-type`: This prop allows you to decide which count to display.
* `total`: Shows the total number of replies. (default)
* `unread`: Shows the number of unread replies.
```jsx
```
## 19. Enable Comment Bubble on Comment Pin
Show a Comment Bubble when user hovers or clicks on the Comment Pin vs showing the Comment Dialog.
The comment dialog will open only on clicking the comment bubble.
`Default: 'false'`
**Using Props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = useCommentUtils();
commentElement.enableBubbleOnPin();
commentElement.disableBubbleOnPin();
```
**Using Props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = client.getCommentElement();
commentElement.enableBubbleOnPin();
commentElement.disableBubbleOnPin();
```
**Using Props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableBubbleOnPin();
commentElement.disableBubbleOnPin();
```
## 20. Enable showing Comment Bubble when hovering on the Comment Pin
Show a Comment Bubble when user hovers on the Comment Pin vs clicks on it.
`Default: 'true'`
**Using Props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = useCommentUtils();
// To enable/disable showing bubble on pin
commentElement.enableBubbleOnPin();
commentElement.disableBubbleOnPin();
// To enable/disable showing bubble on hover
commentElement.enableBubbleOnPinHover();
commentElement.disableBubbleOnPinHover();
```
**Using Props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = client.getCommentElement();
// To enable/disable showing bubble on pin
commentElement.enableBubbleOnPin();
commentElement.disableBubbleOnPin();
// To enable/disable showing bubble on hover
commentElement.enableBubbleOnPinHover();
commentElement.disableBubbleOnPinHover();
```
**Using Props:**
```jsx
```
**Using API Method:**
```jsx
const commentElement = Velt.getCommentElement();
// To enable/disable showing bubble on pin
commentElement.enableBubbleOnPin();
commentElement.disableBubbleOnPin();
// To enable/disable showing bubble on hover
commentElement.enableBubbleOnPinHover();
commentElement.disableBubbleOnPinHover();
```
## 21. Control Collapsed Comments
You can control whether comments inside the annotation should be collapsed.
`Default: true`
Using Props:
```jsx
```
Using API:
```javascript
const commentElement = client.getCommentElement();
// To enable collapsed comments
commentElement.enableCollapsedComments();
// To disable collapsed comments
commentElement.disableCollapsedComments();
```
Using Props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
// To enable collapsed comments
commentElement.enableCollapsedComments();
// To disable collapsed comments
commentElement.disableCollapsedComments();
```
## 22. Enable/Disable Short User Names
You can control whether long user names should be shortened. For long names, this will first create an initial of the second name and if the name is still long, it will truncate it with ellipses.
Default: `true`
**Using Props:**
```jsx
```
**Using API:**
```javascript
const commentElement = client.getCommentElement();
commentElement.enableShortUserName();
commentElement.disableShortUserName();
```
**Using Props:**
```html
```
**Using API:**
```javascript
const commentElement = Velt.getCommentElement();
commentElement.enableShortUserName();
commentElement.disableShortUserName();
```
# Overview
This button shows the comment count and the author's avatar. This is used in Popover comments feature.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default comment bubble component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-bubble-light.png)
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### Example
```jsx
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-bubble-1.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
Below are examples to enable dark mode for comment bubble component.
### Example
```js
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```HTML
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# null
The subcomponent on the Comment Bubble that shows the User avatar
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-bubble-sub-2.png)
This subcomponent does not support children
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Comment Bubble that shows the number of Comments made on a thread.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-bubble-sub-1.png)
This subcomponent does not support children
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The interface that appears when you try to read an existing Comment or make a new Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default comment dialog component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-2.png)
```jsx React / Next.js
{/* Ghost Banner - Only shown for ghost comments */}
{/* Private Banner - Only shown for private comments */}
{/* Assignee Banner - Only shown for assigned comments */}
{/* Header */}
{/* Private Badge - Only shown for private comments */}
{/* Body */}
{/* Composer */}
{/* All Comments */}
{/* Approve - Only when moderator mode is on */}
{/* Sign In - Only when user is anonymous and sign in button is enabled */}
{/* Upgrade - Not visible by default */}
{/* Suggestion Action - Only when suggestion mode is on */}
```
```HTML HTML
```
# Variants
## Pre-defined Variants
The Comment Dialog has 2 pre-defined variants:
* `dialog`: this will customize the Comment Dialog only within Pin, Area, and Text Comments
* `sidebar`: this will customize the Comment Dialog only within Sidebar comments
To use them, set the `variant` name in the wireframe template equal to one of the pre-defined variants. You do not need to add it to the actual Velt component.
```jsx React / Next.js
{/* This pre-defined variant will change the appearance of the Comment Dialog within Pin, Area, and Text comments only */}
...
{/* This pre-defined variant will change the appearance of the Comment Dialog within the Sidebar only */}
...
{/* If you dont use any variants, then customization will be applied to the Comment Dialog globally */}
...
```
```jsx HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
```
### Example
```
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/dark-light-mode.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
Below are the examples to enable Dark Mode for comments dialog:
### Example
```js
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```js
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# null
The subcomponent of the Comment Dialog that is used to show the All Comments button that opens the Comments Sidebar
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any of the components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-8.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that is used approve Comments in Moderator Mode
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-9.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that is used assign a Comment as a task
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-3.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.AssigneeBanner.UserAvatar`
* `VeltCommentDialogWireframe.AssigneeBanner.UserName`
* `velt-comment-dialog-assignee-banner-user-avatar-wireframe`
* `velt-comment-dialog-assignee-banner-user-name-wireframe`
```jsx "React / Next.js"
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the tooltip that appears when you hover over an Autocomplete Chip.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/autocomplete-chip-tooltip.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the dropdown list that appears when you press an Autocomplete hotkey.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-user-selector-dropdown-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-user-selector-dropdown-2.png)
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The subcomponent of the Comment Dialog that is used to show all Comment Replies as well as the Comment Composer.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-6.png)
```jsx React / Next.js
```
```HTML HTML
...
...
```
# ThreadCard
The subcomponent of the Body of the Comment Dialog that is used to show attachments added to a Comment reply.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/threadcard-attachments.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.ThreadCard.Avatar`
* `VeltCommentDialogWireframe.ThreadCard.DeviceType`
* `VeltCommentDialogWireframe.ThreadCard.Name`
* `VeltCommentDialogWireframe.ThreadCard.Time`
* `VeltCommentDialogWireframe.ThreadCard.Message`
* `VeltCommentDialogWireframe.ThreadCard.ReactionTool`
* `VeltCommentDialogWireframe.ThreadCard.Reactions`
* `VeltCommentDialogWireframe.ThreadCard.Recordings`
## Subcomponents that don't support children
* `velt-comment-dialog-thread-card-avatar-wireframe`
* `velt-comment-dialog-thread-card-device-type-wireframe>`
* `velt-comment-dialog-thread-card-name-wireframe`
* `velt-comment-dialog-thread-card-time-wireframe`
* `velt-comment-dialog-thread-card-message-wireframe`
* `velt-comment-dialog-thread-card-reaction-tool-wireframe`
* `velt-comment-dialog-thread-card-reactions-wireframe`
* `velt-comment-dialog-thread-card-recordings-wireframe`
```jsx React / Next.js
{/* Unread indicator */}
{/* Seen indicator with dropdown */}
{/* Draft indicator */}
... customizable
```
```HTML HTML
... customizable
```
# ThreadCard Attachments
The subcomponent of the Body of the Comment Dialog that is used to show attachments added to a Comment reply.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/threadcard-attachments.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.ThreadCard.Attachments.Image.Preview`
* `VeltCommentDialogWireframe.ThreadCard.Attachments.Other.Name`
* `VeltCommentDialogWireframe.ThreadCard.Attachments.Other.Size`
* `velt-comment-dialog-thread-card-attachments-image-preview-wireframe`
* `velt-comment-dialog-thread-card-attachments-other-name-wireframe`
* `velt-comment-dialog-thread-card-attachments-other-size-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# ThreadCard Options
The subcomponent of the Body of the Comment Dialog that is used to show the options dropdown
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/threadcard-options.png)
```jsx React / Next.js
```
```HTML HTML
```
# ThreadCard Seen
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/seen-feature.png)
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The subcomponent of the Comment Dialog that is used to compose new Comment replies.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
When the composer is in focus, a dynamic CSS class `velt-composer-focused` is added to the composer container.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-7.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.Composer.Recordings`
* `VeltCommentDialogWireframe.Composer.Input`
* `VeltCommentDialogWireframe.Composer.AssignUser`
* `velt-comment-dialog-composer-recordings-wireframe`
* `velt-comment-dialog-composer-input-wireframe`
* `velt-comment-dialog-composer-assign-user-wireframe`
```jsx React / Next.js
{/* used for inline comments */}
... customizable
```
```HTML HTML
... customizable
```
# Composer Attachments
The subcomponent of the Comoposer of the Comment Dialog that is used to show attachments on a Comment that has been composed but hasn't been sent yet.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/composer-attachments.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.Composer.Attachments.Selected.Image.Preview`
* `VeltCommentDialogWireframe.Composer.Attachments.Selected.Other.Name`
* `VeltCommentDialogWireframe.Composer.Attachments.Selected.Other.Size`
* `VeltCommentDialogWireframe.Composer.Attachments.Invalid.Item.Preview`
* `velt-comment-dialog-composer-attachments-image-preview-wireframe`
* `velt-comment-dialog-composer-attachments-other-name-wireframe`
* `velt-comment-dialog-composer-attachments-other-size-wireframe`
* `velt-comment-dialog-composer-attachments-invalid-item-preview-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that is shown in the Composer when Private Mode is turned on.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-5.png)
```jsx React / Next.js
```
```HTML HTML
```
# Custom Annotation Dropdown
The subcomponent of the Comment Dialog that represents the custom annotation dropdown.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/comments-dialog-annotation.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/comments-dialog-annotation-breakdown.png)
```jsx React / Next.js
Select a category
```
```HTML HTML
Select a category
```
# null
The subcomponent of the Comment Dialog that indicates if a Comment has become a Ghost Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-2.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Header.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-4.png)
```jsx React / Next.js
{/* Following subcomponents can be used outside header as well */}
{/* Pin Dialog Header */}
{/* Sidebar Dialog Header */}
{/* Navigation Button - Only shown on sidebar when focused thread mode is on */}
{/* Delete Button. Not added to the default components. You can explicitly add it. */}
```
```HTML HTML
```
# null
Navigation button that appears on sidebar. Clicking on it will navigate to the comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Options Dropdown
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/options-dropdown-customization.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Priority Dropdown
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/priority-dropdown-customization.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.Priority.Trigger.Name`
* `VeltCommentDialogWireframe.Priority.Content.Item.Name`
* ``
* ``
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that is shown near the Header when the Comment is a Private Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-1.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the the Reaction Pin that appears after a User selects a Reaction.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/reaction-pin-customization.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the tooltip that appears when a User hovers over a Reaction
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/reaction-tooltip-customization.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the button used to add a Reaction
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/reaction-tool-customization.png)
```jsx React / Next.js
# Your custom element
```
```HTML HTML
# Your custom element
```
# null
The subcomponent of the Comment Dialog that represents the panel of all Reactions that have been added to a Comment
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/reaction-panel-customization.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Sign In button that appears when a User is a Guest User
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-10.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Status Dropdown
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/status-dropdown-customization.png)
## Subcomponents that don't support children
* `VeltCommentDialogWireframe.Status.Trigger.Name`
* `VeltCommentDialogWireframe.Status.Content.Item.Name`
* ``
* ``
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Accept or Reject button is Moderator Mode.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-12.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Comment Dialog that represents the Upgrade button.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-11.png)
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The Pin that appears on the DOM when you place a Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default comment pin component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-pin-light.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-pin-all-states-light.png)
```jsx React / Next.js
{/* Ghost Comment Indicator */}
{/* Index */}
{/* Private Comment Indicator */}
{/* Triangle */}
{/* Unread Comment Indicator */}
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
```
### Example
```jsx
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-pin-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-pin-2.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
Below are the examples to enable Dark Mode for comments pin:
### Example
```js
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```js
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# null
The subcomponent on the Comment Pin that indicates whether the Comment is a Ghost Comment
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/Ghost_comments.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Comment Pin that indicates the index number of the Comment
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/Index_counter.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Comment Pin that indicates whether the Comment is a Private Comment
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/Private_comments.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Comment Pin that appears as a triangle in the top right corner of a Popover Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/Cell_indicator.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Comment Pin that indicates whether a Comment hasn't been read yet by the User.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/Unread_indicator.png)
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
```jsx
```
# Overview
The button to add new comments.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default comment tool component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool-light.png)
```jsx React / Next.js
# Your custom element
```
```HTML HTML
# Your custom element
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
```jsx
```
```jsx
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-tool-1.png)
`Default: false`
```js
```
```js
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
```jsx
```
# Overview
The Confirmation Dialog that appears when you delete a comment annotation.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default confirmation dialog component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/confirmation-dialog.png)
## Subcomponents that don't support children
* `VeltConfirmDialogWireframe.Title`
* `VeltConfirmDialogWireframe.Message`
* `velt-confirm-dialog-title-wireframe`
* `velt-confirm-dialog-message-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
ShadowDOM is not used in this component. You can apply your styling directly to the component.
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/async-collaboration/comments/customize-ui/confirm-dialog/images/customization/confirmation-dialog-dark.png)
This component takes the dark mode property from the parent feature (eg: comments) where this used.
If the parent feature component is in dark mode, this component will also be in dark mode.
# Overview
Components that appear when using Inline Comments
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default inline comment component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/inline-comments-overview.png)
## Subcomponents that don't support children
* `VeltInlineCommentsSectionWireframe.CommentCount`
* `velt-inline-comments-section-comment-count-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# Variants
## Pre-defined Variants
This has two pre-deinfed variants:
1. `dialog-variant`: Use this to customize the `Comment Dialog` that appears within the `Inline Comments Section` component.
2. `variant`: Use this to customize the entire `Inline Comments Section` component itself.
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Inline Comments Section that contains the Comment Count
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/inline-comments-count.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Inline Comments Section that contains the Composer Container
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/inline-comments-composer.png)
You can customize the Comment Dialog that appears in the Inline Comments Section by adding a Comment Dialog Wireframe inside this subcomponent and modifying it to your liking.
```jsx React / Next.js
...Custom Composer
```
```HTML HTML
...Custom Composer
```
# null
The subcomponent of the Inline Comments Section that contains the List
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/inline-comments-list.png)
You can customize the Comment Dialog that appears in the List of the Inline Comments Section by adding a Comment Dialog Wireframe inside this subcomponent and modifying it to your liking.
```jsx React / Next.js
...Custom Dialog
```
```HTML HTML
...Custom Dialog
```
# null
The subcomponent of the Inline Comments Section that contains the Panel
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/async-collaboration/comments/customize-ui/inline-comments-section/subcomponents/panel/images/inline-comments-breakdown.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent of the Inline Comments Section that contains the Skeleton loader that appears when the Inline Comments Section is first loading.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default skeleton subcomponent looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/async-collaboration/comments/customize-ui/inline-comments-section/subcomponents/images/inline-comments-loading.png)
```jsx React / Next.js
```
```HTML HTML
```
# Overview
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The persistent comment mode banner that appears when persistent mode is enabled and user is adding a comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The Comment Tool that appears when you highlight some text.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default text comment tool component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment-light.png)
```jsx React / Next.js
# Your custom element
```
```HTML HTML
# Your custom element
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### Example
```jsx
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-text-comment-1.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
Below are the examples to enable Dark Mode for text comment tool.
### Example
```js
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```js
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# Overview
The Toolbar that appears when you highlight some text
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default text comment toolbar component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment-toolbar-light.png)
There are 4 `Subcomponents` within the `Text Comment Toolbar` component.
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
### Example
```jsx
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment-toolbar-dark.png)
By default, all components are in Light Mode, but there are several properties and methods to enable Dark Mode.
`Default: false`
Below are the examples to enable Dark Mode for text comment toolbar.
### Example
```jsx
```
### API methods
```jsx
const commentElement = client.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
### Example
```jsx
```
### API methods
```jsx
const commentElement = Velt.getCommentElement();
commentElement.enableDarkMode();
commentElement.disableDarkMode();
```
# null
The subcomponent on the Text Comment Toolbar that is used to add a new Text Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/text-comment-toolbar-sub-1.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Text Comment Toolbar that is used to rewrite text on the page using AI.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/text-comment-toolbar-sub-3.png)
This subcomponent does not support children
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Text Comment Toolbar that is used to divide other subcomponents in the UI using a thin line.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/text-comment-toolbar-sub-2.png)
This subcomponent does not support children
```jsx React / Next.js
```
```HTML HTML
```
# null
The subcomponent on the Text Comment Toolbar that is used to query ChatGPT.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Subcomponent
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/text-comment-toolbar-sub-4.png)
This subcomponent does not support children
```jsx React / Next.js
```
```HTML HTML
```
# Notifications
There are several options to send notifications to your users.
Add notifications component within your app.
Send email notifications to your users.
Send notifications to other channels like Slack.
# Comments
Your users can add comments in context to ask questions, leave feedback, report bugs etc. We handle all complexity to ensure the comments are robust against content changes. We support many types of comment UX patterns as illustrated below.
With `Freestyle` comments, you can pin `Comments` on any elements on the page or draw area comments.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=freestyle)
With `Popover` comments, you can add `Comments` on table cells, like in Google Sheets.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=popover)
With `Stream` mode, your `Comments` will appear in a column on the right side.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=stream)
With `Text` comments, you can leave `Comments` as text highlights.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=text)
With `Inline` comments, you can add a more traditional style of comments.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=inline)
With `Inbox` mode, your focused comments appear in a sidebar on the right.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comments\&type=inbox)
With `Page` comments, you can leave `Comments` at the page level.
[Open in larger window](https://docs-mini-demo.vercel.app/comments-page-mode)
With `Chart` comments, you can leave `Comments` on popular charting libraries.
{/* [Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=nivo-charts) */}
[Open in larger window](https://velt-chart-js-demo.vercel.app)
{/* */}
With `Video` comments, you can leave frame by frame `Comments` on videos.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=video-player)
With `Lottie` comments, you can leave frame by frame `Comments` on Lottie animations.
[Open in larger window](https://lottiefiles-velt-integration-2.web.app/)
There are several customizable components that work as part of the `Comments` feature set.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-pin-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-text-comment-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-tool-1.png)
# ChartJS Comments Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/velt-chartjs-barchart.gif)
```jsx
import { Chart as ChartJS, BarElement } from 'chart.js';
import { Bar } from 'react-chartjs-2'
```
* Add `VeltComments` to the root of your app. This component is required to render comments in your app.
* Add the `VeltCommentTool` component wherever you want to show the comment tool button. Clicking on it initiates comment mode & changes your mouse cursor to a comment pin.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
```jsx
import { useCommentModeState, VeltCommentPin, VeltCommentTool, VeltComments } from '@veltdev/react';
```
* Create a `ref` and pass it into the `ref` property of the `ChartJS` component.
```jsx
const chartRef = useRef(null);
```
1. Apply `position: relative` style to the div. This ensures accurate positioning of Velt Comment Pins.
2. Set `data-velt-manual-comment-container` to `true`. This:
* informs Velt which div to use for positioning
* disables Velt's automatic comment positioning system within this div, allowing for manual positioning of comment pins
3. Give a unique ID to the chart to scope comments to the specific chart, isolating them from other charts.
```jsx
const chartId = 'dataAnalyticsChart'; // Unique ID for the chart
```
```jsx
return (
);
```
1. Handle chart click to find nearest data point and add a comment. *(Check `handleChartClick() method` on the right)*
2. Create `context` object which contains the information about the series, x-axis value, y-axis value, chartId and anything else that's relevant. This will be used when rendering the comment pin. *(Check `handleChartClick() method` on the right)*
3. Add comment with the `context` data. *(Check `addManualComment() method` on the right)*
* Only add a comment if the Velt `commentModeState` is true and the Velt `client` is available.
{/* TODO: Create a hook for adding manual comments */}
1. Get all comment annotations.
2. Loop through it and render the comment pin.
3. Use the `context` data in `CommentAnnotation`, to set the position of the comment pin. *(Check `showCommentPin() method` on the right)*
```jsx
const commentAnnotations = useCommentAnnotations(); // Get Velt comment annotations
const [chartCommentAnnotations, setChartCommentAnnotations] = React.useState([]); // Store annotations for this chart
useEffect(() => {
// Filter and store comments for the current chart, using unique chart ID
const chartCommentAnnotations = commentAnnotations?.filter((comment) => comment.context?.chartId === chartId);
setChartCommentAnnotations(chartCommentAnnotations as any || []);
}, [commentAnnotations]);
return (
... {/* Rest of the container code. */}
{/* Loop through comment annotations and render the comment pin. */}
{chartCommentAnnotations.map((comment, index) => showCommentPin(comment))}
... {/* Rest of the container code. */}
);
```
Coming Soon
```js React / Next.js
import React, { useEffect, useRef, useMemo } from 'react';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, BarElement, Title, Tooltip, Legend, Filler,} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { useVeltClient, useCommentAnnotations, useCommentModeState, VeltCommentPin } from '@veltdev/react';
// Register the necessary Chart.js components
ChartJS.register(CategoryScale, LinearScale, PointElement, BarElement, Title, Tooltip, Legend, Filler);
// Define the data for the chart
const scores = [6, 5, 5, 5, 3, 4, 6, 4, 5];
const labels = [100, 200, 300, 400, 500, 600, 700];
// Set the options for the chart
const options: any = {
// your chart options
};
const BarChart = () => {
const chartId = 'dataAnalyticsChart'; // Unique ID for the chart
const chartRef = useRef(null); // Reference to the chart instance
const { client } = useVeltClient(); // Get Velt client
const commentModeState = useCommentModeState(); // Get Velt comment mode state
const commentAnnotations = useCommentAnnotations(); // Get Velt comment annotations
const [chartCommentAnnotations, setChartCommentAnnotations] = React.useState([]); // State for chart comments
// Update chart comments when annotations change
useEffect(() => {
// Filter comments for the current chart, using unique chart ID
const chartCommentAnnotations = commentAnnotations?.filter((comment) => comment.context?.chartId === chartId);
setChartCommentAnnotations(chartCommentAnnotations as any || []);
}, [commentAnnotations]);
// Handle chart clicks to find nearest data point and add a comment
const handleChartClick = (event: any) => {
const chart: any = chartRef.current;
if (chart) {
// Get the nearest element to the click event
const elements = chart.getElementsAtEventForMode(event.nativeEvent, 'nearest', { intersect: true }, false);
if (elements.length > 0) {
const element = elements[0];
const datasetIndex = element.datasetIndex;
const index = element.index;
const dataset = chart.data.datasets[datasetIndex];
const xValue = chart.data.labels[index];
const yValue = dataset.data[index];
const context = { seriesId: dataset.label, xValue, yValue, chartId };
addManualComment(context); // Add a comment at the nearest data point with the context data
}
}
};
// Add a manual comment to the chart
const addManualComment = (context: any) => {
try {
if (client && commentModeState) {
const commentElement = client.getCommentElement();
commentElement.addManualComment({ context }); // Add a Velt comment
}
} catch (error) {
console.error('Error adding manual comment', error);
}
};
// Find the exact point on the chart to place a comment pin
const findPoint = (seriesId: any, xValue: any, yValue: any) => {
const chart: any = chartRef.current;
if (chart) {
const dataset = chart.data.datasets.find((dataset: any) => dataset.label === seriesId);
const index = chart.data.labels.indexOf(xValue);
if (dataset && index !== -1) {
const yValueInDataset = dataset.data[index];
if (yValueInDataset === yValue) {
return { x: chart.scales.x.getPixelForValue(index), y: chart.scales.y.getPixelForValue(yValue) }; // Set the x, y position for the comment pin
}
}
}
return null;
};
// Show the comment pin on the chart at the specified point
const showCommentPin = (commentAnnotation: any) => {
const context = commentAnnotation.context || {};
const point = findPoint(context.seriesId, context.xValue, context.yValue);
if (point) {
const { x, y } = point;
return (
{/* Keep the parent div as relative, as comment pin will be placed with absolute position */}
{/* Set "data-velt-manual-comment-container" to true, so that comment pin will know which div to consider while positioning */}
{/* Render the Bar chart */}
{chartCommentAnnotations.map((comment, index) => showCommentPin(comment))} {/* Show all comment pins */}
);
};
export default BarChart;
```
# Custom Charts Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/velt-chartjs-barchart.gif)
Here we have used ChartJS as an example library, you can follow the approach for any other charting library
```jsx
import { Chart as ChartJS, BarElement } from 'chart.js';
import { Bar } from 'react-chartjs-2'
```
* Add `VeltComments` to the root of your app. This component is required to render comments in your app.
* Add the `VeltCommentTool` component wherever you want to show the comment tool button. Clicking on it initiates comment mode & changes your mouse cursor to a comment pin.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
```jsx
import { useCommentModeState, VeltCommentPin, VeltCommentTool, VeltComments } from '@veltdev/react';
```
* Create a `ref` and pass it into the `ref` property of the `ChartJS` component.
```jsx
const chartRef = useRef(null);
```
1. Apply `position: relative` style to the div. This ensures accurate positioning of Velt Comment Pins.
2. Set `data-velt-manual-comment-container` to `true`. This:
* informs Velt which div to use for positioning
* disables Velt's automatic comment positioning system within this div, allowing for manual positioning of comment pins
3. Give a unique ID to the chart to scope comments to the specific chart, isolating them from other charts.
```jsx
const chartId = 'dataAnalyticsChart'; // Unique ID for the chart
```
```jsx
return (
);
```
1. Handle chart click to find nearest data point and add a comment. *(Check `handleChartClick() method` on the right)*
2. Create `context` object which contains the information about the series, x-axis value, y-axis value, chartId and anything else that's relevant. This will be used when rendering the comment pin. *(Check `handleChartClick() method` on the right)*
3. Add comment with the `context` data. *(Check `addManualComment() method` on the right)*
* Only add a comment if the Velt `commentModeState` is true and the Velt `client` is available.
{/* TODO: Create a hook for adding manual comments */}
1. Get all comment annotations.
2. Loop through it and render the comment pin.
3. Use the `context` data in `CommentAnnotation`, to set the position of the comment pin. *(Check `showCommentPin() method` on the right)*
```jsx
const commentAnnotations = useCommentAnnotations(); // Get Velt comment annotations
const [chartCommentAnnotations, setChartCommentAnnotations] = React.useState([]); // Store annotations for this chart
useEffect(() => {
// Filter and store comments for the current chart, using unique chart ID
const chartCommentAnnotations = commentAnnotations?.filter((comment) => comment.context?.chartId === chartId);
setChartCommentAnnotations(chartCommentAnnotations as any || []);
}, [commentAnnotations]);
return (
... {/* Rest of the container code. */}
{/* Loop through comment annotations and render the comment pin. */}
{chartCommentAnnotations.map((comment, index) => showCommentPin(comment))}
... {/* Rest of the container code. */}
);
```
Coming Soon
```js React / Next.js
import React, { useEffect, useRef, useMemo } from 'react';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, BarElement, Title, Tooltip, Legend, Filler,} from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { useVeltClient, useCommentAnnotations, useCommentModeState, VeltCommentPin } from '@veltdev/react';
// Register the necessary Chart.js components
ChartJS.register(CategoryScale, LinearScale, PointElement, BarElement, Title, Tooltip, Legend, Filler);
// Define the data for the chart
const scores = [6, 5, 5, 5, 3, 4, 6, 4, 5];
const labels = [100, 200, 300, 400, 500, 600, 700];
// Set the options for the chart
const options: any = {
// your chart options
};
const BarChart = () => {
const chartId = 'dataAnalyticsChart'; // Unique ID for the chart
const chartRef = useRef(null); // Reference to the chart instance
const { client } = useVeltClient(); // Get Velt client
const commentModeState = useCommentModeState(); // Get Velt comment mode state
const commentAnnotations = useCommentAnnotations(); // Get Velt comment annotations
const [chartCommentAnnotations, setChartCommentAnnotations] = React.useState([]); // State for chart comments
// Update chart comments when annotations change
useEffect(() => {
// Filter comments for the current chart, using unique chart ID
const chartCommentAnnotations = commentAnnotations?.filter((comment) => comment.context?.chartId === chartId);
setChartCommentAnnotations(chartCommentAnnotations as any || []);
}, [commentAnnotations]);
// Handle chart clicks to find nearest data point and add a comment
const handleChartClick = (event: any) => {
const chart: any = chartRef.current;
if (chart) {
// Get the nearest element to the click event
const elements = chart.getElementsAtEventForMode(event.nativeEvent, 'nearest', { intersect: true }, false);
if (elements.length > 0) {
const element = elements[0];
const datasetIndex = element.datasetIndex;
const index = element.index;
const dataset = chart.data.datasets[datasetIndex];
const xValue = chart.data.labels[index];
const yValue = dataset.data[index];
const context = { seriesId: dataset.label, xValue, yValue, chartId };
addManualComment(context); // Add a comment at the nearest data point with the context data
}
}
};
// Add a manual comment to the chart
const addManualComment = (context: any) => {
try {
if (client && commentModeState) {
const commentElement = client.getCommentElement();
commentElement.addManualComment({ context }); // Add a Velt comment
}
} catch (error) {
console.error('Error adding manual comment', error);
}
};
// Find the exact point on the chart to place a comment pin
const findPoint = (seriesId: any, xValue: any, yValue: any) => {
const chart: any = chartRef.current;
if (chart) {
const dataset = chart.data.datasets.find((dataset: any) => dataset.label === seriesId);
const index = chart.data.labels.indexOf(xValue);
if (dataset && index !== -1) {
const yValueInDataset = dataset.data[index];
if (yValueInDataset === yValue) {
return { x: chart.scales.x.getPixelForValue(index), y: chart.scales.y.getPixelForValue(yValue) }; // Set the x, y position for the comment pin
}
}
}
return null;
};
// Show the comment pin on the chart at the specified point
const showCommentPin = (commentAnnotation: any) => {
const context = commentAnnotation.context || {};
const point = findPoint(context.seriesId, context.xValue, context.yValue);
if (point) {
const { x, y } = point;
return (
{/* Keep the parent div as relative, as comment pin will be placed with absolute position */}
{/* Set "data-velt-manual-comment-container" to true, so that comment pin will know which div to consider while positioning */}
{/* Render the Bar chart */}
{chartCommentAnnotations.map((comment, index) => showCommentPin(comment))} {/* Show all comment pins */}
);
};
export default BarChart;
```
# Highcharts Comments Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Charts-Comments.gif)
```jsx
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
```
{/* bar/column, line */}
* Add `VeltComments` once to the root of your app. This component is required to render comments in your app.
```jsx
import { VeltHighChartComments, VeltComments } from '@veltdev/react';
```
* Create a `ref` and pass it into the `ref` property of the `HighchartsReact` component.
* This ref object will be used by Velt to get the Chart data and add comment pin to it.
```jsx
const chartComponentRef = useRef(null);
```
* Give it a `position: relative` style. This will help position the Velt Comment Pins accurately.
```jsx
```
* Conditionally render `VeltHighChartsComments` in the same container div as the `HighchartsReact` component.
* Set a unique `id` in the VeltHighChartsComments component to scope comments to the specific chart, isolating them from other charts.
* Pass the `chartComputedData` prop with the `ref` you created earlier.
```jsx
{
chartComponentRef.current &&
}
```
* Pass an array to the `dialogMetadataTemplate` prop in the VeltHighChartsComments component.
* This array determines the chart metadata displayed in the comment dialog, representing the data point (e.g., x-axis and y-axis values) on which the comment was added.
* Customize the order of the keys or remove unnecessary keys to control how the metadata is presented in the comment dialog.
`Default: ['groupId', 'label', 'value']`
For example:
```jsx
```
Coming Soon
```js React / Next.js
import React, { useEffect, useRef, useState } from 'react';
import { VeltHighChartComments } from '@veltdev/react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
function HighChartsLineChartExample() {
const [data, setData] = useState(CHART_DATA);
const chartComponentRef = useRef(null);
const options = {
// your chart options
};
return (
{
chartComponentRef.current &&
}
);
}
```
# NivoCharts Comments Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Charts-Comments.gif)
```jsx
import { ResponsiveLine } from '@nivo/line';
```
* Add `VeltComments` once to the root of your app. This component is required to render comments in your app.
```jsx
import { VeltNivoChartComments, VeltComments } from '@veltdev/react';
```
* Add a `nivo-chart-container` class to it. This will help us position the Velt Comment Pins accurately.
```jsx
```
* Add a custom function as a layer inside your Nivo Chart component and return `VeltNivoChartComments` component.
* Set a unique `id` in the VeltNivoChartComments component to scope comments to the specific chart, isolating them from other charts.
* Pass the `chartComputedData` props to it.
{/* bar, line, pie */}
```jsx
{
return (
)
}
]}
/>
```
* Pass an array to the `dialogMetadataTemplate` prop in the VeltHighChartsComments component.
* This array determines the chart metadata displayed in the comment dialog, representing the data point (e.g., x-axis and y-axis values) on which the comment was added.
* Customize the order of the keys or remove unnecessary keys to control how the metadata is presented in the comment dialog.
`Default: ['groupId', 'label', 'value']`
For example:
```jsx
```
Coming Soon
```js React / Next.js
import { ResponsiveLine } from '@nivo/line';
import { VeltNivoChartComments } from '@veltdev/react';
import { useState } from 'react'
function YourComponent() {
const [data, setData] = useState(CHART_DATA);
return (
// add nivo-chart-container class to the parent element of your NivoChart
{
return (
)
}
]}
/>
)
}
```
# Freestyle Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Freestyle-Comment.gif)
Import the `VeltComments` component and the `VeltCommentTool` component.
```js
import {
VeltProvider,
VeltComments,
VeltCommentTool
} from '@veltdev/react';
```
Add the `VeltComments` component to the root of your app.
This component is required to render comments in your app.
```js
```
Add the `VeltCommentTool` component wherever you want to show the comment tool button.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
Clicking on it initiates comment mode & changes your mouse cursor to a comment pin. Now you can click anywhere on the document to attach comments to any elements.
```js
```
Test it out by opening the page with Velt components in your browser.
Click on the `Comment Tool` and click anywhere on the page to add a comment.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/standard-comment.png)
Add the comment component to your template. Try to put it as close to the root level of your ``.
This component is required to render comments in your app.
```html
```
Add the `` component wherever you want to show the comment tool button.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
Clicking on it initiates comment mode & changes your mouse cursor to a comment pin. Now you can click anywhere on the document to attach comments to any elements.
```js
```
Test it out by adding a comment.
You should be able to leave comments using the `Comment Tool`.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/standard-comment.png)
```jsx React / Next.js
import {
VeltProvider,
VeltComments,
VeltCommentTool
} from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
Comment documentation
```
# Inbox Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Inbox-Comment.gif)
Import the `VeltComments` component and the `VeltCommentTool` component.
```js
import {
VeltProvider,
VeltComments,
VeltCommentTool
} from '@veltdev/react';
```
Add the `VeltComments` component to the root of your app and mark the `inboxMode` property as true.
This component is required to render comments in your app.
This mode enable a more focused inbox style UI for each comment on the right side. Instead of showing a comment dialog box, it shows the given comment in a sidebar.
You can combine this with Freestyle, Popover, or Text mode.
```js
```
Add the `VeltCommentTool` component wherever you want to show the comment tool button.
Clicking on it initiates comment mode & changes your mouse cursor to a comment pin. Now you can click anywhere on the document to attach comments to any elements.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
```jsx
```
Test it out by opening the page with Velt components in your browser.
You should be able to leave a comment by selecting some text or using the `Comment Tool`.
Add the comment component to your template. Try to put it as close to the root level of your ``.
Set the `inbox-mode` attribute to `true`.
This component is required to render comments in your app.
This mode enable a more focused inbox style UI for each comment on the right side. Instead of showing a comment dialog box, it shows the given comment in a sidebar.
You can combine this with Freestyle, Popover, or Text mode.
```html
```
The comment tool allows you to add comments.
When you click on the comment tool, it initiates comment mode. In Freestyle mode, you can attach comments to any elements on the page.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
```html
```
Test it out by opening the page with Velt components in your browser.
You should be able to leave a comment by selecting some text or using the `Comment Tool`.
```jsx React / Next.js
import { VeltProvider, VeltComments, VeltCommentTool } from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
Comment documentation
```
# Inline Comments Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/inline-comment.gif)
Import the `VeltProvider`, `VeltComments`, and `VeltInlineCommentsSection` component.
```js
import { VeltProvider, VeltComments, VeltInlineCommentsSection } from '@veltdev/react';
```
* Add the `VeltComments` component to the root of your app where your `VeltProvider` is.
* This component is required to render comments in your app.
```js
```
* Create an element to hold your Inline Comments component, such as a `div` or `section`.
* Add a unique element `id` to it.
* (optional) Set the same id to the `data-velt-target-inline-comment-element-id` attribute.
This is used in advanced scenarios, eg: This ensures that if you add a freestyle or text comment within this container, it will appear in inline comments component too.
```jsx
Your Article
```
* Add `VeltInlineCommentsSection` component inside your container.
* Add `targetCommentElementId` property to the Velt Inline Comments component. This needs to match the id you set to the container. This binds the Inline Comments component with the desired container.
```jsx
```
* Default: `true`
* By default inline comment section is multithreaded.
* You can make it single threaded by setting `multiThread` attribute to `false`.
```jsx
```
* Add the Velt Comments component to the root of your app.
* This component is required to render comments in your app.
```html
```
* Create an element to hold your Inline Comments component, such as a `div` or `section`.
* Add a unique element `id` to it.
* (optional) Set the same id to the `data-velt-target-inline-comment-element-id` attribute.
This is used in advanced scenarios, eg: This ensures that if you add a freestyle or text comment within this container, it will appear in inline comments component too.
```html
Your Article
```
* Add `velt-inline-comments-section` component inside your container.
* Add `target-comment-element-id` property to the Velt Inline Comments component. This needs to match the id you set to the container. This binds the Inline Comments component with the desired container.
```jsx
```
* Default: `true`
* By default inline comments are multithreaded.
* You can make it single threaded by setting `multi-thread` attribute to `false`.
```html
```
```jsx React / Next.js
import { VeltProvider, VeltComments } from '@veltdev/react';
export default function App() {
return (
Your Article
);
}
```
```html HTML
Comment documentation
Your Article
```
# Lottie Player Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Lottie-Comment.gif)
Make sure you turn React strict mode off.
### a. Add the `VeltComments` component in the root of your app
Add the `VeltComments` component to enable the Comment feature in your app.
Enable the follow attributes to be `true`:
* `priority`: allows user to P0, P1, or P2 as priority. You can customize this list.
* `autoCategorize`: allows AI to categorize comment as Question, Feedback, Bug, Other. You can customize this list.
* `commentIndex`: shows a small icon showing index of the comment in order of creation.
```jsx
```
### b. Add the `VeltCommentTool` component wherever you want your render the comment tool.
Note you can also provide your own button to this component.
```jsx
```
### c. Place the `VeltCommentPlayerTimeline` component as a sibling to your video player.
To show comment bubbles on your player seek bar, add the `` component as a sibling to your video player component.
It will auto adjust to the same width as your video player.
```jsx
```
Right now we assume you have a maximum of one `` component and one sibling video player component per `documentID`
Ensure that the parent container of `` doesnt have CSS position value as 'static'.
### d. Add the `` component.
(Optional) To embed the sidebar in your existing component, set `embedMode` prop as `true`.
```jsx
```
### e. Add the `` component.
This will open or close the Comment Sidebar.
Note you can also provide your own button to this component.
```jsx
{/* your custom button (optional) */}
```
You can pass an integer to `totalMediaLength` using props to represent the total number of frames or seconds in the video:
```jsx
```
Alternatively, you can set this using API method call. This is useful if you first need to grab the total frames from another method before setting it.
```jsx
const commentElement = client.getCommentElement();
commentElement.setTotalMediaLength(120);
```
Add an event handler on where you originally placed `` to handle `onCommentModeChange` events.
Use this whenever the user clicks on the comment tool, to pause your player and set a new `Location` in the Velt SDK.
Setting a `Location` in the Velt SDK ensures that the comments are tied to that particular media frame or timestamp.
```jsx
onCommentModeChange(mode)} />
const onCommentModeChange = (mode) => {
// mode will be `true` if the user has activated the comment tool
// If the comment tool is active, pause the player and set the "location".
if (mode) {
// pause player
// See step 8 below for details
setLocation()
}
});
```
You can pass in a key value pair object that represents the current state of your player. If you are using the `VeltCommentPlayerTimeline` component, ensure to set the current rounded frame or second in the special key `currentMediaPosition`.
`currentMediaPosition` is a protected keyword that is used to arrange the comment bubbles on top of the video player timeline in the correct spot
```jsx
const setLocation = (client) => {
// set currentMediaPosition property on a Location object to represent the current frame
let location = {
currentMediaPosition : 120
}
//set the Location using the client
client.setLocation(location)
}
```
Call `removeLocation()` when your player starts playing:
```jsx
const removeLocation = () => {
//remove the location, so the video player can play without comments appearing in frames
client.removeLocation()
}
```
Add the `onCommentClick` event handler on the `VeltCommentsSidebar` & `VeltCommentPlayerTimeline` components you added earlier.
The event will give you back the `location` object that you had set earlier.
You can use this object to update your player state and also update the SDK's `location` so that we can start rendering the comments associated with that `location`.
### a. Handle it on the `VeltCommentsSidebar`:
```jsx
onCommentClick(event)} />
const onCommentClick = (event) => {
if (event) {
// Get the location object from the event.
const { location } = event;
if (location) {
// Get the media position where the comment was added.
const { currentMediaPosition } = location;
if (currentMediaPosition) {
// Pause the player.
// Seek to the given comment media position.
// Set the Velt Location to the clicked comment location.
client.setLocation(location);
}
}
}
}
```
### b. Handle it on the `VeltCommentPlayerTimeline`:
```jsx
onTimelineCommentClick(event)} />
const onTimelineCommentClick = (event) => {
if (event) {
// Get the location object from the event.
const { location } = event;
if (location) {
// Get the media position where the comment was added.
const { currentMediaPosition } = location;
if (currentMediaPosition) {
// Pause the player.
// Seek to the given comment media position.
// Set the Velt Location to the clicked comment location.
client.setLocation(location);
}
}
}
}
```
The clicked Comment data will be in the following format:
| property | type | description |
| ----------------- | ------ | --------------------------------------------------------------- |
| `documentId` | string | The document ID where the comment was added |
| `location` | object | The location where the comment was added |
| `targetElementId` | string | The DOM ID of the target element on which the comment was added |
| `context` | Object | Any context data passed when the comment was added |
# Additional Configurations
## Allow comments only on certain elements
Provide a list of element DOM IDs where commenting should be allowed.
Comments will be disabled for all other elements.
```jsx
const commentElement = client.getCommentElement();
commentElement.allowedElementIds(['lottiePlayerContainer']);
```
### a. Add the `VeltComments` component in the root of your app
Add the `VeltComments` component to enable the Comment feature in your app.
Enable the follow attributes to be `true`:
* `priority`: allows user to P0, P1, or P2 as priority. You can customize this list.
* `auto-categorize`: allows AI to categorize comment as Question, Feedback, Bug, Other. You can customize this list.
* `comment-index`: shows a small icon showing index of the comment in order of creation.
```jsx
```
### b. Add the `velt-comment-tool` component wherever you want your render the comment tool.
Note you can also provide your own button to this component.
```jsx
```
### c. Place the `velt-commen-player-timeline` component as a sibling to your video player.
To show comment bubbles on your player seek bar, add the `` component as a sibling to your video player component.
It will auto adjust to the same width as your video player.
```jsx
```
Right now we assume you have a maximum of one `` component and one sibling video player component per `documentID`
Ensure that the parent container of `` doesnt have CSS position value as 'static'.
### d. Add the `` component.
(Optional) To embed the sidebar in your existing component, set `embed-mode` prop as `true`.
```jsx
```
### e. Add the `` component.
This will open or close the Comment Sidebar.
Note you can also provide your own button to this component.
```jsx
```
You can pass an integer to `total-media-length` using props to represent the total number of frames or seconds in the video:
```jsx
```
Alternatively, you can set this using API method call. This is useful if you first need to grab the total frames from another method before setting it.
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setTotalMediaLength(120);
```
Add an event handler to handle `onCommentModeChange` events.
Use this whenever the user clicks on the comment tool, to pause your player and set a new `Location` in the Velt SDK.
Setting a `Location` in the Velt SDK ensures that the comments are tied to that particular media frame or timestamp.
```jsx
const commentElement = Velt.getCommentElement();
let subscription = commentElement.onCommentModeChange().subscribe((mode) => {
// mode will be `true` if the user has activated the comment tool
// If the comment tool is active, pause the player and set the "location".
if (mode) {
// pause player
// See step 8 below for details
setLocation()
}
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can pass in a key value pair object that represents the current state of your player. If you are using the `velt-comment-player-timeline` component, ensure to set the current rounded frame or second in the special key `current-media-position`.
`current-media-position` is a protected keyword that is used to arrange the comment bubbles on top of the video player timeline in the correct spot
```jsx
const setLocation = (client) => {
// set currentMediaPosition property on a Location object to represent the current frame
let location = {
currentMediaPosition : 120
}
//set the Location using the client
Velt.setLocation(location)
}
```
Call `removeLocation()` when your player starts playing:
```jsx
const removeLocation = () => {
//remove the location, so the video player can play without comments appearing in frames
Velt.removeLocation()
}
```
Add the `onCommentClick` event handler on the `VeltCommentsSidebar` & `VeltCommentPlayerTimeline` components you added earlier.
The event will give you back the `location` object that you had set earlier.
You can use this object to update your player state and also update the SDK's `location` so that we can start rendering the comments associated with that `location`.
### a. Handle it on the `velt-comments-sidebar`:
```jsx
const onCommentClick = (event) => {
if (event) {
// Get the location object from the event.
const { location } = event;
if (location) {
// Get the media position where the comment was added.
const { currentMediaPosition } = location;
if (currentMediaPosition) {
// Pause the player.
// Seek to the given comment media position.
// Set the Velt Location to the clicked comment location.
client.setLocation(location);
}
}
}
}
const commentElement = Velt.getCommentElement();
let subscription = commentElement.onSidebarButtonOnCommentDialogClick().subscribe((event) => onCommentClick(event));
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
### b. Handle it on the ``:
```html
```
The clicked Comment data will be in the following format:
| property | type | description |
| ----------------- | ------ | --------------------------------------------------------------- |
| `documentId` | string | The document ID where the comment was added |
| `location` | object | The location where the comment was added |
| `targetElementId` | string | The DOM ID of the target element on which the comment was added |
| `context` | Object | Any context data passed when the comment was added |
# Additional Configurations
## Allow comments only on certain elements
Provide a list of element DOM IDs where commenting should be allowed.
Comments will be disabled for all other elements.
```jsx
const commentElement = Velt.getCommentElement();
commentElement.allowedElementIds(['lottiePlayerContainer']);
```
# Page Mode Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Page-Comments.gif)
Import the Comments Sidebar Components.
```jsx
import {
VeltProvider,
VeltCommentsSidebar,
VeltSidebarButton,
} from '@veltdev/react';
```
Add the `VeltComments` and `VeltCommentsSidebar` components to the root of your app.
```jsx
```
Set the `pageMode` attribute to true on the `VeltCommentsSidebar` component.
```js App.js
```
Add the Sidebar button to toggle the sidebar.
```jsx
```
Test Page Mode out by opening the sidebar. You should be able to leave Page Mode comments at the bottom of the Comments Sidebar.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/page-comment.png)
Place the `` component at the root of your app.
```html
```
Enable Page Mode by setting the `page-mode` attribute to `true` on the `` component.
```html
```
Place the `` component wherever you want the toggle button to appear.
```html
```
Test Page Mode out by opening the sidebar. You should be able to leave Page Mode comments at the bottom of the Comments Sidebar.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/page-comment.png)
```jsx React / Next.js
import {
VeltProvider,
VeltCommentsSidebar,
VeltSidebarButton,
} from '@veltdev/react';
export default function App() {
return (
{/* Add VeltComments to the root of your app provider */}
{/* Add VeltCommentsSidebar to the root of your app provider */}
{/* Add VeltCommentSideBarButton wherever you want it to appear */}
);
}
```
```html HTML
Collaboration App
```
# Popover Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Cell-Comment.gif)
Import the `VeltComments`, `VeltCommentTool`, and `VeltCommentBubble` components.
```js
import {
VeltProvider,
VeltComments,
VeltCommentTool,
VeltCommentBubble
} from '@veltdev/react';
```
Add the `VeltComments` component to the root of your app and mark the `popoverMode` property as `true`.
This component is required to render comments in your app.
Popover mode means that comments can be attached to specific target elements. The UX pattern is very similar to commenting in Google Sheets.
```js
```
There are two patterns to add the `Comment Tool` component with `Popover` comments:
* Add a `Comment Tool` next to each element you want to have `Popover` comments
* Have a single `Comment Tool` and use it to pin a `Popover `comment on a particular element
### a. Comment Tool next to each element
In `Popover Mode`, you can add a `Comment Tool` near each cell or element you want to comment on.
Add the ``component on each component where you want to enable commenting.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
For example, in a table you could add this tool to each cell and show it on hover or right click context menu.
You must specify a target element ID which binds the tool to that element. When users click on the `Comment Tool`, it will attach a `Comment` to the target element.
Once the `Comment` is saved, you will notice a triangle on the top right corner of the element indicating that a `Comment` is present on this element.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-comment-show.png)
```jsx
```
### b. Single Comment Tool
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/freestyle-popover.gif)
If you want to have a single `Comment Tool` in a single location such as the navigation bar, you can do so as well.
To do this, add `data-velt-target-comment-element-id` as an attribute on each element you want to add comments on.
Now, when you click on the `Comment Tool` and click on the target element, it will attach a `Popover` comment to the element.
You will now notice that you can only add one `Comment Annotation` per element.
If you don't add the `data-velt-target-comment-element-id` attribute, you will be adding multiple `Comment Annotations` on the same element.
```jsx
```
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-bubble.png)
This component accepts a target element ID & binds the comment annotation to it.
It shows the total number of replies in the given comment annotation.
You also have the option to choose whether to display the total number of replies or just the unread replies.
This gives you a lot of flexibility as you can place this component anywhere and provides a more obvious affordance to your users.
**Props:** `commentCountType`: This prop allows you to decide which count to display.
* `total`: Shows the total number of replies. (default)
* `unread`: Shows the number of unread replies.
```js
```
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-comment-pin.png)
You can choose to remove the triangle that appears in `Popover` mode.
By default, the triangle is enabled.
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePopoverTriangleComponent();
commentElement.disablePopoverTriangleComponent();
```
Test it out by opening the page with Velt components in your browser.
Click on the `Comment Tool` and leave a comment on the target element.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-comment-pin.png)
Add the comment component to your template. Try to put it as close to the root level of your ``.
This component is required to render comments in your app.
```html
```
There are two patterns to add the `Comment Tool` component with `Popover` comments:
* Add a `Comment Tool` next to each element you want to have `Popover` comments
* Have a single `Comment Tool` and use it to pin a `Popover `comment on a particular element
### Comment Tool next to each element
In `Popover Mode`, you can add a `Comment Tool` near each cell or element you want to comment on.
Add the ``component on each component where you want to enable commenting.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool.png)
For example, in a table you could add this tool to each cell and show it on hover or right click context menu.
You must specify a target element ID which binds the tool to that element. When users click on the `Comment Tool`, it will attach a `Comment` to the target element.
Once the `Comment` is saved, you will notice a triangle on the top right corner of the element indicating that a `Comment` is present on this element.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-comment-show.png)
```html
```
### Single Comment Tool
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/freestyle-popover.gif)
If you want to have a single `Comment Tool` in a single location such as the navigation bar, you can do so as well.
To do this, add `data-velt-target-comment-element-id` as an attribute on each element you want to add comments on.
Now, when you click on the `Comment Tool` and click on the target element, it will attach a `Popover` comment to the element.
You will now notice that you can only add one `Comment Annotation` per element.
If you don't add the `data-velt-target-comment-element-id` attribute, you will be adding multiple `Comment Annotations` on the same element.
```jsx
```
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-bubble.png)
This component accepts a target element ID & binds the comment annotation to it.
It shows the total number of replies in the given comment annotation.
You also have the option to choose whether to display the total number of replies or just the unread replies.
This gives you a lot of flexibility as you can place this component anywhere and provides a more obvious affordance to your users.
**Props:** `comment-count-type`: This prop allows you to decide which count to display.
* `total`: Shows the total number of replies. (default)
* `unread`: Shows the number of unread replies.
```html
```
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-comment-pin.png)
You can choose to remove the triangle that appears in `Popover` mode.
By default, the triangle is enabled.
```jsx
```
API Method:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePopoverTriangleComponent();
commentElement.disablePopoverTriangleComponent();
```
Test it out by adding a comment.
You should be able to leave a comment on the target element using the `Comment Tool`.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/popover-comment-pin.png)
```jsx React / Next.js
import {
VeltProvider,
VeltComments,
VeltCommentTool,
VeltCommentBubble
} from '@veltdev/react';
export default function App() {
return (
{/* Comment Tool next to each element */}
{/* Single Comment Tool */}
);
}
```
```html HTML
Comment documentation
```
# Stream Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/View-Stream-Comments.gif)
Import the `VeltComments` component.
```js
import {
VeltProvider,
VeltComments,
} from '@veltdev/react';
```
Add the `VeltComments` component inside a scrolling container. Make it a sibling to the element that contains the content you want to be commented.
In the `VeltComments` component, mark the `streamMode` attribute as `true`.
Also set the `streamViewContainerId` attribute to the id of the scrolling container.
Stream mode renders all comment dialog boxes in a column on the right side similar to Google Docs. It works well with Text mode, which is enabled by default.
Setting a reference to the container ID helps us position & scroll the comment stream as the user scrolls more robustly.
```js
//This element is scrollable
//This element contains the content that you want to be commented.
```
Test it out by adding a comment.
Select any text, a `Comment Tool` button will appear near the highlighted text.
Click on it to add a comment and see the comment appear in the comment stream.
Add the `` component inside a scrolling container. Make it a sibling to the element that contains the content you want to be commented.
In the `` component, mark the `stream-mode` attribute as `true`.
Also set the `stream-view-container-id` attribute to the id of the scrolling container.
Stream mode renders all comment dialog boxes in a column on the right side similar to Google Docs. It works well with Text mode, which is enabled by default.
Setting a reference to the container ID helps us position & scroll the comment stream as the user scrolls more robustly.
```html
//This element is scrollable
//This element contains the content that you want to be commented.
```
Test it out by adding a comment.
Select any text, a `Comment Tool` button will appear near the highlighted text.
Click on it to add a comment and see the comment appear in the comment stream.
```jsx React / Next.js
import { VeltProvider, VeltComments, VeltCommentTool } from '@veltdev/react';
export default function App() {
return (
//This element is scrollable
//This element contains the content that you want to be commented.
);
}
```
```html HTML
Comment documentation
//This element is scrollable
//This element contains the content that you want to be commented.
```
# Text Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Text-Comments.gif)
Import the `VeltComments` component.
```js
import { VeltProvider, VeltComments } from '@veltdev/react';
```
Add the `VeltComments` component to the root of your app.
This component is required to render comments in your app.
Text mode is enabled by default. To disable it, set the `textMode` attribute to `false`.
Text mode allows users to select any text and attach comments to it similar to Google Docs.
```js
```
Test it out by opening the page with Velt components in your browser.
Select any text, a `Comment Tool` button will appear near the highlighted text. Click on it to add a comment.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment.png)
Add the comment component to your template. Try to put it as close to the root level of your ``.
This component is required to render comments in your app. Text mode allows users to attach comments to highlighted text.
```html
```
Test it out by opening the page with Velt components in your browser.
Select any text, a `Comment Tool` button will appear near the highlighted text. Click on it to add a comment.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/text-comment.png)
```jsx React / Next.js
import { VeltProvider, VeltComments } from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
Comment documentation
```
# Tiptap Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/Add-Text-Comments.gif)
* Add the `Velt Comments` component to the root of your app.
* This component is required to render comments in your app.
```js
```
```html
```
```bash
npm i @veltdev/tiptap-velt-comments
```
```js
import { TiptapVeltComments } from '@veltdev/tiptap-velt-comments';
const editor = new Editor({
extensions: [
TiptapVeltComments,
// ... other extensions
],
});
```
```js
import { TiptapVeltComments } from '@veltdev/tiptap-velt-comments';
const editor = new Editor({
extensions: [
TiptapVeltComments,
// ... other extensions
],
});
```
* Add a button that appears in the context menu of your Tiptap editor when the user selects text. Refer to the [Tiptap documentation](https://tiptap.dev/docs/editor/getting-started/style-editor/custom-menus) to learn more about custom menus.
* This button will be used to add a comment to the selected text.
* Call this method to add a comment to selected text in the Tiptap editor. You can use this when the user clicks on the comment button in context menu or presses a keyboard shortcut.
* Args:
* `editor`: instance of the Tiptap editor.
* `tiptapVeltCommentConfig`: optional object to set the Comment Annotation's [custom metadata](/async-collaboration/comments/customize-behavior/custom-metadata).
* Example:
```js
import { addTiptapVeltComment } from '@veltdev/tiptap-velt-comments';
const tiptapVeltCommentConfig = {
context: {
storyId: 'story-id',
storyName: 'story-name',
},
};
addTiptapVeltComment(editor, tiptapVeltCommentConfig);
```
```js
import { addTiptapVeltComment } from '@veltdev/tiptap-velt-comments';
const tiptapVeltCommentConfig = {
context: {
storyId: 'story-id',
storyName: 'story-name',
},
};
addTiptapVeltComment(editor, tiptapVeltCommentConfig);
```
* You can style the commented text by adding a CSS class to the `velt-comment-text` element.
* By using the `comment-available` attribute, you can apply styles only when the comment data has loaded.
```css
velt-comment-text[comment-available="true"] {
background-color: #ffff00;
}
```
# Custom Video Player Setup
Use this guide to add collaboration into your own custom video player.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/custom-video-player.gif)
You will be using the following components:
* `Velt Comments`: Renders comments on the DOM.
* `Velt Comment Tool`: Enables or disables adding comments.
* `Velt Reaction Tool`: Enables or disables adding reactions.
* `Velt Comment Player Timeline`: Adds comments bubble over your player seek bar.
* `Velt Comments Sidebar`: Adds a sidebar that shows all comments. Users can also search, filter & navigate to the comments from here.
* `Velt Sidebar Button`: Toggles the sidebar on/off.
### Add the `Velt Comments` component in the root of your app
Add the `Velt Comments` component to the root of your app.
```jsx
```
```html
```
### Add the `Velt Comment Tool` component wherever you want your render the comment tool.
Note you can also provide your own button to this component.
```jsx
```
```jsx
```
### Add the `Velt Reaction Tool` component wherever you want your render the reaction tool.
* Provide the video player ID on which you want the reactions to be added.
* Add an event handler to handle `onReactionToolClick` events.
```jsx
onReactionToolClick()}>
```
```html
```
```js
const reactionToolTag = document.querySelector('velt-reaction-tool');
reactionToolTag.addEventListener('onReactionToolClick', (event) => {
console.log('reaction tool clicked', event.detail);
});
```
### Place the `Velt Comment Player Timeline` component as a sibling to your video player.
* To show comment bubbles on your player seek bar, add the `Velt Comment Player Timeline` component as a sibling to your video player component.
* It will auto adjust to the same width as your video player.
Right now we assume you have a maximum of one `velt comment player timeline` component and one sibling video player component per `documentID`
Ensure that the parent container of `velt comment player timeline` doesnt have CSS position value as 'static'.
```jsx
```
```jsx
```
### Add `id` to the video player or its parent element.
* If you don't have access to the raw `
You can pass an integer to `total media length` using props to represent the total number of frames or seconds in the video:
```jsx
```
```jsx
```
Alternatively, you can set this using API method call.
This is useful if you first need to grab the total frames from another method before setting it.
```jsx
const commentElement = client.getCommentElement();
commentElement.setTotalMediaLength(120);
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.setTotalMediaLength(120);
```
* Detect when the user activates the comment tool by adding an event handler to the `onCommentModeChange` event.
* Pause your player and set a new `Location` in the Velt SDK.
* This ensures that the comments are tied to that particular media frame or timestamp.
* You can pass in a key value pair object that represents the current state of your player.
If you are using the `Velt Comment Player Timeline` component, ensure to set the current rounded frame or second in the special key `currentMediaPosition`.
`currentMediaPosition` is a protected keyword that is used to arrange the comment bubbles on top of the video player timeline in the correct spot
```jsx
onCommentModeChange(mode)} />
const onCommentModeChange = (mode) => {
// mode will be `true` if the user has activated the comment tool
// If the comment tool is active, pause the player and set the "location".
if (mode) {
// pause player
setLocation()
}
});
const setLocation = (client) => {
// set currentMediaPosition property on a Location object to represent the current frame
let location = {
currentMediaPosition : 120,
videoPlayerId : "videoPlayerId"
}
//set the Location using the client
client.setLocation(location)
}
```
```jsx
const commentElement = Velt.getCommentElement();
let subscription = commentElement.onCommentModeChange().subscribe((mode) => {
// mode will be `true` if the user has activated the comment tool
// If the comment tool is active, pause the player and set the "location".
if (mode) {
// pause player
setLocation()
}
});
const setLocation = (client) => {
// set currentMediaPosition property on a Location object to represent the current frame
let location = {
currentMediaPosition : 120,
videoPlayerId : "videoPlayerId"
}
//set the Location using the client
Velt.setLocation(location)
}
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
* Detect when the user activates the reaction tool by adding an event handler to the `onReactionToolClick` event.
* Pause your player and set a new `Location` in the Velt SDK.
* This ensures that the reactions are tied to that particular media frame or timestamp.
* You can pass in a key value pair object that represents the current state of your player.
If you are using the `Velt Comment Player Timeline` component, ensure to set the current rounded frame or second in the special key `currentMediaPosition`.
`currentMediaPosition` is a protected keyword that is used to arrange the comment bubbles on top of the video player timeline in the correct spot
```jsx
onReactionToolClick()}>
const onReactionToolClick = () => {
// pause player
setLocation()
});
const setLocation = () => {
// set currentMediaPosition property on a Location object to represent the current frame
let location = {
currentMediaPosition : 120,
videoPlayerId : "videoPlayerId"
}
//set the Location using the client
client.setLocation(location)
}
```
```jsx
const reactionToolTag = document.querySelector('velt-reaction-tool');
reactionToolTag.addEventListener('onReactionToolClick', (event) => {
console.log('reaction tool clicked', event.detail);
});
```
Call `removeLocation()` when your player starts playing:
```jsx
const removeLocation = () => {
//remove the location, so the video player can play without comments appearing in frames
client.removeLocation()
}
```
```jsx
const removeLocation = () => {
//remove the location, so the video player can play without comments appearing in frames
Velt.removeLocation()
}
```
Add the `onCommentClick` event handler on the `Velt Comments Sidebar` & `Velt Comment Player Timeline` components you added earlier.
The event will give you back the `location` object that you had set on the comment.
Use this object to:
* update your player state
* update the SDK's `location` so the comments associated with that `location` are rendered.
### Handle click on the `Velt Comments Sidebar`:
```jsx
onCommentClick(event)} />
const onCommentClick = (event) => {
if (event) {
// Get the location object from the event.
const { location } = event;
if (location) {
// Get the media position where the comment was added.
const { currentMediaPosition } = location;
if (currentMediaPosition) {
// Pause the player.
// Seek to the given comment media position.
// Set the Velt Location to the clicked comment location.
client.setLocation(location);
}
}
}
}
```
```jsx
const commentElement = document.querySelector('velt-comments-sidebar');
commentElement.addEventListener('onCommentClick', onCommentClick);
// event handler for when a comment is clicked on
const onCommentClick = (event) => {
if (event) {
// Get the location object from the event.
const { location } = event;
if (location) {
// Get the media position where the comment was added.
const { currentMediaPosition } = location;
if (currentMediaPosition) {
// Pause the player.
// Seek to the given comment media position.
// Set the Velt Location to the clicked comment location.
client.setLocation(location);
}
}
}
};
```
### Handle click on the `Velt Comment Player Timeline`:
```jsx
onTimelineCommentClick(event)} />
const onTimelineCommentClick = (event) => {
if (event) {
// Get the location object from the event.
const { location } = event;
if (location) {
// Get the media position where the comment was added.
const { currentMediaPosition } = location;
if (currentMediaPosition) {
// Pause the player.
// Seek to the given comment media position.
// Set the Velt Location to the clicked comment location.
client.setLocation(location);
}
}
}
}
```
```js
const playerTimelineElement = document.querySelector('velt-comment-player-timeline');
if (playerTimelineElement) {
playerTimelineElement.addEventListener('onCommentClick', (event) => {
console.log("onCommentClick: ", event.detail);
});
}
```
The clicked Comment data will be in the following format:
| property | type | description |
| ----------------- | ------ | --------------------------------------------------------------- |
| `documentId` | string | The document ID where the comment was added |
| `location` | object | The location where the comment was added |
| `targetElementId` | string | The DOM ID of the target element on which the comment was added |
| `context` | Object | Any context data passed when the comment was added |
{/* # Additional Configurations
## Allow comments only on certain elements
Provide a list of element DOM IDs where commenting should be allowed.
Comments will be disabled for all other elements.
```jsx
const commentElement = client.getCommentElement();
commentElement.allowedElementIds(['custom-video-player-container']);
``` */}
# Prebuilt Video Player Setup
Use this guide if you want to set up a prebuilt video player from our SDK with collaborative features built in.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/video-comment.gif)
Import the `VeltVideoPlayer` component.
```jsx
import { VeltVideoPlayer} from '@veltdev/react'
```
Add your video src URL to the `src` attribute of the `VeltVideoPlayer` component.
There are a few other properties:
* `darkMode` - boolean to enable dark mode
* `sync` - boolean to enable sync mode
```jsx
```
Add your video src URL to the `src` attribute of the `` component.
There are a few other properties:
* `dark-mode` - boolean to enable dark mode
* `sync` - boolean to enable sync mode
```html
```
```jsx React / Next.js
import {
VeltVideoPlayer
} from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
Video Player documentation
```
# Overview
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
This component is a thin wrapper around the [Comment Dialog Composer](/async-collaboration/comments/customize-ui/comment-dialog/subcomponents/composer/overview) component.
## Default Component
Here's how the default comment composer component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-sub-7.png)
```jsx React / Next.js
... Custom Composer Wirefame
```
```HTML HTML
... custom composer wireframe
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
```jsx
```
```html
```
## Dark Mode
`Default: false`
```jsx
```
```html
```
# Variants
* Define variants for the entire Comment Composer component. This will enable you to show different Composer UI in different parts of your app.
* Alternatively, define a variant for the Comment Dialog component and use it here. This will enable you to show different Comment Dialog UI on the DOM vs here.
* Learn more about how to define and use variants [here](/advanced-customization/variants).
```jsx
```
```html
```
# Comment Standalone Composer
The Comment Standalone Composer enables you to add comments anywhere in your application. It's designed to work seamlessly with other Velt components and APIs:
* [Comment Data APIs](/async-collaboration/comments/customize-behavior/retrieve-comments): Fetch and submit comment data.
* [Comment Thread](/async-collaboration/comments/standalone-components/comment-thread/overview): Render comment threads using fetched data.
* [Comment Pin](/async-collaboration/comments/standalone-components/comment-pin/overview): Display and position comment pins based on fetched data.
By combining these components, you can create custom comment interfaces such as sidebars, overlays, popovers, or inline comments. This approach offers greater flexibility and control over your comment system's design and functionality.
# Setup
## Add Comment Composer Component
The Comment Composer component allows you to add a comment composer anywhere in your app. You can:
* Display it on a hotkey press
* Embed it in your custom sidebar
* Place it below an article
* Overlay it on an image
* and more...
```jsx
```
```html
```
# Overview
The Pin that appears on the DOM when you place a Comment.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default comment pin component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-pin-light.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-pin-all-states-light.png)
```jsx React / Next.js
{/* Ghost Comment Indicator */}
{/* Index */}
{/* Private Comment Indicator */}
{/* Triangle */}
{/* Unread Comment Indicator */}
```
```HTML HTML
```
# Variants
* Define variants for the `Velt Comment Pin` component. This is useful for customizing how the pin looks on different elements like charts, tables, etc.
* Learn more about how to define and use variants [here](/advanced-customization/variants).
```jsx
```
```html
```
# Comment Pin
The Comment Pin component allows you to manually set the position of Comment Annotations.
This feature is particularly useful for complex UIs where you need precise control over the placement of Comment Pins.
Implementing this feature involves:
**1. Adding comment:** You have two options for adding comments with custom positioning:
* **a. Velt-Managed Click Events:** Use the `onCommentAdd` method to incorporate custom metadata into the comment object. This metadata will be used later to position the Comment Pin. [Learn more](/async-collaboration/comments/customize-behavior/event-handlers#oncommentadd-event-data-schema)
* **b. Custom Click Event Handling:** Handle click events on your canvas and use the `addManualComment` method to create a comment with custom metadata. [Learn more](/async-collaboration/comments/customize-behavior/action-methods#3-add-manual-comment)
**2. Retrieving comments data:** Use the `getAllCommentAnnotations` method to fetch all Comment Annotations with their associated custom metadata.
For React developers, we provide hooks for easier integration. [Learn more](/async-collaboration/comments/customize-behavior/retrieve-comments)
**3. Rendering comments Pins:**
Iterate through the retrieved annotations and render the Comment Pin component, using the custom metadata to set each pin's position.
# Setup
You have two options for adding comments with custom positioning metadata:
**Option A: Velt-Managed Click Events**
* Use the `onCommentAdd` prop of the VeltComments component to add custom metadata when a comment is added.
* You need to set the mandatory `commentType: 'manual'` property to the metadata object.
```jsx
yourMethod(event)} />
const yourMethod = (event) => {
event?.addContext({ postion: {x: 200, y: 100}, commentType: 'manual'});
}
```
```js
const veltCommentsTag = document.querySelector('velt-comments');
veltCommentsTag?.addEventListener('onCommentAdd', (event) => {
console.log('*** onCommentAdd ***');
console.log(event.detail);
event.detail?.addContext({ postion: {x: 200, y: 100}, commentType: 'manual'});
});
```
**Option B: Custom Click Event Handling**
* Handle click events on your canvas and use the `addManualComment` method to create a comment with custom metadata.
```jsx
const context = {
postion: {x: 200, y: 100},
};
const commentElement = useCommentUtils();
commentElement.addManualComment({ context });
```
```js
const context = {
postion: {x: 200, y: 100},
};
const commentElement = Velt.getCommentElement();
commentElement.addManualComment({ context: context});
```
Retrieve all `Comment Annotations` using the `useCommentAnnotations()` hook.
To learn more about the `useCommentAnnotations()` hook, [read here](/api-reference/hooks/hooks#usecommentannotations).
```jsx
let commentAnnotations = useCommentAnnotations()
```
```js
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations().subscribe((commentAnnotations) => {
// console.log(commentAnnotations);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
Add the `Velt Comment Pin` component and pass in the `Comment Annotation Id`.
Now retrieve the `context` to retrieve the custom metadata you set earlier and use it to set the position of the `Comment Pin`.
```jsx
let commentAnnotations = useCommentAnnotations()
return (
)
}
```
```html HTML
```
# Customize Behavior
## 1. `onCommentClick` Callback
* When the user clicks on the comment thread, you can listen to it using this method.
* Example: Use this to fetch the context and make the necessary app state changes to navigate to the comment.
```jsx
handleOnCommentClick(data)}
/>
```
```jsx
```
## 2. Pass Comment Annotation Object
* You can pass a Comment Annotation object directly to render the comment thread
* When using annotations from other documents:
* Comments will be read-only
* Reactions and recordings will not be rendered
* This enables creating Kanban boards by fetching comment annotations from multiple documents using our REST APIs
```jsx
```
```html
```
# Overview
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
This component is a thin wrapper around the Comment Dialog component.
## Default Component
Here's how the default comment thread component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-1.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/velt-comment-dialog-2.png)
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
```jsx
```
```html
```
## Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/dark-light-mode.png)
`Default: false`
```js
```
```html
```
# Variants
* Define variants for the entire Comment Thread component. This will enable you to show different Thread UI in different parts of your app.
* Alternatively, define a variant for the Comment Dialog component and use it here. This will enable you to show different Comment Dialog UI on the DOM vs here.
* Learn more about how to define and use variants [here](/advanced-customization/variants).
```jsx
```
```html
```
# Standalone Comment Thread
* You can use the Standalone Comment Thread component to build things such as:
* a kanban board
* your own version of the `Comments Sidebar` component
* This component does not add any additional functionality. It is just used to render existing comment data.
* This is a thin wrapper around the [Comment Dialog component](/async-collaboration/comments/customize-ui/comment-dialog/overview).
* Get the Comment Annotations data using the APIs.
* Use this component to render the Comment Annotations data.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comment-thread)
# Setup
* Retrieve all comment annotations data.
* [Learn more](/async-collaboration/comments/customize-behavior/retrieve-comments).
Using Hooks:
```jsx
let commentAnnotations = useCommentAnnotations()
```
Using API:
```js
const commentElement = client.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations().subscribe((commentAnnotations) => {
// console.log(commentAnnotations);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
```js
const commentElement = Velt.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations().subscribe((commentAnnotations) => {
// console.log(commentAnnotations);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
* Iterate over the `Comment Annotations` array and add the `Velt Comment Thread` component.
* Pass the `annotation Id` prop to set the specific Comment Thread data.
Here's an improved example:
```jsx
let commentAnnotations = useCommentAnnotations()
return (
{commentAnnotations.map((x,i) => )}
)
```
```html
```
```jsx React / Next.js
import { VeltCommentThread, useCommentAnnotations } from '@veltdev/react';
export default function YourDocument() {
let commentAnnotations = useCommentAnnotations()
return (
{commentAnnotations.map((x,i) => )}
)
}
```
```js Other Frameworks
const commentElement = client.getCommentElement();
let subscription = commentElement.getAllCommentAnnotations().subscribe((comments) => {
// If you are using pure html, inject
// In other frameworks, you can loop over the comments inside the template itself
});
//To unsubscribe from the subscription:
subscription?.unsubscribe()
```
# Customize Behavior
## 1. Get Notifications Data
* Get the notifications data for the current user.
* Returns [`Notification[]`](/api-reference/models/Notification)
```jsx
const notificationData = useNotificationsData();
```
```jsx
const notificationElement = client.getNotificationElement();
let subscription = notificationElement.getNotificationsData().subscribe((notifications) => {
console.log("Notifications: ", notifications)
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
```jsx
const notificationElement = Velt.getNotificationElement();
let subscription = notificationElement.getNotificationsData().subscribe((notifications) => {
console.log("Notifications: ", notifications)
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## 2. Take action when a Notification is clicked
* The `onNotificationClick` event fires when a notification is clicked in the Notifications Panel.
* It returns a [`Notification`](/api-reference/models/Notification) object with details about the clicked notification.
* Listen to this event via either the Notification Tool or the Notification Panel, but not both.
* Use this event to implement custom actions in response to notification clicks, such as navigating to a specific part of the app.
```jsx
onNotificationClickEvent(notification)} />
onNotificationClickEvent(notification)} />
const onNotificationClickEvent = (notification) => {
console.log('onNotificationClick: ', notification);
}
```
```jsx
const notificationsTool = document.querySelector('velt-notifications-tool');
notificationsTool?.addEventListener('onNotificationClick', (event) => {
console.log('onNotificationClick from Tool: ', event.detail);
});
const notificationsPanel = document.querySelector('velt-notifications-panel');
notificationsPanel?.addEventListener('onNotificationClick', (event) => {
console.log('onNotificationClick from Panel: ', event.detail);
});
```
## 3. Configure Custom Tabs
* Using this config, you can customize the name of the tabs or disable them altogether.
* By default, all the three tabs are enabled.
You can set it on Notifications Tool:
```jsx
```
You can alternatively set it on Notifications Panel if you have directly embedded it:
```jsx
```
**Using APIs:**
```jsx
const notificationElement = useNotificationUtils();
​​const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Document',
enable: false,
},
"all": {
name: 'Custom All',
enable: true,
},
};
notificationElement.setTabConfig(tabConfig);
```
You can set it on Notifications Tool:
```jsx
```
You can alternatively set it on Notifications Panel if you have directly embedded it:
```jsx
```
**Using APIs:**
```jsx
const notificationElement = client.getNotificationElement();
​​const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Document',
enable: false,
},
"all": {
name: 'Custom All',
enable: true,
},
};
notificationElement.setTabConfig(tabConfig);
```
```js
const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Document',
enable: false,
},
"all": {
name: 'Custom All',
enable: true,
},
};
// Set it using Notifications Tool
const notificationsTool = document.querySelector('velt-notifications-tool');
notificationsTool?.setAttribute("tab-config", JSON.stringify(tabConfig));
// Or, set it using Notifications Panel
const notificationsPanel = document.querySelector('velt-notifications-panel');
notificationsPanel?.setAttribute("tab-config", JSON.stringify(tabConfig));
```
**Using APIs:**
```jsx
const notificationElement = Velt.getNotificationElement();
​​const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Document',
enable: false,
},
"all": {
name: 'Custom All',
enable: true,
},
};
notificationElement.setTabConfig(tabConfig);
```
## 4. Set maximum days for which Notifications should be displayed
Notifications older than the specified number of days will not be displayed.
Default: 15 days.
```jsx
```
**Using API:**
```jsx
const notificationElement = useNotificationUtils();
notificationElement.setMaxDays(15);
```
```jsx
```
**Using API:**
```jsx
const notificationElement = client.getNotificationElement();
notificationElement.setMaxDays(15);
```
```jsx
```
**Using APIs:**
```jsx
const notificationElement = Velt.getNotificationElement();
notificationElement.setMaxDays(15);
```
## 5. Change how the Notifications Panel is opened
Notificaitons Panel opens in one of the following ways:
* `popover`: It opens as a popover on the Notification Tool.
* `sidebar`: It opens as a sidebar from the right edge of the screen.
Default: `popover`.
```jsx
```
```jsx
```
```jsx
```
## 6. Configure visibility of read notifications in the "For You" tab
* You can control whether read notifications are displayed in the "For You" tab. By default, read notifications are removed from this tab.
* This feature allows you to customize the visibility of read notifications in the "For You" tab, providing more flexibility in how notifications are displayed to users.
Default: `false`.
Using Props:
```jsx
```
Using APIs:
```jsx
const notificationElement = useNotificationUtils();
// Enable to keep read notifications in the for you tab
notificationElement.enableReadNotificationsOnForYouTab();
// Disable to hide read notifications in the for you tab
notificationElement.disableReadNotificationsOnForYouTab();
```
Using Props:
```jsx
```
Using APIs:
```jsx
const notificationElement = client.getNotificationElement();
// Enable to keep read notifications in the for you tab
notificationElement.enableReadNotificationsOnForYouTab();
// Disable to hide read notifications in the for you tab
notificationElement.disableReadNotificationsOnForYouTab();
```
Using Props:
```html
```
Using API:
```html
```
## 7. Mark All Notifications as Read
* Mark all notifications as read, either globally or for a specific tab.
* Using 'all' or 'document' as the `tabId` marks all notifications as read across all tabs (equivalent to calling `setAllNotificationsAsRead()` without arguments).
* Using 'for-you' as the `tabId` only marks notifications in the 'for-you' tab as read.
```javascript
const notificationElement = client.getNotificationElement();
// Mark all notifications as read
notificationElement.setAllNotificationsAsRead();
// Mark all notifications as read for a specific tab
notificationElement.setAllNotificationsAsRead({ tabId: 'for-you' });
notificationElement.setAllNotificationsAsRead({ tabId: 'all' });
notificationElement.setAllNotificationsAsRead({ tabId: 'document' });
```
```javascript
const notificationElement = Velt.getNotificationElement();
// Mark all notifications as read
notificationElement.setAllNotificationsAsRead();
// Mark all notifications as read for a specific tab
notificationElement.setAllNotificationsAsRead({ tabId: 'for-you' });
notificationElement.setAllNotificationsAsRead({ tabId: 'all' });
notificationElement.setAllNotificationsAsRead({ tabId: 'document' });
```
## 8. Get Unread Notifications Count
* Retrieve the count of unread notifications, which includes a breakdown for different tabs.
* The 'Document' tab is not included in the response because it contains all the notifications present in the 'All' tab.
**Sample response:**
```javascript
{
forYou: 4, // # of unread notifications in the "For You" tab
all: 5 // # of unread notifications in the "All" or "Document" tab
}
```
Using Hooks:
```jsx
const unreadCount = useUnreadNotificationsCount();
useEffect(() => {
console.log('Unread Count', unreadCount);
}, [unreadCount]);
```
Using API:
```javascript
const notificationElement = client.getNotificationElement();
notificationElement.getUnreadNotificationsCount().subscribe((data) => {
console.log('Unread notifications count:', data);
});
```
```javascript
const notificationElement = Velt.getNotificationElement();
notificationElement.getUnreadNotificationsCount().subscribe((data) => {
console.log('Unread notifications count:', data);
});
```
## 9. Mark a Single Notification as Read
* Mark a single notification as read using its notificationId.
* The notification will be marked as read in all tabs.
```javascript
const notificationElement = client.getNotificationElement();
notificationElement.markNotificationAsReadById("notificationId");
```
```javascript
const notificationElement = Velt.getNotificationElement();
notificationElement.markNotificationAsReadById("notificationId");
```
# Overview
The Notification Panel contains all notifications within the current organization. It appears when you click the notification tool or embed it directly on a page.
We recommend that you familiarize yourselves with [Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-overview.png)
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-overview-breakdown.png)
```jsx React / Next.js
{/* Header with Tabs */}
{/* Main Content */}
{/* For You Tab */}
{/* Documents Tab */}
{/* People Tab */}
{/* All Tab */}
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
```jsx
```
```jsx
```
## Dark Mode
`Default: false`
```js
```
```html
```
# null
Common Content List component used within all the tab content sections. This renders the list of notifications. You can customize it for all tabs using this or customize it individually within each tab.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-item.png)
## Subcomponents that don't support children
* `VeltNotificationsPanelWireframe.Content.List.Item.Avatar`
* `VeltNotificationsPanelWireframe.Content.List.Item.Headline`
* `VeltNotificationsPanelWireframe.Content.List.Item.Body`
* `VeltNotificationsPanelWireframe.Content.List.Item.FileName`
* `VeltNotificationsPanelWireframe.Content.List.Item.Time`
* `velt-notifications-panel-content-list-item-avatar-wireframe`
* `velt-notifications-panel-content-list-item-headline-wireframe`
* `velt-notifications-panel-content-list-item-body-wireframe`
* `velt-notifications-panel-content-list-item-file-name-wireframe`
* `velt-notifications-panel-content-list-item-time-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# null
This contains the main body of the Notifications Panel with the content of each tab.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-content.png)
```jsx React / Next.js
{/* For You Tab */}
{/* Documents Tab */}
{/* People Tab */}
{/* All Tab */}
```
```HTML HTML
```
# null
All Tab content that contains it's notifications.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications_all_tab_content.png)
```jsx React / Next.js
{/* Refer to the Common Content List section for this definition */}
{/* This only renders when there are no unread notifications */}
```
```HTML HTML
```
# null
Document tab content that contains it's notifications.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications_documents_tab_content.png)
## Subcomponents that don't support children
* `VeltNotificationsPanelWireframe.Content.Documents.List.Item.Name`
* `VeltNotificationsPanelWireframe.Content.Documents.List.Item.Count`
* `velt-notifications-panel-content-documents-list-item-name-wireframe`
* `velt-notifications-panel-content-documents-list-item-count-wireframe`
```jsx React / Next.js
{/* Refer to the Common Content List section for this definition */}
{/* This only renders when there are no unread notifications */}
```
```HTML HTML
```
# null
For You tab content that contains it's notifications.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications_for_you_tab_content.png)
```jsx React / Next.js
{/* Refer to the Common Content List section for this definition */}
{/* This only renders when there are no unread notifications */}
```
```HTML HTML
```
# null
People tab content that contains it's notifications.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications_people_tab_content.png)
## Subcomponents that don't support children
* `VeltNotificationsPanelWireframe.Content.People.List.Item.Avatar`
* `VeltNotificationsPanelWireframe.Content.People.List.Item.Name`
* `VeltNotificationsPanelWireframe.Content.People.List.Item.Count`
* `velt-notifications-panel-content-people-list-item-avatar-wireframe`
* `velt-notifications-panel-content-people-list-item-name-wireframe`
* `velt-notifications-panel-content-people-list-item-count-wireframe`
```jsx React / Next.js
{/* Refer to the Common Content List section for this definition */}
{/* This only renders when there are no unread notifications */}
```
```HTML HTML
```
# null
This contains all the tabs that are present in the Notifications Panel Header
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-tab.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
This button marks all notifications as read.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-read-all-button.png)
```jsx React / Next.js
```
```HTML HTML
```
# null
The component that displays the title of the Notification Panel.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notifications-title.png)
```jsx React / Next.js
```
```HTML HTML
```
# Overview
The button that opens or closes the notification panel.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
## Default Component
Here's how the default notification tool component looks like:
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/customization/notification-tool-dark.png)
## Subcomponents that don't support children
* `VeltNotificationsToolWireframe.UnreadCount`
* `velt-notifications-tool-unread-count-wireframe`
```jsx React / Next.js
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
```jsx
```
## Dark Mode
`Default: false`
```js
```
```js
```
# null
You can define and use [variants](/advanced-customization/variants) for the Notification Tool or the Notification Panel.
1. `variant`: For the Notification Tool.
2. `panelVariant`: For the Notification Panel.
```jsx React / Next.js
```
```html HTML
```
# In-app Notifications
There are two components associated with In-app Notifications feature:
* `Velt Notifications Tool`: This opens the Notifications Panel.
* `Velt Notifications Panel`: This shows all the `Notifications` grouped in categories.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=notifications)
# Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/Notification_Dialog_Box.png)
* Go to the [Notifications section](https://console.velt.dev/dashboard/config/notification) in the Configurations section of the Velt Console and enable Notifications.
Notifications will not work if you do not enable this first.
* Place the `Velt Notifications Tool` component wherever you want the Notifications button to appear.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/Notification_Dialog_Box.png)
```jsx
```
```jsx
```
* By default, the Velt Notifications Panel is automatically added or removed when you use the `Velt Notifications Tool`.
* However, if you want to create a dedicated page or dedicated section for Notifications, you can embed the Velt Notifications Panel component directly there.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/Notification_Dialog_Box.png)
```jsx
```
```jsx
```
```jsx React / Next.js
import { VeltNotificationsTool } from '@veltdev/react';
function YourComponent() {
return (
)
}
```
```html HTML
```
# Overview
This component is used to render the reaction tool and all the reactions.
We recommend that you familiarize yourselves with [UI Customization Concepts](/advanced-customization/overview) before attempting to modify any components.
{/* ## Default Component
Here's how the default component looks like: */}
```jsx React / Next.js
{/* This renders the reaction tool */}
{/* This renders the list of reactions added by the users */}
```
```HTML HTML
```
# Styling
## Disable ShadowDOM
* By default, ShadowDOM is used to ensure that your app's CSS does not interfere with the styling of the SDK components.
* Disable the shadow dom to apply your custom CSS to the component.
`Default: true`
### Example
```jsx
```
```jsx
```
## Dark Mode
`Default: false`
Using Props:
```js
```
Using API Methods:
```js
const reactionElement = client.getReactionElement();
reactionElement.enableDarkMode();
reactionElement.disableDarkMode();
```
Using Props:
```html
```
Using API Methods:
```js
const reactionElement = Velt.getReactionElement();
reactionElement.enableDarkMode();
reactionElement.disableDarkMode();
```
# Variants
## Pre-defined Variants
The Inline Reactions has 1 pre-defined variant:
* `inline`: This will customize the default components inside the Inline Reactions Component.
You can define your own variants and use them in different places of your app.
```jsx React / Next.js
```
```jsx HTML
```
# Inline Reactions
This allows users to add emoji reactions to specific parts of your UI (eg: posts, images etc). This is great for quick emotional responses or feedback.
Add `Velt Inline Reactions Section` component to your app to enable Inline Reactions.
[Open in larger window](https://docs-mini-demo.vercel.app/reactions-inline)
# Setup
Import the `VeltInlineReactionsSection` component.
```js
import { VeltInlineReactionsSection } from '@veltdev/react';
```
* Create an element to hold your Inline Reactions component, such as a `div` or `section`.
* Add a unique element `id` to it.
```jsx
Your Article
```
* Add `VeltInlineReactionsSection` component inside your container.
* Add `targetReactionElementId` property to the Velt Inline Reactions component. This needs to match the id you set to the container. This binds the Inline Reactions component with the desired container.
```jsx
Your Article
```
* Create an element to hold your Inline Reactions component, such as a `div` or `section`.
* Add a unique element `id` to it.
```html
Your Article
```
* Add `velt-inline-reactions-section` component inside your container.
* Add `target-reaction-element-id` property to the Velt Inline Reactions component. This needs to match the id you set to the container. This binds the Inline Reactions component with the desired container.
```jsx
Your Article
```
```jsx React / Next.js
import { VeltInlineReactionsSection } from '@veltdev/react';
export default function App() {
return (
Your Article
);
}
```
```html HTML
Your Article
```
# Customize Behavior
## 1. buttonLabel
Sets a custom label for the `Velt Recorder Tool`.
```js
```
```js
```
## 2. enableRecordingCountdown
Controls whether to display a countdown timer before a recording starts.
Default: `enabled`
```js
const recorderElement = client.getRecorderElement();
recorderElement.enableRecordingCountdown();
recorderElement.disableRecordingCountdown();
```
```js
const recorderElement = Velt.getRecorderElement();
recorderElement.enableRecordingCountdown();
recorderElement.disableRecordingCountdown();
```
## 3. enableRecordingTranscription
Controls whether to enable AI transcription for recordings.
Default: `enabled`
```javascript
const recorderElement = client.getRecorderElement();
recorderElement.enableRecordingTranscription();
recorderElement.disableRecordingTranscription();
```
```javascript
const recorderElement = Velt.getRecorderElement();
recorderElement.enableRecordingTranscription();
recorderElement.disableRecordingTranscription();
```
## 4. getRecordingDataByRecorderId
Fetches recording data (transcript, summary, and URLs) for a recording ID and returns a [RecorderData](/api-reference/models/RecorderData) object.
**Using Hook:**
```jsx
const recorderData = useRecordingDataByRecorderId('-O9yTMWmEe5u6YGX8EFV');
useEffect(() => {
console.log('Recorder Data: ', recorderData);
}, [recorderData]);
```
**Using API:**
```jsx
const recorderElement = client.getRecorderElement();
recorderElement.getRecordingDataByRecorderId("-O9yGiYS8lehOXMpQf4j").subscribe((recorderData) => {
console.log('Recorder Data: ', recorderData);
});
```
```javascript
const recorderElement = Velt.getRecorderElement();
recorderElement.getRecordingDataByRecorderId("-O9yGiYS8lehOXMpQf4j").subscribe((recorderData) => {
console.log('Recorder Data: ', recorderData);
});
```
## 5. mode
The `Velt Recorder Control Panel` has two display modes:
* `floating`: Shows a preview in the bottom left corner of the page, regardless of component placement
* `thread`: Displays the component at its placed location in the DOM
`Default: "floating"`
```js
```
```html
```
## 6. onDelete
When a recording is deleted by its creator, the `Velt Recorder Player` component emits an `onDelete` event containing the recorder ID. You can listen for this event to perform any necessary cleanup.
The event returns an object with the following field:
| Field | Type | Description |
| ----- | ------ | ------------------------------ |
| id | string | The ID of the deleted recorder |
```js
yourDeleteMethod(data)} />
```
```js
const recorderPlayer = document.querySelector('velt-recorder-player');
recorderPlayer?.addEventListener('onDelete', (s) => {
console.log('onDelete', s.detail);
});
```
## 7. onRecordedData
The `onRecordedData` callback is triggered when a recording is completed. It provides the following data:
| Field | Type | Description |
| ----- | ------ | --------------------------------------------------------------------------------------------------------------- |
| id | string | Unique identifier for the recording |
| tag | string | HTML tag to embed the recording player (e.g. ``) |
**Using Hooks:**
```jsx
const recorderAddEvent = useRecorderAddHandler();
useEffect(() => {
console.log('recorderAddEvent', recorderAddEvent);
}, [recorderAddEvent]);
```
**Using API:**
```jsx
const recorderElement = client.getRecorderElement();
recorderElement.onRecordedData().subscribe((recorderAddEvent) => {
console.log(recorderAddEvent);
});
```
**Using Event Listener:**
```jsx
const onRecordedData = (recorderAddEvent) => {
console.log(recorderAddEvent);
}
return (
)
```
**Using API:**
```jsx
const recorderElement = Velt.getRecorderElement();
recorderElement.onRecordedData().subscribe((recorderAddEvent) => {
console.log(recorderAddEvent);
});
```
**Using Event Listener:**
```js
const recorderControlPanel = document.querySelector('velt-recorder-control-panel');
recorderControlPanel?.addEventListener('onRecordedData', (s) => {
console.log('onRecordedData', s.detail);
});
```
## 8. summary
Controls whether to display a summary transcript of the recording. When enabled, an AI-generated summary of the recording's content will be shown.
Default: `true`
```jsx
```
```jsx
```
## 9. type
Sets the recording mode for the `Velt Recorder Tool`.
Available modes:
* `all` - Records audio, video and screen
* `audio` - Records audio only
* `video` - Records video only
* `screen` - Records screen only
Default: `audio`
```js
```
```js
```
# Recorder
The Recorder allows your users to create audio, screen, and video recordings.
The Velt Recorder consists of 4 key components:
* **Velt Recorder Notes**: Enables pinning recordings to specific locations on the screen
* **Velt Recorder Tool**: A button to initiate recordings
* **Velt Recorder Control Panel**: Controls for managing active recordings (start/stop/pause)
* **Velt Recorder Player**: Plays back recordings using their unique ID
[Open in larger window](https://recorder-landing-page-demo.vercel.app/?type=all\&backgroundColor=black\&darkMode=true)
[Open in larger window](https://recorder-landing-page-demo.vercel.app/?type=audio\&backgroundColor=black\&darkMode=true)
[Open in larger window](https://recorder-landing-page-demo.vercel.app/?type=video\&backgroundColor=black\&darkMode=true)
[Open in larger window](https://recorder-landing-page-demo.vercel.app/?type=screen\&backgroundColor=black\&darkMode=true)
# null
```jsx
import {
VeltRecorderNotes,
VeltRecorderTool,
VeltRecorderControlPanel,
VeltRecorderPlayer
} from '@veltdev/react'
```
Not needed.
Add the `Velt Recorder Notes` component to the root of your app.
```jsx
```
```html
```
* Add the `Velt Recorder Tool` component wherever you want the recorder button to appear.
* Set the `type` attribute of the `Velt Recorder Tool` component to one of the following:
* `all`
* `audio`
* `video`
* `screen`
```js
```
```html
```
* Add the `Velt Recorder Control Panel` component wherever you want the control panel to appear.
* When a user clicks on the `Velt Recorder Tool` button, the `Velt Recorder Control Panel` component will show the recording preview with options to save, pause, or delete the recording.
* Set the `mode` attribute on the `VeltRecorderControlPanel` component to either `floating` (default) or `thread`.
To learn more about `floating` or `thread` mode [read here](/async-collaboration/recorder/customize-behavior/velt-recorder-control-panel).
```js
```
```html
```
* Add the `Velt Recorder Player` component wherever you want to show recordings.
* The `Velt Recorder Player` component displays the recorded data with controls such as pause, play and delete.
* Set the `recorderId` of the `Velt Recorder Player` component as the Id of the recording you are trying to show.
```js
```
```html
```
```js React / Next.js
import { VeltRecorderTool, VeltRecorderControlPanel, VeltRecorderPlayer } from '@veltdev/react'
function TaskInputBox() {
return (
{/* some component that holds your recording components */}
{/* Put this where you want the Recorder button to appear */}
{/* Put this where you want the Recorder control panel to appear */}
{/* some component that holds your video player */}
{/* Put this where you want to show the recorded data */}
)
}
```
```html HTML
```
# Customize Behavior
## Customize for `Location` specific tracking
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
import { useUniqueViewsByDate, useUniqueViewsByUser } from '@veltdev/react';
// to get document level views by date
const viewsByDate = useUniqueViewsByDate();
// to get location level views by date
const viewsByDateForLocation = useUniqueViewsByDate('your-location-id');
// to get document level views by user
const viewsByUser = useUniqueViewsByUser();
// to get location level views by user
const viewsByUserForLocation = useUniqueViewsByUser('your-location-id');
```
If you want to specify `Location` specific tracking, you can add two additional properties:
* type - this should be either 'document' or 'location' (default: 'location')
* location-id - if type is 'location', you can provide a location id here
```jsx
```
API Methods:
```jsx
const viewsElement = client.getViewsElement();
// to get unique views by user
let subscription = viewsElement.getUniqueViewsByUser().subscribe((viewsByUser) => {
console.log('Unique views by user: ', viewsByUser);
});
// you can optionally pass client-location-id to get unique views by users for that location
let subscription = viewsElement.getUniqueViewsByUser('your-location-id').subscribe((viewsByUser) => {
console.log('Unique views by date for location: ', viewsByUser);
});
// to get unique views by date
let subscription = viewsElement.getUniqueViewsByDate().subscribe((viewsByDate) => {
console.log('Unique views by date: ', viewsByDate);
});
// you can optionally pass client-location-id to get unique views by date for that location
let subscription = viewsElement.getUniqueViewsByDate('your-location-id').subscribe((viewsByDate) => {
console.log('Unique views by date for location: ', viewsByDate);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
If you want to specify `Location` specific tracking, you can add two additional properties:
* type - this should be either 'document' or 'location' (default: 'location')
* location-id - if type is 'location', you can provide a location id here
```js
```
API Methods:
```jsx
const viewsElement = client.getViewsElement();
// to get unique views by user
let subscription = viewsElement.getUniqueViewsByUser().subscribe((viewsByUser) => {
console.log('Unique views by user: ', viewsByUser);
});
// you can optionally pass client-location-id to get unique views by users for that location
let subscription = viewsElement.getUniqueViewsByUser('your-location-id').subscribe((viewsByUser) => {
console.log('Unique views by date for location: ', viewsByUser);
});
// to get unique views by date
let subscription = viewsElement.getUniqueViewsByDate().subscribe((viewsByDate) => {
console.log('Unique views by date: ', viewsByDate);
});
// you can optionally pass client-location-id to get unique views by date for that location
let subscription = viewsElement.getUniqueViewsByDate('your-location-id').subscribe((viewsByDate) => {
console.log('Unique views by date for location: ', viewsByDate);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
# View Analytics
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=view-analytics)
# Setup
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/Trends.png)
Import the `VeltViewAnalytics` component
```jsx
import { VeltViewAnalytics } from '@veltdev/react';
```
Place the `VeltViewAnalytics` component wherever you want the View Analytics component to appear.
```jsx
```
Place the `` component wherever you want the View Analytics component to appear.
```jsx
```
```js React / Next.js
import { VeltViewAnalytics } from '@veltdev/react';
function YourComponent() {
return (
)
}
```
```html HTML
Collaboration App
```
# null
## Getting Started
Set up Velt with a fresh App
Integrate Velt with an existing app
## Popular Features
Enable contextual discussions within your app
Notify users about comment updates or add custom app notifications
Loom-style audio, video & screen recording with AI transcription
Slack-style huddle calls within your app
## Async Collaboration
Leave a comment anywhere
A sidebar that holds your comments
Notification center in your app
Embeddable audio, video & screen recording
Frame.io style comments on video
Leave comments using an API
Add reactions to components
Show which users viewed a document
Point at what is important
## Realtime Collaboration
Sync the state of your app realtime
Lock access to a single editor
See who else is browsing online
See where everyone else is browsing
Let others follow along your screen
Hop on a group call with everyone
See what others are editing
## Advanced
Call a webhook when a comment is added
Set up email notifications
Retrieve or modify data
Fully customize Velt Components
# Quickstart
Quickstart for React. For other frameworks like `vue`, `angular`, `svelte`, `vanilla js` etc. check out the setup guide.
Create a new React app [using any method](https://react.dev/learn/start-a-new-react-project) you prefer.
{/* ```bash Terminal
npx create-next-app@latest
``` */}
```bash Terminal
cd my-app && npm install @veltdev/react
```
If you're using TypeScript, you can install the types package.
```bash Terminal
npm install --save-dev @veltdev/types
```
Go to [console.velt.dev](https://console.velt.dev) and grab your Velt API Key
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-api-key.png)
In the Velt console, add the URL where your app is deployed to the list of Managed Domains.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-add-website.png)
In **App.js**, add the `VeltProvider` component to the root of your app with your Velt API Key.
```js App.js
```
In **YourAuthComponent.js**, use the `useIdentify()` hook from the Velt SDK to identify your user.
```js YourAuthComponent.js
import { useIdentify } from '@veltdev/react';
useIdentify(user)
```
Make sure to call `useIdentify()` within a child component of the Velt Provider. Otherwise, it will not work.
In **YourDocument.js**, use the `useSetDocumentId()` hook from the Velt SDK to set the Document ID.
```js YourDocument.js
import { useSetDocumentId } from '@veltdev/react';
useSetDocumentId("my-document-id")
```
In **App.js**, add `VeltComments` to enable the `Comments` functionality.
```js App.js
```
In **YourDocument.js**, add the `VeltCommentTool` and `VeltPresence` components to test out the `Comments` and `Presence` functionality.
```js YourDocument.js
```
### Comments
* Click the `VeltCommentTool` button, then hover over any element on the page to leave a comment.
* Click the `VeltCommentTool` button, then try to draw a box on the page to leave a comment.
* You can also highlight any text to leave a comment.
### Presence
* Open two browser tabs side by side with one in Incognito mode. You should see a bubble showing the other browser's profile avatar pop up.
Fork this repo if you want all the steps above done for you. You will still need to use your own Velt API Key.
[Github Repo](https://github.com/Snippyly-Docs/velt-quickstart-hooks-demo)
[CodeSandBox Link](https://codesandbox.io/embed/github/Snippyly-Docs/velt-quickstart-hooks-demo?fontsize=14\&hidenavigation=1\&module=%2Fsrc%2FApp.tsx\&theme=dark\&view=editor)
[View Demo in Larger Window](https://velt-quickstart-hooks-demo.vercel.app/)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/Snippyly-Docs/velt-quickstart-hooks-demo)
Check out this guide on [how to set up Velt with your existing app](/get-started/setup/install)
```jsx App.js
import { VeltProvider, VeltComments, VeltPresence } from '@veltdev/react';
import YourAuthComponent from './YourAuthComponent';
import YourDocument from './YourDocument';
export default function App() {
return (
);
}
```
```jsx YourAuthComponent.js
import { useIdentify} from "@veltdev/react";
import { useState } from "react";
export default function YourAuthComponent() {
const userService = () => {
return {
uid: "user1",
displayName: "User 1",
email: "user1@velt.dev",
photoURL: "https://i.pravatar.cc/301"
};
};
// Fetch user data from user service
let yourAuthenticatedUser = userService();
const { uid, displayName, email, photoURL } = yourAuthenticatedUser;
// Create the Velt user object
let veltUser = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
};
//identify Velt user
useIdentify(veltUser)
let [user,setUser] = useState(veltUser)
return
User: {user?.userId}
;
}
```
```jsx YourDocument.js
import { useSetDocumentId, VeltCommentTool, VeltPresence } from '@veltdev/react';
import { useEffect, useState } from 'react';
export default function YourDocument() {
useSetDocumentId('my-document-id')
return (
);
}
```
# Advanced Setup Options
## Advanced Set Up options
This section includes a list of optional advanced set up options.
## Location
Users logged into the same **Document** ID can see each other's `Presence`, `Cursors`, `Comments` etc.
However, if you want to add another layer of categorization to organize users together, you can use **Location**.
If a **Document** is like a house, a **Location** is like a room within the house.
To learn more about `Locations`, check out its dedicated section [here](/key-concepts/overview#locations).
## Contacts
When you reply to a comment, you can `@mention` other teammates that are added to a `User's Contacts List`.
To learn more about creating a `User's Contacts List`, [read here](/key-concepts/users/contact-list).
## Detect if Velt SDK is initialized
To detect if the Velt SDK is initialized, subscribe using the following method:
```jsx
let subscription = client.getVeltInitState().subscribe((veltInitState: boolean | undefined) => {
console.log('Velt Init State:', veltInitState);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can also the use `useVeltInitState()` hook:
```jsx
import { useVeltInitState } from '@veltdev/react';
function YourComponent() {
const veltInitState = useVeltInitState();
useEffect(() => {
console.log('Velt Init State:', veltInitState);
if (veltInitState) {
// Velt state is initialized, so user can perform any action here
}
}, [veltInitState]);
}
```
## Advanced Set Up options
This section includes a list of optional advanced set up options.
## Location
Users logged into the same **Document** ID can see each other's `Presence`, `Cursors`, `Comments` etc.
However, if you want to add another layer of categorization to organize users together, you can use **Location**.
If a **Document** is like a house, a **Location** is like a room within the house.
To learn more about `Locations`, check out its dedicated section [here](/key-concepts/overview#locations).
## Contacts
When you reply to a comment, you can `@mention` other teammates that are added to a `User's Contacts List`.
To learn more about creating a `User's Contacts List`, [read here](/key-concepts/users/contact-list).
## Detect if Velt SDK is initialized
To detect if the Velt SDK is initialized, subscribe using the following method:
```jsx
let subscription = client.getVeltInitState().subscribe((veltInitState: boolean | undefined) => {
console.log('Velt Init State:', veltInitState);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## Advanced Set Up options
This section includes a list of optional advanced set up options.
## Location
Users logged into the same **Document** ID can see each other's `Presence`, `Cursors`, `Comments` etc.
However, if you want to add another layer of categorization to organize users together, you can use **Location**.
If a **Document** is like a house, a **Location** is like a room within the house.
To learn more about `Locations`, check out its dedicated section [here](/key-concepts/overview#locations).
## Contacts
When you reply to a comment, you can `@mention` other teammates that are added to a `User's Contacts List`.
To learn more about creating a `User's Contacts List`, [read here](/key-concepts/users/contact-list).
## Detect if Velt SDK is initialized
To detect if the Velt SDK is initialized, subscribe using the following method:
```jsx
let subscription = Velt.getVeltInitState().subscribe((veltInitState: boolean | undefined) => {
console.log('Velt Init State:', veltInitState);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## Advanced Set Up options
This section includes a list of optional advanced set up options.
## Location
Users logged into the same **Document** ID can see each other's `Presence`, `Cursors`, `Comments` etc.
However, if you want to add another layer of categorization to organize users together, you can use **Location**.
If a **Document** is like a house, a **Location** is like a room within the house.
To learn more about `Locations`, check out its dedicated section [here](/key-concepts/overview#locations).
## Contacts
When you reply to a comment, you can `@mention` other teammates that are added to a `User's Contacts List`.
To learn more about creating a `User's Contacts List`, [read here](/key-concepts/users/contact-list).
## Detect if Velt SDK is initialized
To detect if the Velt SDK is initialized, subscribe using the following method:
```jsx
let subscription = this.client.getVeltInitState().subscribe((veltInitState: boolean | undefined) => {
console.log('Velt Init State:', veltInitState);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## Advanced Set Up options
This section includes a list of optional advanced set up options.
## Location
Users logged into the same **Document** ID can see each other's `Presence`, `Cursors`, `Comments` etc.
However, if you want to add another layer of categorization to organize users together, you can use **Location**.
If a **Document** is like a house, a **Location** is like a room within the house.
To learn more about `Locations`, check out its dedicated section [here](/key-concepts/overview#locations).
## Contacts
When you reply to a comment, you can `@mention` other teammates that are added to a `User's Contacts List`.
To learn more about creating a `User's Contacts List`, [read here](/key-concepts/users/contact-list).
## Detect if Velt SDK is initialized
To detect if the Velt SDK is initialized, subscribe using the following method:
```jsx
let subscription = client.getVeltInitState().subscribe((veltInitState: boolean | undefined) => {
console.log('Velt Init State:', veltInitState);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
# 2. Authenticate
Autheticate your logged in users with the SDK.
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that handles authentication.
Import the `useIdentify` hook.
```js
import { useIdentify } from '@veltdev/react'
```
Create a Velt `User` object.
```js
// Fetch the relevant user info from `yourAuthenticatedUser`
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
```
To enable `@mention` in the comments, you need to pass the user's contacts. Learn more about how it works [here](https://docs.velt.dev/users/add-user-contacts/setup).
Call the `useIdentify()` hook and pass in the Velt `User` object.
```js
useIdentify(user);
```
The `useIdentify()` method is asynchronous.You must call `useIdentify` within a child component of the `VeltProvider`, or else it will not work.Provide an initial within the user object. If the initial is not provided in the identify call, then we will automatically create it using the name.
The second parameter of the `useIdentify()` method is an optional configuration object that has a `JWT Token` as a field.
This can be used to add an additional layer of security to prevent user impersonation.
```js
useIdentify(user, {
authToken: authToken,
});
```
We will use the `email` address and `organizationId` passed in the identify call to validate the user later to prevent unauthorized access.
See [JWT Tokens](/security/jwt-tokens) for more information on how to generate a `JWT Token` with the Velt SDK.
`Default: false`
By default when you identify a **User**, we maintain the user auth in the browser unless you explicitly sign out the logged in user.
If you are changing a User's access or any metadata and want those changes to be reflected immediately,
then you should re-call the `identify` method with `forceReset` option set to `true`.
```js
useIdentify(user, {
forceReset: true
});
```
We recommend following the setup guide that uses `React / Next.js with Hooks` for a cleaner experience.It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that handles authentication.
Import the `useVeltClient` React hook. You can use this hook within your
component to fetch the Velt client.
```js
import { useVeltClient } from '@veltdev/react';
```
```js
const { client } = useVeltClient();
```
The code in the following steps will go inside this `useEffect` hook.
```js
useEffect(() => {
if (client && yourAuthenticatedUser) {
// Fetch the relevant user info from your authenticated user object.
}
}, [client, yourAuthenticatedUser]);
```
Create a Velt `User` object by taking the relevant fields from `yourAuthenticatedUser`.
```js
// Fetch the relevant user info from `yourAuthenticatedUser`
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
```
To enable `@mention` in the comments, you need to pass the user's contacts. Learn more about how it works [here](https://docs.velt.dev/users/add-user-contacts/setup).
Call the `identify()` method and pass in the Velt `User` object.
```js
await client.identify(user);
```
The `client.identify()` method is asynchronous.You must call `client.identify` within a child component of the `VeltProvider`, or else it will not work.Provide an initial within the user object. If the initial is not provided in the identify call, then we will automatically create it using the name.
The second parameter of the `client.identify()` method is an optional configuration object that has a `JWT Token` as a field.
This can be used to add an additional layer of security to prevent user impersonation.
```js
await client.identify(user, {
authToken: authToken,
});
```
We will use the `email` address and `organizationId` passed in the identify call to validate the user later to prevent unauthorized access.
See [JWT Tokens](/security/jwt-tokens) for more information on how to generate a `JWT Token` with the Velt SDK.
`Default: false`
By default when you identify a **User**, we maintain the user auth in the browser unless you explicitly sign out the logged in user.
If you are changing a User's access or any metadata and want those changes to be reflected immediately,
then you should re-call the `identify` method with `forceReset` option set to `true`.
```js
await client.identify(user, {
forceReset: true
});
```
Create a Velt `User` object.
```js
// Fetch the relevant user info from `yourAuthenticatedUser`
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
```
To enable `@mention` in the comments, you need to pass the `User's` contacts. Learn more about how it works [here](https://docs.velt.dev/users/add-user-contacts/setup).
Call this function in the component where you authenticate your `Users` once your Velt client and your `User` object is available.
If your `.js` files are all in one file, you will need to include the `.js` file on every html page you want the features to be enabled on.
Make sure you pass the `User` with the fields defined in the `User` object or refer to the example below.
```js
await Velt.identify(yourLoggedInUser)
```
The `Velt.identify()` method is asynchronousYou must call `client.identify` within a child component of the `VeltProvider`, or else it will not work.Provide an initial within the user object. If the initial is not provided in the identify call, then we will automatically create it using the name.
The second parameter of the `client.identify()` method is an optional configuration object that has a `JWT Token` as a field.
This can be used to add an additional layer of security to prevent user impersonation.
```js
await Velt.identify(user, {
authToken: authToken,
});
```
We will use the `email` address and `organizationId` passed in the identify call to validate the user later to prevent unauthorized access.
See [JWT Tokens](/security/jwt-tokens) for more information on how to generate a `JWT Token` with the Velt SDK.
`Default: false`
By default when you identify a **User**, we maintain the user auth in the browser unless you explicitly sign out the logged in user.
If you are changing a User's access or any metadata and want those changes to be reflected immediately,
then you should re-call the `identify` method with `forceReset` option set to `true`.
```js
await Velt.identify(user, {
forceReset: true
});
```
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that handles authentication.
Create a Velt User object.
```jsx
// Fetch the relevant user info from `yourAuthenticatedUser`
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
```
```jsx
this.client.identify(user);
```
The `this.client.identify()` method is asynchronous.Provide an initial within the user object. If the initial is not provided in the identify call, then we will automatically create it using the name.
The second parameter of the `useIdentify()` method is an optional configuration object that has a `JWT Token` as a field.
This can be used to add an additional layer of security to prevent user impersonation.
```js
this.client.identify(user, {
authToken: authToken,
});
```
We will use the `email` address and `organizationId` passed in the identify call to validate the user later to prevent unauthorized access.
See [JWT Tokens](/security/jwt-tokens) for more information on how to generate a `JWT Token` with the Velt SDK.
`Default: false`
By default when you identify a **User**, we maintain the user auth in the browser unless you explicitly sign out the logged in user.
If you are changing a User's access or any metadata and want those changes to be reflected immediately,
then you should re-call the `identify` method with `forceReset` option set to `true`.
```js
this.client.identify(user, {
forceReset: true
});
```
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that handles authentication.
Create a Velt User object.
```jsx
// Fetch the relevant user info from `yourAuthenticatedUser`
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
```
```jsx
client.identify(user);
```
The `client.identify()` method is asynchronous.You must call `client.identify()` within a child component of the `VeltProvider`, or else it will not work.Provide an initial within the user object. If the initial is not provided in the identify call, then we will automatically create it using the name.
The second parameter of the `useIdentify()` method is an optional configuration object that has a `JWT Token` as a field.
This can be used to add an additional layer of security to prevent user impersonation.
```js
client.identify(user, {
authToken: authToken,
});
```
We will use the `email` address and `organizationId` passed in the identify call to validate the user later to prevent unauthorized access.
See [JWT Tokens](/security/jwt-tokens) for more information on how to generate a `JWT Token` with the Velt SDK.
`Default: false`
By default when you identify a **User**, we maintain the user auth in the browser unless you explicitly sign out the logged in user.
If you are changing a User's access or any metadata and want those changes to be reflected immediately,
then you should re-call the `identify` method with `forceReset` option set to `true`.
```js
client.identify(user, {
forceReset: true
});
```
```js React / Next.js with Hooks
//Warning: Make sure this is a child component to VeltProvider
//and not within the same file where VeltProvider is placed.
// 1) Import the useIdentify hook
import { useIdentify } from '@veltdev/react';
export default function YourAuthComponent() {
const userService = () => {
return {
uid:"123",
displayName:"Bob",
email:"bob@gmail.com",
photoURL:'https://i.pravatar.cc/300',
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId
}
}
let yourAuthenticatedUser = userService()
// 2) Fetch the relevant User info from yourAuthenticatedUser
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
//3) Pass the user object to the SDK
useIdentify(user)
return (
// Your auth component template
);
}
```
```js React / Next.js
//Warning: Make sure this is a child component to VeltProvider
//and not within the same file where VeltProvider is placed.
// 1) Get the Velt Client
import { useVeltClient } from '@veltdev/react';
import { useEffect } from 'react';
export default function YourAuthComponent() {
const userService = () => {
return {
uid:"123",
displayName:"Bob",
email:"bob@gmail.com",
photoURL:'https://i.pravatar.cc/300',
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId
}
}
let yourAuthenticatedUser = userService()
const { client } = useVeltClient();
// 2) Create a useEffect hook
useEffect(() => {
const initVelt = async () => {
if (client && yourAuthenticatedUser) {
// 3) Fetch the relevant user info from yourAuthenticatedUser
const { uid, displayName, email, photoURL, organizationId, colorCode } = yourAuthenticatedUser;
// Create the Velt user object
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: organizationId // this is the organization id the user belongs to
};
//4) Pass the user object to the SDK
await client.identify(user)
}
}
initVelt().catch(console.error)
}, [client, yourAuthenticatedUser]);
return (
// Your auth component template
);
}
```
```html HTML
Collaboration App
```
```jsx Angular
import { Component } from '@angular/core';
import { initVelt } from '@veltdev/client';
import { Velt } from '@veltdev/types';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
client?: Velt;
constructor() {
this.initVelt();
}
// Initialize velt sdk
async initVelt() {
this.client = await initVelt('YOUR_APIKEY');
this.setUser();
}
// login with your user in velt
setUser() {
if (this.client) {
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Use valid Hex code value. Used in the background color of the user's avatar.
textColor: textColor, // Use valid Hex code value. Used in the text color of the user's intial when photoUrl is not present.
organizationId: 'organizationId123' //(optional) If you want to categorize the user into an organization
}; // Your user object here
this.client.identify(user);
}
}
}
```
```html Vue.js
```
# 3. Initialize Document
A **Document** represents a shared collaborative space where users can interact. Documents live inside the Organization.
Learn more about documents [here](/key-concepts/documents).
The Set Document method takes two parameters:
* `documentId`: The unique identifier for the document.
* `metadata`: (optional) This is a key/value pair object where you can set metadata about the document such as `documentName`. documentName is a special field that we use to display the document name in some Velt Components.
The SDK will not work without this call.
## 1. Initialize Document for the current Organization
* By default, users can only access documents within their own organization. Use this to set the document for the current organization the user is logged into.
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that represents your document.
```jsx
import { useSetDocument } from '@veltdev/react';
useSetDocument('unique-document-id', {documentName: 'Document Name'});
```
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that represents your document.
```jsx
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
}, [client]);
```
```jsx
if(Velt){
Velt.setDocument('unique-document-id', {documentName: 'Document Name'});
}
```
```jsx
if (this.client) {
this.client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
```
```jsx
if (client) {
client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
```
## 2. Initialize Document for a different Organization
* Use this to access a document from an organization different than the one the user is logged into.
* You can enable cross-organization access by specifying the `organizationId` of the target document in the document metadata.
* Ensure that the user has access to the target document in the target organization.
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that represents your document.
```jsx
import { useSetDocument } from '@veltdev/react';
useSetDocument('unique-document-id', {organizationId: 'ANOTHER_ORGANIZATION_ID'});
```
It is critical that you do the following steps within a child component and not within the same root component where you placed the VeltProvider.Realistically, these steps should be done inside your component that represents your document.
```jsx
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.setDocument('unique-document-id', {organizationId: 'ANOTHER_ORGANIZATION_ID'});
}
}, [client]);
```
```jsx
if(Velt){
Velt.setDocument('unique-document-id', {organizationId: 'ANOTHER_ORGANIZATION_ID'});
}
```
```jsx
if (this.client) {
this.client.setDocument('unique-document-id', {organizationId: 'ANOTHER_ORGANIZATION_ID'});
}
```
```jsx
if (client) {
client.setDocument('unique-document-id', {organizationId: 'ANOTHER_ORGANIZATION_ID'});
}
```
```js React / Next.js with Hooks
// 1) Create a component that will represent your document
//Warning: Make sure this is a child component to VeltProvider
//and not within the same file where VeltProvider is placed.
// 2) Import the useSetDocument hook
import { useSetDocument } from '@veltdev/react';
export default function YourDocument() {
// 3) Set a document ID
useSetDocument('unique-document-id', {documentName: 'Document Name'});
return (
//your document template - add Velt Components here
);
}
```
```js React / Next.js
// 1) Create a component that will represent your document
//Warning: Make sure this is a child component to VeltProvider
//and not within the same file where VeltProvider is placed.
// 2) Get the Velt client
import { useVeltClient } from '@veltdev/react';
import { useEffect, useState } from 'react';
export default function YourDocument() {
const { client } = useVeltClient();
// 3) Create a useEffect hook
useEffect(() => {
if (client) {
// 4) Set a document ID
client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
}, [client]);
return (
//your document template - add Velt Components here
);
}
```
```html HTML
Collaboration App
//your document template - add Velt Components here
```
```jsx Angular
import { Component } from '@angular/core';
import { initVelt } from '@veltdev/client';
import { Velt } from '@veltdev/types';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
client?: Velt;
constructor() {
this.initVelt();
}
// Initialize velt sdk
async initVelt() {
this.client = await initVelt('YOUR_APIKEY');
this.setDocument();
}
// set document in velt
setDocument() {
if (this.client) {
this.client.setDocument('YOUR_DOCUMENT_ID', {documentName: 'Document Name'});
}
}
}
// Your HTML File //
// to add comments (ideally add to root component ex: AppComponent)
// comment tool
// comment sidebar
// Add other feature tags ...
```
```html Vue.js
... your html
... Add other velt elements as required
```
# 1. Install
Steps to integrate Velt into an existing app
npm:
```bash
npm install @veltdev/react
```
yarn:
```bash
$ yarn add @veltdev/react
```
If you're using TypeScript, you can install the types package.
```bash
npm install --save-dev @veltdev/types
```
Go to [console.velt.dev](https://console.velt.dev) and grab your Velt API Key
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-api-key.png)
In the Velt console, add the URL where your app is deployed to the list of Managed Domains.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-add-website.png)
Add the VeltProvider component to the root of your app.
Add your Velt API key.
```js
import { VeltProvider } from '@veltdev/react';
```
```js
```
We recommend following the setup guide that uses `React / Next.js with Hooks` for a cleaner experience.
npm:
```bash
npm install @veltdev/react
```
yarn:
```bash
$ yarn add @veltdev/react
```
If you're using TypeScript, you can install the types package.
```bash
npm install --save-dev @veltdev/types
```
Go to [console.velt.dev](https://console.velt.dev) and grab your Velt API Key
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-api-key.png)
In the Velt console, add the URL where your app is deployed to the list of Managed Domains.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-add-website.png)
Add the VeltProvider component to the root of your app.
Add your Velt API key.
```js
import { VeltProvider } from '@veltdev/react';
```
```js
```
Go to [console.velt.dev](https://console.velt.dev) and grab your Velt API Key
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-api-key.png)
In the Velt console, add the URL where your app is deployed to the list of Managed Domains.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-add-website.png)
```html
```
Put this in your root app script:
```js
async function loadVelt() {
await Velt.init("YOUR_VELT_API_KEY");
}
```
```jsx
npm i @veltdev/client
```
If you are using Typescript, install the types library:
```jsx
npm i @veltdev/types --save-dev
```
Go to [console.velt.dev](https://console.velt.dev) and grab your Velt API Key
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-api-key.png)
In the Velt console, add the URL where your app is deployed to the list of Managed Domains.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-add-website.png)
Add `schemas: [CUSTOM_ELEMENTS_SCHEMA]` to your App Module:
```jsx
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Add this line
})
export class AppModule { }
```
```jsx
import { initVelt } from '@veltdev/client';
```
```jsx
this.client = await initVelt('YOUR_APIKEY');
```
```jsx
npm i @veltdev/client
```
If you are using Typescript, install the types library:
```jsx
npm i @veltdev/types --save-dev
```
Go to [console.velt.dev](https://console.velt.dev) and grab your Velt API Key
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-api-key.png)
In the Velt console, add the URL where your app is deployed to the list of Managed Domains.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/velt-console-add-website.png)
In main.js, add the following code to allow Velt elements in your Vue app:
```html
Vue.config.ignoredElements = [
/velt-*/
]
```
```jsx
import { initVelt } from '@veltdev/client';
```
```jsx
client = await initVelt("YOUR_APIKEY");
```
```jsx React / Next.js with Hooks
import { VeltProvider } from '@veltdev/react';
export default function App() {
return (
);
}
```
```jsx React / Next.js
import { VeltProvider } from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
Collaboration App
```
```jsx Angular
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA], // Add this line
})
export class AppModule { }
```
```html Vue.js
Vue.config.ignoredElements = [
/velt-*/
]
```
# AG Grid
## How to add Velt attributes to AG Grid cells?
Some Velt features like live selection require adding Velt specific attributes to the cell elements. Here's how you can do it:
1. In your AG Grid column definitions, use the `cell renderer` property to customize the cell rendering.
2. Within the `cell renderer`, add the necessary Velt attributes to the cell element.
Here's a code sample that shows how to add the `data-velt-live-selection-enabled` attribute and other required attributes to AG Grid cell div tags:
```jsx
colDefs: ColDef[] = [
{
field: "FIELD_NAME",
cellRenderer: (params: any) => {
// Let AG Grid render the default cell, then modify the outer div
setTimeout(() => {
const cellElement = params.eGridCell;
// Add Velt attributes to the parent cell div
cellElement.setAttribute('data-velt-live-selection-enabled', 'true');
// Add other Velt attributes as required
}, 0); // Timeout to wait for the DOM to be ready
return params.value; // Use the default renderer, which is the text value
},
editable: true, // Set to true if the column is editable
},
];
```
# null
### Overview
Access Control is maintained at two levels:
1. Organization level
2. Document level
### 1. Organization Level
Here are some properties of Organization level access control:
* Access to an Organization can be granted or revoked by adding or removing a User from an organization. A user can be added to multiple organizations but can only log in to one organization at a time.
* By default all organization users have access to all organization data including documents, locations and user contacts.
* Access to documents can be restricted by setting permissions at the document level. (more below)
### 2. Document Level
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/document-access.png)
Here are some properties of Organization level access control:
* A document has three types of access:
* `public`: `default` Any user who logs into the given document id can access the document whether or not they are part of the organization or the document.
* `organizationPrivate`: Only all organization users have access to the document.
* `restricted`: Only users explicitly added to the document have access. Organization users not explicitly added will not have access.
* Access to a Document can be granted or revoked by adding or removing a User from a document. Document level access overrides organization level access. Eg: if a document is restricted, only users explicitly added to the document will have access to it.
* Access can also be granted or revoked by adding or removing organization user groups. `(coming soon)`
* Organization users can access the entire Organization contact list in the Document.
* Guest (Non-organization) users who have access to the document cannot access the Organization contact list. If you want to show some organization contacts to these guest users, then you need to explicitly add those contacts to the document.
# Set Document
A **Document** represents a shared collaborative space where users can interact. Documents live inside the Organization.
Documents contain:
* All feature data (e.g., Comments, Presence, Cursors, etc.).
* Locations
* Users: These are different from Organization Users. (more details in Access Control section)
Users logged into the same `Document ID` can see each other's `Presence`, `Cursors`, `Comments` etc.
For example, in a slide presentation application, the entire slide deck is a document.
Hooks currently do not support automatic change detection in variables.
## 1. Setting a Document
* Use this to initialize a Document.
* It takes two parameters:
* `documentId`: The unique identifier for the document.
* `metadata`: (optional) This is a key/value pair object where you can set metadata about the document such as `documentName`. documentName is a special field that we use to display the document name in some Velt Components.
The SDK will not work without this call.
```jsx
useSetDocument('unique-document-id', {documentName: 'Document Name'});
```
```jsx
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
}, [client]);
```
```jsx
if(Velt){
Velt.setDocument('unique-document-id', {documentName: 'Document Name'});
}
```
```jsx
this.client.setDocument('unique-document-id', {documentName: 'Document Name'});
```
```jsx
client.setDocument('unique-document-id', {documentName: 'Document Name'});
```
## 2. Unset a Document
* Use this to unset a Document.
* For some parts of your app, you may not need Velt. In such cases, you can unset the document.
```jsx
useUnsetDocumentId();
```
```jsx
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.unsetDocumentId();
}
}, [client]);
```
```jsx
Velt.unsetDocumentId();
```
```jsx
this.client.unsetDocumentId();
```
```jsx
client.unsetDocumentId();
```
## 3. Get Document Metadata
* Use this to get the metadata of a Document.
* This is useful when you want to display the document name in your app or any custom metadata that you have set.
* This returns a subscription with [`DocumentMetadata`](/api-reference/models/DocumentMetadata) object.
{/*
```jsx
useUnsetDocumentId();
```
*/}
```jsx
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.getDocumentMetadata().subscribe((documentMetadata) => {
console.log("Current document metadata: ", documentMetadata);
});
}
}, [client]);
```
```jsx
Velt.getDocumentMetadata().subscribe((documentMetadata) => {
console.log("Current document metadata: ", documentMetadata);
});
```
```jsx
this.client.getDocumentMetadata().subscribe((documentMetadata) => {
console.log("Current document metadata: ", documentMetadata);
});
```
```jsx
client.getDocumentMetadata().subscribe((documentMetadata) => {
console.log("Current document metadata: ", documentMetadata);
});
```
## 4. Access Documents from Other Organizations
* By default, users can only access documents within their own organization.
* Enable cross-organization access by specifying the `organizationId` of the target document in the document metadata.
* Ensure that the user has access to the target document in the target organization.
**Using Hook:**
```jsx
useSetDocument(DOCUMENT_ID, {
organizationId: 'ANOTHER_ORGANIZATION_ID'
});
```
**Using API:**
```jsx
client.setDocument(DOCUMENT_ID, {
organizationId: 'ANOTHER_ORGANIZATION_ID'
});
```
```javascript
Velt.setDocument(DOCUMENT_ID, {
organizationId: 'ANOTHER_ORGANIZATION_ID'
});
```
# Use Multiple Locations at once
There are three main steps to adding multiple locations:
1. Add a root location
2. Add additional locations
3. Bind elements containers to `locations`
```jsx
useSetDocument('some_document_id');
useSetLocation({
'id': 'locationRoot',
'locationName': 'PageWithVideo',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
})
```
```jsx
client.setDocument('some_document_id');
client.setLocation({
'id': 'locationRoot',
'locationName': 'PageWithVideo',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
});
```
```jsx
Velt.setDocument('some_document_id');
Velt.setLocation({
'id': 'locationRoot',
'locationName': 'PageWithVideo',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
});
```
```jsx
this.client.setDocument('some_document_id');
this.client.setLocation({
'id': 'locationRoot',
'locationName': 'PageWithVideo',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
})
```
```jsx
client.setDocument('some_document_id');
client.setLocation({
'id': 'locationRoot',
'locationName': 'PageWithVideo',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
})
```
Add additional locations on the page by using set location with the `true` parameter:
```jsx
useSetLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '120'
}, true)
useSetLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '50'
}, true)
useSetLocation({
'id': 'locationId3',
'locationName': 'Scene3LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '80'
}, true)
```
```jsx
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '120'
}, true)
client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '50'
}, true)
client.setLocation({
'id': 'locationId3',
'locationName': 'Scene3LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '80'
}, true)
```
```jsx
Velt.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'scene1',
'videoFrame': '120'
}, true)
Velt.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'scene2',
'videoFrame': '50'
}, true)
Velt.setLocation({
'id': 'locationId3',
'locationName': 'Scene3LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'scene3',
'videoFrame': '80'
}, true)
```
```jsx
this.client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '120'
}, true)
this.client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '50'
}, true)
this.client.setLocation({
'id': 'locationId3',
'locationName': 'Scene3LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '80'
}, true)
```
```jsx
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '120'
}, true)
client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '50'
}, true)
client.setLocation({
'id': 'locationId3',
'locationName': 'Scene3LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'videoFrame': '80'
}, true)
```
* When you render multiple elements representing different locations on the same page,
then you can bind each element container to the correct `location` by adding the `data-velt-location-id` attribute.
* This ensures that the comment added within the location is associated with the correct location.
```jsx
// any content
// any content
```
```jsx
// any content
// any content
```
```jsx
// any content
// any content
```
```jsx
// any content
// any content
```
```jsx
// any content
// any content
```
```js React / Next.js with Hooks
import { useSetDocument, useSetLocation } from '@veltdev/react';
export default function YourDocument() {
//Set Document ID
useSetDocument('some_document_id');
//Set Root Location
useSetLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'mainPage',
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
//Add another Location
useSetLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'mainPage',
'videoFrame': '240'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
},true)
return (
// any content
// any content
);
}
```
```js React / Next.js
import { useVeltClient } from '@veltdev/react';
import { useEffect, useState } from 'react';
export default function YourDocument() {
const { client } = useVeltClient();
useEffect(() => {
if (client) {
//Set Document ID
client.setDocument('some_document_id');
//Set Root Location
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'mainPage',
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
//Add another Location
client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'mainPage',
'videoFrame': '240'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
},true)
}
}, [client]);
return (
// any content
// any content
);
}
```
```html HTML
Collaboration App
// any content
// any content
```
```js Angular
import { Component } from '@angular/core';
import { initVelt } from '@veltdev/client';
import { Velt } from '@veltdev/types';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
client?: Velt;
constructor() {
this.initVelt();
}
// Initialize velt sdk
async initVelt() {
this.client = await initVelt('YOUR_APIKEY');
this.setDocument();
}
// set document id in velt
setDocument() {
if (this.client) {
//Set Document ID
this.client.setDocument('some_document_id');
//Set Root Location
this.client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'mainPage',
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
//Add another Location
this.client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName',
'version': {
'id': 'v1.0',
'name': 'Version Name'
},
'page': 'mainPage',
'videoFrame': '240'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
},true)
}
}
}
// Your HTML File //
// any content
// any content
```
```js Vue.js
// any content
// any content
```
# Remove Locations
To remove all locations, use the `client.removeLocation()` method.
```jsx
client.removeLocation()
```
If you have added multiple locations and only want to remove one of them, you can remove it by using `client.removeLocation()` and by passing in the exact location object you want to remove.
```jsx
// remove specific location
const locationToRemove = {
// your custom location object
}
client.removeLocation(locationToRemove);
```
To remove all locations, use the `client.removeLocation()` method.
```jsx
Velt.removeLocation()
```
If you have added multiple locations and only want to remove one of them, you can remove it by using `client.removeLocation()` and by passing in the exact location object you want to remove.
```jsx
// remove specific location
const locationToRemove = {
// your custom location object
}
Velt.removeLocation(locationToRemove);
```
To remove all locations, use the `client.removeLocation()` method.
```jsx
this.client.removeLocation()
```
If you have added multiple locations and only want to remove one of them, you can remove it by using `client.removeLocation()` and by passing in the exact location object you want to remove.
```jsx
// remove specific location
const locationToRemove = {
// your custom location object
}
this.client.removeLocation(locationToRemove);
```
To remove all locations, use the `client.removeLocation()` method.
```jsx
client.removeLocation()
```
If you have added multiple locations and only want to remove one of them, you can remove it by using `client.removeLocation()` and by passing in the exact location object you want to remove.
```jsx
// remove specific location
const locationToRemove = {
// your custom location object
}
client.removeLocation(locationToRemove);
```
```jsx React / Next.js
//Set Location
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
})
//Add another Location
client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName'
},true)
//Remove first Location
client.removeLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
});
```
```jsx HTML
//Set Location
Velt.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
})
//Add another Location
Velt.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName'
},true)
//Remove first Location
Velt.removeLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
});
```
```jsx Angular
//Set Location
this.client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
})
//Add another Location
this.client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName'
},true)
//Remove first Location
this.client.removeLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
});
```
```jsx Vue.js
//Set Location
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
})
//Add another Location
client.setLocation({
'id': 'locationId2',
'locationName': 'Scene2LocationName'
},true)
//Remove first Location
client.removeLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName'
});
```
# Set Location
Hooks currently do not support automatic change detection in variables.
```jsx
import { useSetDocumentId, useSetLocation } from '@veltdev/react';
```
```js
useSetDocumentId("my-document-id")
```
A **Location** can be described using any plain JSON object and can be set using the `useSetLocation` hook.
This object could represent your app's pages, sections, versions, video frames, or data points on maps/charts etc.
There are three important fields you should care about in your `location` object that are protected keywords:
* `id` - a unique ID to identify your location. It can be used by other methods to find or remove the location later. This field is required.
* `locationName` - a name to describe your location. It is used by other Velt components, such as the `VeltCommentsSideBar`, to label your location. This field is optional but highly recommended.
* `version` - an object that includes an `id` and `name` to mark the specific version of your location. This field is optional.
```jsx
useSetLocation({
'id': 'locationId1',
'locationName': 'MainVideoPlayer',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
```
```jsx
import { useVeltClient } from '@veltdev/react';
```
```jsx
const { client } = useVeltClient();
```
```js
useEffect(() => {
if (client) {
client.setDocumentId('my-unique-document-id')
}
}, [client]);
```
A **Location** can be described using any plain JSON object and can be set using the `client.setLocation()` method.
This object could represent your app's pages, sections, versions, video frames, or data points on maps/charts etc.
There are three important fields you should care about in your `location` object that are protected keywords:
* `id` - a unique ID to identify your location. It can be used by other methods to find or remove the location later. This field is required.
* `locationName` - a name to describe your location. It is used by other Velt components, such as the `VeltCommentsSideBar`, to label your location. This field is optional but highly recommended.
* `version` - an object that includes an `id` and `name` to mark the specific version of your location. This field is optional.
```jsx
client.setLocation({
'id': 'locationId1',
'locationName': 'MainVideoPlayer',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
```
```js
if (Velt) {
Velt.setDocumentId('my-unique-document-id')
}
```
A **Location** can be described using any plain JSON object and can be set using the `client.setLocation()` method.
This object could represent your app's pages, sections, versions, video frames, or data points on maps/charts etc.
There are three important fields you should care about in your `location` object that are protected keywords:
* `id` - a unique ID to identify your location. It can be used by other methods to find or remove the location later. This field is required.
* `locationName` - a name to describe your location. It is used by other Velt components, such as the `VeltCommentsSideBar`, to label your location. This field is optional but highly recommended.
* `version` - an object that includes an `id` and `name` to mark the specific version of your location. This field is optional.
```jsx
Velt.setLocation({
'id': 'locationId1',
'locationName': 'MainVideoPlayer',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
```
```jsx
import { initVelt } from '@veltdev/client';
```
```jsx
this.client = await initVelt('YOUR_APIKEY');
```
```js
if (this.client) {
this.client.setDocumentId('YOUR_DOCUMENT_ID');
}
```
A **Location** can be described using any plain JSON object and can be set using the `this.client.setLocation()` method.
This object could represent your app's pages, sections, versions, video frames, or data points on maps/charts etc.
There are three important fields you should care about in your `location` object that are protected keywords:
* `id` - a unique ID to identify your location. It can be used by other methods to find or remove the location later. This field is required.
* `locationName` - a name to describe your location. It is used by other Velt components, such as the `VeltCommentsSideBar`, to label your location. This field is optional but highly recommended.
* `version` - an object that includes an `id` and `name` to mark the specific version of your location. This field is optional.
```jsx
this.client.setLocation({
'id': 'locationId1',
'locationName': 'MainVideoPlayer',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
```
```jsx
import { initVelt } from "@veltdev/client";
```
```jsx
client = await initVelt("YOUR_APIKEY");
```
```js
if (client) {
client.setDocumentId("YOUR_DOCUMENT_ID");
}
```
A **Location** can be described using any plain JSON object and can be set using the `client.setLocation()` method.
This object could represent your app's pages, sections, versions, video frames, or data points on maps/charts etc.
There are three important fields you should care about in your `location` object that are protected keywords:
* `id` - a unique ID to identify your location. It can be used by other methods to find or remove the location later. This field is required.
* `locationName` - a name to describe your location. It is used by other Velt components, such as the `VeltCommentsSideBar`, to label your location. This field is optional but highly recommended.
* `version` - an object that includes an `id` and `name` to mark the specific version of your location. This field is optional.
```jsx
client.setLocation({
'id': 'locationId1',
'locationName': 'MainVideoPlayer',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
```
```jsx React / Next.js with Hooks
import { useSetDocumentId, useSetLocation } from '@veltdev/react';
export default function YourDocument() {
useSetDocumentId('some_document_id')
useSetLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
return (
//your document template
);
}
```
```jsx React / Next.js
import { useVeltClient } from '@veltdev/react';
import { useEffect, useState } from 'react';
export default function YourDocument() {
const { client } = useVeltClient();
useEffect(() => {
if (client) {
//Set Document ID
client.setDocumentId('some_document_id');
//Set Location
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
}
}, [client]);
return (
//your document template
);
}
```
```html HTML
Collaboration App
```
```js Angular
import { Component } from '@angular/core';
import { initVelt } from '@veltdev/client';
import { Velt } from '@veltdev/types';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
client?: Velt;
constructor() {
this.initVelt();
}
// Initialize velt sdk
async initVelt() {
this.client = await initVelt('YOUR_APIKEY');
this.setDocumentId();
}
// set document id in velt
setDocumentId() {
if (this.client) {
this.client.setDocumentId('YOUR_DOCUMENT_ID');
}
}
//Set Location
client.setLocation({
'id': 'locationId1',
'locationName': 'Scene1LocationName',
'page': 'mainPage',
'version': {
'id': 'v2.3',
'name': 'Version Name'
},
'videoFrame': '120'
// You can keep adding more field to make the location very specific.
// The field names can be anything.
})
}
```
```html Vue.js
```
# null
### Create an Organization
#### Client side
When the user signs into an organization as described below,
if an organization is not present then it will be created automatically.
#### Server side
Using our REST APIs, you will be able to create an organization and also set metadata like name, description, etc.
### Add or Remove Users to an Organization to provision access
* You will need to explicitly [add](/api-reference/rest-apis/users/add-users) or [delete](/api-reference/rest-apis/users/delete-users) users from the organization to provision or revoke access.
* As users are added or removed in your app, you can use our API to sync that information on Velt as well.
* Learn more about [User Management](/api-reference/rest-apis/users/add-users).
If users are not added to the Organization, they will not be able to access the data inside it.
### Sign in User into an Organization
* In the [identify()](/get-started/setup/authenticate) method, set the `organizationId` and also add it to the [auth token](/security/jwt-tokens).
* If you want the user to log into another organization, call the identify method again with the new organizationId and new auth token.
### Access Controls for Organizations
You can set up granular access controls for users within an organization. Learn more [here](/key-concepts/access-control/overview).
# null
## Overview
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/overview-concepts.png)
Velt uses Organizations, Documents, and Locations to organize data and users into the same shared spaces and subspaces, enabling granular access controls. The data hierarchy in Velt follows this structure:
Organization -> Documents -> Locations.
## Organizations
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/organizations.png)
An **Organization** is the top-level entity.
* It contains documents, locations, and users.
* Within an organization, you can create multiple documents, and each document can contain several locations.
Think of an `organization` as a Google Account belonging to a company (e.g., Company A). This account may have several `users` (Company A employees). A `document` will be any file created within the organization (e.g., document, spreadsheet, slides, etc.). A `location` will be any child section within the document (e.g., slide within a presentation).
## Documents
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/documents.png)
A **Document** represents a shared collaborative space where users can interact. Documents live inside the Organization.
Documents contain:
* All feature data (e.g., Comments, Presence, Cursors, etc.).
* Locations
* Users: These are different from Organization Users. (more details in Access Control section)
Users logged into the same `Document ID` can see each other's `Presence`, `Cursors`, `Comments` etc.
For example, in a slide presentation application, the entire slide deck is a document.
## Locations
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/locations.png)
**Locations** are optional subspaces within a document, providing finer partitioning of data.
For instance:
* In a slide presentation, the entire slide presentation will be a document each individual slide will be a location.
* In a dashboard, the entire dashboard is a document but various filters applied will be locations;
* In a video player, the entire video will be the document and timestamps will be locations.
If a **Document** is like a house, a **Location** is like a room within the house.
**Locations** are *completely optional*, you can opt to use them or not. If the complexity of your application does not require the use of **Locations**, you can use **Documents** by themselves to group your users together.
If you opt to use **Locations**, users in the same **Document** must also be in the same **Location** now to interact.
## Users
A `User` represents a person who has been authenticated with the Velt SDK.
Once a `User` has been authenticated, their profile can be seen within Velt's collaboration features.
For example in the `Comments` feature, the `User's` name is shown by their comment and in `@mentions`.
Additionally in the `Presence` and in `Cursors` features, the `User's` name is shown by their avatar bubble and mouse cursors.
There can be two categories of users:
1. **Organization Users**: These belong to the organization and by default have access to organization documents and locations. You can control access to documents and locations at a more granular level for these users.
2. **Guest Users**: For some use cases, you may want non-organization users to access and collaborate on a document within the organization. Using our access control features, you can provide access to guest users to only certain documents and user contacts within the organization.
### Contact List for @mention
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/mentions-users.png)
When the user is on a document, they can `@mention` other users. By default, the contact list for the @mention feature includes users from:
* Organization users.
* Guest users explicitly added to the current document.
* Organization User Groups.
* `@here`: This is a special group that includes only the users explicitly added on the document. This doesn't include organization users or organization user groups.
**Contact list is automatically updated:**
* as users are added or removed from the organization or the document.
* organization user groups are created or updated.
**Access Control:**
Using the access control features, you can control which users are visible on the contact list on a given document.
* Organization users can access the entire Organization contact list on a given organization document.
* Guest (Non-organization) users who were granted access to an organization document cannot access the Organization contact list. If you want to show some organization contacts to these guest users, then you need to explicitly add them to the document.
### Organization User Groups
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/mentions-groups.png)
You can create custom collection of users such as engineering, marketing etc to group organization users and manage access effectively.
* This can be used to @mention a team group versus just individuals (just like in Slack).
* This can also be used to provision access to documents.
* Organization users can belong to multiple organization user groups.
* Non-organization users can't be part of organization user groups.
# null
### Overview
When the user is on a document, they can `@mention` other users. By default, the contact list for the `@mention` feature includes users from:
* Organization users.
* Guest users explicitly added to the current document.
* Organization User Groups.
* `@here`: This is a special group that includes only the users explicitly added on the document. This doesn't include organization users or organization user groups.
**Contact list is automatically updated:**
* as users are [added](/api-reference/rest-apis/users/add-users) or [deleted](/api-reference/rest-apis/users/delete-users) from the organization or the document.
* users are [added](/api-reference/rest-apis/organization-user-groups/add-users-to-group) or [deleted](/api-reference/rest-apis/organization-user-groups/delete-users-from-group) from organization user groups.
* organization user groups are [added](/api-reference/rest-apis/organization-user-groups/add-groups) or updated.
**Access Control:**
Using the access control features, you can control which users are visible on the contact list on a given document.
* Organization users can access the entire Organization contact list on a given organization document.
* Guest (Non-organization) users who were granted access to an organization document cannot access the Organization contact list. If you want to show some organization contacts to these guest users, then you need to explicitly add them to the document.
# null
### Overview
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/key-concepts/mentions-groups.png)
You can create custom collection of users such as engineering, marketing etc to group organization users and manage access effectively.
* This can be used to @mention a team group versus just individuals (just like in Slack).
* This can also be used to provision access to documents. (coming soon)
* Organization users can belong to multiple organization user groups.
* Non-organization users can't be part of organization user groups.
Learn more:
* [Add Organization User Groups](/api-reference/rest-apis/organization-user-groups/add-groups)
* [Add Users to Organization User Groups](/api-reference/rest-apis/organization-user-groups/add-users-to-group)
* [Delete Users from Organization User Groups](/api-reference/rest-apis/organization-user-groups/delete-users-from-group)
# null
To sign in a user call the `identify()` method with the `userId` and `authToken` as described [here](get-started/setup/authenticate).
Even though using JWT Auth token is optional, we recommend using it for additional security.
Once a `User` has been authenticated, their profile can be seen within Velt's collaboration features.
For example in the Comments feature, the User's name is shown by their comment and in `@mentions`.
Additionally in the Presence and in Cursors features, the User's name is shown by their avatar bubble and mouse cursors.
### Sign in with force reset
By default when you identify a **User**, we maintain the user auth in the browser unless you explicitly sign out the logged in user.
If you are changing a User's access or any metadata and want those changes to be reflected immediately,
then you should re-call the `identify` method with `forceReset` option set to `true`.
Learn more [here](get-started/setup/authenticate).
# null
In a given session or browser tab, if you want to switch users,
you need to first signout the current user and then sign in using `identify` method again.
This will ensure we clean up the current user session and start a new session with the new user.
```jsx
client.signOutUser();
```
# Export Cord Data for Migration
### Note:
* These APIs have pagination built-in and return 1000 entries by default. Either extend the limit or loop over the pages to get all the data.
* Send data pulled from each API in a separate JSON file.
## Comments
Use the following Cord REST APIs to pull all the comments-related data and export it as a JSON file:
• [Threads](https://docs.cord.com/rest-apis/threads)
• [Messages](https://docs.cord.com/rest-apis/messages)
• [Users](https://docs.cord.com/rest-apis/users)
• [Groups](https://docs.cord.com/rest-apis/groups)
## Comment File Attachments
When you pull thread and message data using the above APIs, the file attachment URL is also present in the data. However, those URLs expire. You have two options for migration:
**Option 1**: You can use the URL to download the files and upload them to your cloud. Send us the new file attachment URLs in the following format in a JSON file:
Sample Format:
```jsx
[{
"documentId": "",
"userId": "",
"messageId": "",
"type": "file",
"fileData": {
"id": "",
"type": "file",
"name": "",
"url": "",
"mimeType": "MIME_TYPE",
"size": 6278949,
"uploadStatus": "uploaded"
},
"fileUrl": ""
}]
```
**Option 2**: Schedule file migration date with us so you can pull the latest comments data, which will have fresh file access tokens. Then we can download/store it on our end, and update the URLs ourselves.
## Notifications
Given the ephemeral nature of notifications, we don't think you need to migrate them. However, if you do want us to migrate your notifications data, you could use this API to get the notifications data from Cord:
* [Notifications](https://docs.cord.com/rest-apis/notifications)
# Overview
Cord has shut down their service. We are providing a migration path for all the Cord users to migrate to Velt.
## Migration Process Overview
* Download the cord data in JSON format using their REST APls. Send it to us using a secure file-sharing system.
* We have an internal service written to transform that data and add it to your Velt account. You need not use our APls for this. We will do it for you.
* It will take about a week for the end-to-end migration process. We can expedite it to be 1-2 days if it's more urgent for you.
* If you prefer, we can also help you migrate over to Velt on production over a weekend so that your customers don't notice any changes.
## Migration Steps
Give us access to a test account in your app where Cord is implemented. It should contain some cord comments.
Export the Cord data from the above test account and send it to us. [Learn more](https://docs.velt.dev/migration/cord-migration/export-cord-data-for-migration)
We will migrate that small sample first and verify it with you.
Once everything looks good, you will send all of the Cord data as JSON files. We will do a test run on the entire dataset.
Schedule a migration date: On this date, all your cord production data (as of this date) with Velt implemented in your app will go live.
# Overview
You can enable email notifications to send out emails whenever you `@mention` a user in the Comments feature or when another user replies to your comment.
There are two ways to trigger email notifications: Webhooks and SendGrid.
### 1. Webhooks
To learn how to trigger email notifications via Webhooks please refer [here](https://docs.velt.dev/webhooks/overview).
### 2. SendGrid Integration
To enable Email Notifications, go to the Configurations -> Email Service in the Velt Console, or [click here](https://console.velt.dev/dashboard/config/email).
For SendGrid integration, provide the following details:
* SendGrid API Key
* SendGrid Email Template ID for Comments feature
* 'From' Email Address
The 'From' Email Address needs to be whitelisted from your SendGrid account or else it will not work.
## Email Template Data
The following fields are sent to Sendgrid:
| Field | Type | Description |
| ----------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| firstComment | Object | first message in the thread. Contains `commentId`, `commentText` and `from` properties derived from Comment class. |
| latestComment | Object | latest message in the thread that prompted the email. Contains `commentId`, `commentText` and `from` properties derived from Comment class. |
| prevComment | Object | the previous message to the latestMessage. Contains `commentId`, `commentText` and `from` properties derived from Comment class. |
| commentsCount | string | total number of comments in the comment annotation |
| fromUser | string | action user's object |
| commentAnnotation | Object | the comment annotation object without the `comments` field |
| actionType | Object | the action that resulted in the notification |
These are the older fields that will be deprecated soon. These are already contained in the fields above:
| Field | Type | Description |
| --------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| message | string | the message of the email |
| messageFromName | string | the name of whoever wrote the message that the email is referencing. If name is not found then it will contain the email. |
| name | string | the name of the person who performed the action that triggered the email. If name is not found then it will contain the email. Sometimes a notification can be triggered without a message. For those cases, you can use this. |
| fromEmail | string | email address of the user who performed the action |
| photoUrl | string | avatar URL of user |
| pageUrl | string | url of the page the comment is on |
| pageTitle | string | title of the web page |
| deviceInfo | Object | contains browser, OS and device info |
| subject | string | subject of the email |
## Sample Email Template
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/email-template.png)
We have provided this sample HTML email template you can use for your SendGrid email template:
\[Download Link (Right click and view source)][https://firebasestorage.googleapis.com/v0/b/snippyly.appspot.com/o/external%2Femail-template.html?alt=media\&token=16a17614-70ca-464c-b6f4-2b2b097b0007](https://firebasestorage.googleapis.com/v0/b/snippyly.appspot.com/o/external%2Femail-template.html?alt=media\&token=16a17614-70ca-464c-b6f4-2b2b097b0007))
```html
Email Template
@here testing here functionality
 
{{messageFromName}} mentioned you on {{pageTitle}}
```
# Customize Behavior
## 1. Enable avatar mode
Show the user's avatar floating next to their cursor instead of their name.
Enabling this mode will allow you to show the user's avatar in context with the cursor.
```js
```
## 2. Set inactivity time
Set the time it takes for a user to go inactive in milliseconds.
By default we mark a user as inactive if they do not take any action on the document within a 5 mins timeframe.
If they unfocus the tab, we mark them inactive immediately.
```js
```
## 3. Set allowed element IDs
Provide a list of element IDs where the cursors should show.
If you provide a list of element IDs, we will only show cursors that hover over those specific elements.
For eg: For an app with canvas and tool picker: You can whitelist the canvas ID so that the cursors are only visible on the canvas and not the tool picker.
```js
```
You must pass a string into allowedElementIds instead of an object
## 4. Subscribe to changes in User Cursors
Whenever the cursor for any user changes, we emit this event with the updated list of users currently online on this document with their cursor positions.
```js
yourMethod(cursorUsers)} />
```
## 1. Enable avatar mode
Show the user's avatar floating next to their cursor.
Enabling this mode will allow you to show the user's avatar in context with the cursor.
```html
```
## 2. Set inactivity time
Set the time it takes for a cursor to disappear.
If a user leaves their mouse cursor, set the time it takes for that cursor to disappear on everyone else's screen. If they unfocus the tab, they will immediately go inactive
```html
```
## 3. Set allowed element IDs
Whitelist allowed elements
If you provide a list of element IDs, we will only show cursors that hover over those specific elements.
```html
```
## 4. Subscribe to changes in User Cursors
Whenever the cursor for any user changes, we emit this event with the updated list of users currently online on this document with their cursor positions.
```js
```
```js React
import { VeltProvider, VeltCursor } from '@veltdev/react';
export default function App() {
return (
yourMethod(cursorUsers)} {/* 4) Subscribe to changes in User Cursors */}
/>
);
}
```
```html HTML
Cursors documentation
inactivity-time="300000"
allowed-element-ids='["element-1", "element-2"]'
>
```
# Create Your Own UI
Import the `useCursorUsers` hook.
```js
import { useCursorUsers } from '@veltdev/react';
```
```js
const cursorUsers = useCusorUsers()
```
The `cursorUsers` hook will return an array of [User objects](/api-reference/models/User).
You can map the Cursor Users List to generate your own custom UI.
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
return (
Cursor Users: {cursorUsers.length}
{
cursorUsers.map( x =>
{x.name}
)
}
)
```
Import the `useVeltClient` React hook.
You can use this hook within your component to fetch the Velt client.
```js
import { useVeltClient } from '@veltdev/react';
```
Create the hook with the client as a dependency.
Make sure to check if the client is null or undefined before you use it.
```js
useEffect(() => {
if (client) {
//...
}
}, [client]);
```
Subscribe to the realtime Cursor users data on the current document and location.
We will send you a new list of cursors everytime there is a change so you can build out your own cursor UI.
```js
const cursorElement = client.getCursorElement();
let subscription = cursorElement.getOnlineUsersOnCurrentDocument().subscribe((_cursorUsers) => {
// Do something with Cursor Users list
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
Fetch the cursor element from the Velt client.
```js
const cursorElement = window.Velt.getCursorElement();
```
At this point, the Velt instance should be loaded and available to you on the window object.
Subscribe to constant cursor changes.
We will send you a new list of cursors everytime there is a change so you can build out your own cursor UI and logic.
```js
let subscription = cursorElement.getOnlineUsersOnCurrentDocument().subscribe((cursors) => {
// Do something with cursors list
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
```jsx React / Next.js with Hooks
import { useCursorUsers } from '@veltdev/react';
export default function App() {
const cursorUsers = useCursorUsers()
return (
Cursor Users: {cursorUsers.length}
{
cursorUsers.map( x =>
{x.name}
)
}
);
}
```
```js React / Next.js
import { useVeltClient } from '@veltdev/react';
import { useEffect } from 'react';
export default function App() {
const { client } = useVeltClient();
useEffect(() => {
if (client) {
const cursorElement = client.getCursorElement();
let subscription = cursorElement.getOnlineUsersOnCurrentDocument().subscribe((_cursorUsers) => {
// Do something with Cursor Users list
});
//To unsubscribe from the subscription:
subscription?.unsubscribe()
}
}, [client]);
return (
<>
>
);
}
```
```html HTML
Cursors documentation
```
# Parts
We offer several parts which can be used like classes. Full list below.
The component is encapsulated in Shadow DOM, which is isolated from the normal DOM.
Set whatever CSS rules you want.
The part lets you target a specific element within a Shadow DOM.
Reference the table below to see what parts we expose.
Alternatively, you can directly inspect the component HTML to see what parts are available.
```java
velt-cursor::part(label-container) {
border-radius: 0;
}
```
| Part Name | What does it do? |
| ----------------------- | ------------------------------------------------------------------------------------------------------- |
| container | Targets the root cursor container. |
| huddle-avatar-container | Targets the cursor avatar specific to huddle mode. You can learn more about this in the Huddle Feature. |
| avatar-container | Targets the avatar container specific to avatar mode. |
| label-container | Targets the label container. |
| label | Targets the label text. |
# Slots
## Add a custom icon for the cursor
You can provide any HTML inside the cursor element.
Just provide the correct slot attribute and the cursor icon that we use will be replaced.
```js
```
## Add a custom icon for the cursor
You can provide any HTML inside the cursor element.
Just provide the correct slot attribute and the cursor icon that we use will be replaced.
```html
```
```js React / Next.js
import { VeltCursor } from '@veltdev/react';
export default function App() {
return (
<>
{/* ... */}
>
);
}
```
```html HTML
Cursors documentation
```
# Variables
You can select the Cursor component.
Our CSS variables are set at the component level.
Set the variable to anything you want.
We expose a set of variables so you can customize the component to better match your UI.
Reference the table below to see what variables we expose.
Alternatively, you can directly inspect the component CSS to see what variables are available.
| Default | Default | What does it do? |
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `--velt-cursor-avatar-size` | 2rem | Sets the Cursor avatar size in avatar mode. |
| `--velt-cursor-video-size` | 4.2rem | Sets the Cursor video bubble size in Huddle mode. You can learn more about this in the Huddle Feature. |
```css
velt-cursor {
--velt-cursor-avatar-size: 1.5rem;
}
```
# Overview
Your users can view each other's cursors when interacting on the same document. This makes your app more alive. We handle the complexity of adapting the cursors to different screen sizes, differences in content etc.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=cursors)
# null
Import the `VeltCursor` component.
```js
import { VeltProvider, VeltCursor } from '@veltdev/react'
```
Add the `VeltCursor` component to the root of your app.
```js
{/* ... */}
```
This component renders the cursors of users on the same document and location in your web app.
Note that we automatically assign different colors to users & adapt the cursors to different screen sizes and to what's actually present on the screen. So you don't have to worry about building this logic.
Test it out by opening the target page in two browsers (with one in incognito) with two different users logged in.
You should see the cursors of the users rendered as you move mouse around.
Add the `` component to the root of your app.
```html
```
This component renders the cursors of users on the same document and location in your web app.
Note that we automatically assign different colors to users & adapt the cursors to different screen sizes and to what's actually present on the screen. So you don't have to worry about building this logic.
Test it out by opening the target page in two browsers (with one in incognito) with two different users logged in.
You should see the cursors of the users rendered as you move mouse around.
```js React / Next.js
import { VeltProvider, VeltCursor } from '@veltdev/react';
export default function App() {
return (
{/* ... */}
);
}
```
```js HTML
Cursors documentation
```
# Customize Behavior
## 1. Enable custom navigation
Use a callback for custom navigation or side-effects.
When the leader of a `Follow Me` session navigates, we can use the React Router to update the follower's position. In the callback you will receive a `PageInfo` object.
```js
navigate(pageInfo.path)} />
```
### PageInfo
| property | type | description |
| --------- | ------ | --------------------------- |
| `baseUrl` | string | The base URL of the site |
| `path` | string | The path after the base URL |
| `url` | string | The full URL |
## 2. Disable default Follow Me navigation
Disable default `Follow Me` navigation.
`Default: true`
Our default navigation uses window\.location.href to detect navigation. If you are handling navigation using the callback method above, you should disable our default navigation.
```js
```
## 3. Start or Stop Following a User
You can start or Stop Following a User by calling the `startFollowingUser()` and `stopFollowingUser()` methods within a useEffect() hook.
### Start Following
Start following a user by passing in their user ID to the `startFollowingUser()` method.
You can programatically start a `Follow Me` session by passing in the user ID of the user you want to lead the session.
```js
// Start following the user
presenceElement.startFollowingUser(userId);
```
### Stop Following
Stop `Follow Me` session for the current user by by calling the `stopFollowingUser()` method.
If the current user is in a `Follow Me` session, they will be removed from that session. If there are no more followers in the session, the session will be destroyed.
```js
// Stop following the user
presenceElement.stopFollowingUser();
```
## 1. Enable custom navigation
Use a callback for custom navigation or side-effects.
When the leader of a `Follow Me` session navigates, we can use the React Router to update the follower's position. In the callback you will receive a `PageInfo` object.
```js
let onNavigateHandler = (pageInfo) => navigate(pageInfo.path);
if (presenceDOMElement) {
presenceDOMElement.addEventListener("onNavigate", onNavigateHandler);
}
```
### PageInfo
| property | type | description |
| --------- | ------ | --------------------------- |
| `baseUrl` | string | The base URL of the site |
| `path` | string | The path after the base URL |
| `url` | string | The full URL |
## 2. Disable default Follow Me navigation
Disable default `Follow Me` navigation.
`Default: true`
If you are handling navigation using a callback, you can opt to disable our default navigation using window\.location.href.
```js
```
## 3. Start or Stop Following a User
You can start or Stop Following a User by calling the `startFollowingUser()` and `stopFollowingUser()` methods within a script tag.
### Start Following
Start following a user by passing in their user ID to the `startFollowingUser()` method.
You can programatically start a `Follow Me` session by passing in the user ID of the user you want to lead the session.
```js
// Start following the user
presenceElement.startFollowingUser(userId);
```
### Stop Following
Stop `Follow Me` session for the current user by by calling the `stopFollowingUser()` method.
If the current user is in a `Follow Me` session, they will be removed from that session. If there are no more followers in the session, the session will be destroyed.
```js
// Stop following the user
presenceElement.stopFollowingUser();
```
```js React
import { VeltPresence } from '@veltdev/react';
import { useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
if (client) {
const presenceElement = client.getPresenceElement();
// Pass in the user ID of the user you want to follow
presenceElement.startFollowingUser(userId);
// Stop following the user
presenceElement.stopFollowingUser();
}
}, [client]);
const navigate = useNavigate();
return (
);
}
```
```html HTML
Follow Me mode documentation
```
# Overview
This is like Figma's follow along feature. Start a shared session in a click. One person is the leader, and whatever they do - like clicking, scrolling, or navigating - happens automatically on everyone else's screen.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=flock)
# Setup
Enable `Follow Me` mode feature.
```js
```
You need to add the [Presence feature](/realtime-collaboration/presence/overview) to trigger `Follow Me` mode. This will enable `Follow Me` mode as an option for your users globally, wherever `Presence` is shown.
To start the shared `Follow Me` session, click on a user's avatar to start following them.
Test it out by opening the target page in two browsers with two different users logged in.
You should see the avatars of the users rendered where you added the `Presence` component. Now click on any avatar to start following them.
Enable `Follow Me` mode feature.
```html
```
You need to add [Presence feature](/realtime-collaboration/presence/overview) to trigger `Follow Me` mode. This will enable `Follow Me` mode as an option for your users globally, wherever Presence is shown.
To start the shared `Follow Me` session, click on a user's avatar to start following them.
Test it out by opening the target page in two browsers with two different users logged in.
You should see the avatars of the users rendered where you added the presence component. Now click on any avatar to start following them.
```js React / Next.js
import { VeltPresence } from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
```
# Customize Behavior
## 1. Enable Audio Only Mode
Sets the mode to audio only mode.
This is the default mode if no type is provided.
```jsx
```
## 2. Enable Video Only Mode
Sets the mode to video only mode.
```jsx
```
## 3. Enable Screen Only Mode
Sets the mode to screen only mode.
```jsx
```
## 4. Enable All Modes
Sets the mode to all modes.
```jsx
```
## 5. Dark Mode
Whether dark mode is enabled.
`Default: false`
```js
```
## 6. Disable Chat Feature
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/chat-in-huddle.png)
Whether Chat feature is disabled in `Huddles`.
`Default: true`
```js
```
API Methods:
```jsx
const huddleElement = client.getHuddleElement();
// to enable chat
huddleElement.enableChat();
// to disable chat
huddleElement.disableChat();
```
## 7. Enable Flock Mode on Avatar Click
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/huddle-flock.gif)
Whether Flock Mode on Avatar Click is enabled in `Huddles`.
`Default: false`
```js
```
API Methods:
```jsx
const huddleElement = client.getHuddleElement();
// to enable flock mode on avatar click
huddleElement.enableFlockModeOnAvatarClick();
// to disable flock mode on avatar click
huddleElement.disableFlockModeOnAvatarClick();
```
## 1. Enable Audio Only Mode
Sets the mode to audio only mode.
This is the default mode if no type is provided.
```jsx
```
## 2. Enable Video Only Mode
Sets the mode to video only mode.
```jsx
```
## 3. Enable Screen Only Mode
Sets the mode to screen only mode.
```jsx
```
## 4. Enable All Modes
Sets the mode to all modes.
```jsx
```
## 5. Dark Mode
Whether dark mode is enabled.
`Default: false`
```js
```
## 6. Disable Chat Feature
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/chat-in-huddle.png)
Whether Chat feature is disabled in `Huddles`.
`Default: true`
```js
```
API Methods:
```jsx
const huddleElement = client.getHuddleElement();
// to enable chat
huddleElement.enableChat();
// to disable chat
huddleElement.disableChat();
```
## 7. Enable Flock Mode on Avatar Click
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/gifs/huddle-flock.gif)
Whether Flock Mode on Avatar Click is enabled in `Huddles`.
`Default: false`
```js
```
API Methods:
```jsx
const huddleElement = client.getHuddleElement();
// to enable flock mode on avatar click
huddleElement.enableFlockModeOnAvatarClick();
// to disable flock mode on avatar click
huddleElement.disableFlockModeOnAvatarClick();
```
```js React / Next.js
import { VeltHuddle, VeltHuddleTool} from '@veltdev/react';
import {useEffect} from 'react'
export default function App() {
useEffect(()=>{
const huddleElement = client.getHuddleElement();
huddleElement.enableChat();
huddleElement.disableChat();
huddleElement.enableFlockModeOnAvatarClick();
huddleElement.disableFlockModeOnAvatarClick();
})
return (
);
}
```
```html HTML
```
# null
We offer several parts which can be used like classes. Full list below.
The component is encapsulated in Shadow DOM, which is isolated from the normal DOM.
Set whatever CSS rules you want.
The part lets you target a specific element within a Shadow DOM.
Reference the table below to see what parts we expose.
Alternatively, you can directly inspect the component HTML to see what parts are available.
| property | description |
| ------------------ | ----------------------------------------- |
| `container` | Targets the comment tool container |
| `button-container` | Targets the comment tool button container |
| `button-icon` | Targets the comment tool button SVG icon |
```css Tool
velt-huddle-tool::part(button-icon) {
width: 1.5rem;
height: 1.5rem;
}
```
# Slots
Provide a template for the Huddle Tool.
Target the `button` slot with your own custom template.
```js
```
Provide a template for the Huddle Tool.
Target the `button` slot with your own custom template.
```html
```
```js React / Next.js
import {
VeltHuddleTool
} from '@veltdev/react';
export default function App() {
return (
<>
>
);
}
```
```html HTML
Huddle documentation
```
# null
To update CSS variables for the Huddle Tool, please refer to [Global Styles](/global-styles/global-styles)
# Overview
Enable slack-style effortless audio, video and screen sharing discussions inside your own product. It even comes with built in chat.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=huddle)
# Setup
Import the `Huddle` component from the React library.
```js
import { VeltHuddle, VeltHuddleTool } from '@veltdev/react';
```
Add the `VeltHuddle` component to the root of your app.
```js
```
This component is required to render `Huddle` UI components and `Huddle` users in your app.
Add the `VeltHuddleTool` component wherever you want to show the `Huddle` tool button.
Clicking on it initiates a huddle.
```js
```
Add the `VeltHuddle` component to the root of your app.
This component is required to render `Huddle` UI components and `Huddle` users in your app.
```html
```
Add the `VeltHuddleTool` component wherever you want to show the `Huddle` tool button.
Clicking on it initiates a `Huddle`.
```html
```
```js React / Next.js
import { VeltHuddle, VeltHuddleTool} from '@veltdev/react';
export default function App() {
return (
{/* Add VeltHuddle at the root of your app */}
{/* Add VeltHuddleTool where you want the huddle button to appear */}
);
}
```
```html HTML
```
# Customize Behavior
## 1. Enable/Disable Default Elements Tracking
* When **enabled**,
* the SDK will track User's presence on these elements types automatically: `input`, `textarea`, `button`, `contenteditable`.
* to disable tracking on certain elements, you need to manually add an attribute `data-velt-live-selection-enabled="false"` to those elements.
* When **disabled**,
* the SDK will not track User's presence on any elements.
* to enable tracking on certain elements, you need to manually add an attribute `data-velt-live-selection-enabled="true"` to those elements.
* Default: `Enabled`.
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.enableDefaultElementsTracking();
selectionElement.disableDefaultElementsTracking();
```
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.enableDefaultElementsTracking();
selectionElement.disableDefaultElementsTracking();
```
## 2. Enable/Disable User Indicator
* User indicator is the user avatar or their name label that appears on the top left or top right of the selected element.
* Default: `Enabled`.
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.enableUserIndicator();
selectionElement.disableUserIndicator();
```
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.enableUserIndicator();
selectionElement.disableUserIndicator();
```
## 3. Set User Indicator Position
* User indicator position can be set to `start` or `end`.
* You can set it globally using API or set it locally on individual elements using attributes.
* Default: `end`.
Set globally using API:
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.setUserIndicatorPosition('start');
```
Set locally on individual elements using attributes:
```html
...
```
Set globally using API:
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.setUserIndicatorPosition('start');
```
Set locally on individual elements using attributes:
```html
...
```
## 4. Set User Indicator Type
* User indicator type can be set to `avatar` or `label`.
* `avatar`: Displays the user avatar.
* `label`: Displays the user's name.
* You can set the type globally using API or set it locally on individual elements using attributes.
* Default: `avatar`.
Set globally using API:
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.setUserIndicatorType('label');
```
Set locally on individual elements using attributes:
```html
...
```
Set globally using API:
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.setUserIndicatorType('label');
```
Set locally on individual elements using attributes:
```html
...
```
## 5. Get Live Selection Data
* Get the live selection data for the current document.
**Using Hook:**
```jsx
const liveSelectionData = useLiveSelectionDataHandler();
useEffect(() => {
console.log('liveSelectionData', liveSelectionData);
}, [liveSelectionData]);
```
**Using API:**
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.getLiveSelectionData().subscribe((liveSelectionData: LiveSelectionData) => {
console.log("liveSelectionData: ", liveSelectionData);
});
```
**Using API:**
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.getLiveSelectionData().subscribe((liveSelectionData: LiveSelectionData) => {
console.log("liveSelectionData: ", liveSelectionData);
});
```
# Customize UI
## 1. Enable/Disable Default Styling
* When enabled, the SDK will apply default styling to the selected elements.
* When disabled, the SDK will not apply any styling to the selected elements. You can use these classes to style the selected elements:
* `.velt-live-selection-on-element`: This dynamic class will be added to the selected element only when there is a user on that element.
* `.velt-live-selection-on-text`: This dynamic class will be added to the text node only when a user has selected that text.
* Default: `Enabled`.
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.enableDefaultStyling();
selectionElement.disableDefaultStyling();
```
**Custom Styling:**
```css
.velt-live-selection-on-element {
outline: 2px solid var(--velt-color);
outline-offset: -2px;
}
```
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.enableDefaultStyling();
selectionElement.disableDefaultStyling();
```
**Custom Styling:**
```css
.velt-live-selection-on-element {
outline: 2px solid var(--velt-color);
outline-offset: -2px;
}
```
# Overview
Your users can see what part of the document others are interacting with in real-time.
When Live Selection is enabled, users will see the users' avatar or name, and a highlight box around the element they are interacting with.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=live-selection)
# null
## Get the Live Selection Element and enable it
When enabled, Live Selection will automatically detect and highlight user's presence on the following elements:
* `input`
* `textarea`
* `button`
* `contenteditable`
You can learn more about customizing and controlling which elements are highlighted and its styling [here](/realtime-collaboration/live-selection/customize-behavior).
```js
const { client } = useVeltClient();
useEffect(() => {
if (client) {
const selectionElement = client.getSelectionElement();
selectionElement.enableLiveSelection();
selectionElement.disableLiveSelection();
}
}, [client]);
```
```js
if (Velt) {
const selectionElement = Velt.getSelectionElement();
selectionElement.enableLiveSelection();
selectionElement.disableLiveSelection();
}
```
# Overview
Allow users to sync state across forms in different clients
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=live-state-sync\&layout=horizontal)
# Redux Middleware
If you are using Redux, you can use our middleware to sync state changes directly into your Redux store.
To get started import the `createLiveStateMiddleware` middleware from the Velt SDK.
```jsx
import { createLiveStateMiddleware } from "@veltdev/react";
```
Use `createLiveStateMiddleware` to create a `middleware` and a `updateLiveStateDataId` method.
Then add the created `middleware` to your Redux store configuration.
You can also export the `updateLiveStateDataId` method from your Redux store. This method will be used to dynamically set the `liveStateDataId`.
**store.js**:
```jsx
import { configureStore } from "@reduxjs/toolkit";
import { createLiveStateMiddleware } from "@veltdev/react";
//create middleware
const { middleware, updateLiveStateDataId } = createLiveStateMiddleware();
//add middleware to your Redux store configuration
export const store = configureStore({
reducer: {
... your reducers here
},
// You just have to concat our liveStateMiddleware in store
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(middleware)
});
// optionally export updateLiveStateDataId method if you want to set liveStateDataId dynamically
export { updateLiveStateDataId };
```
`createLiveStateMiddleware` takes in an optional configuration object with the following schema:
```tsx
type LiveStateMiddlewareConfig = {
allowedActionTypes?: Set,
disabledActionTypes?: Set,
allowAction?: (action: any) => boolean,
liveStateDataId?: string,
};
```
You can use it to define the following fields:
* `allowedActionTypes` - allow live state syncing on specific action types only
* `disabledActionTypes` - restrict live state syncing on specific action types
* `allowAction` - custom callback method to dynamically decide to allow or disable syncing for that action. Return `true` to allow the action and `false` to restrict the action.
* `liveStateDataId` - used to set a custom string value as live state data key. If not provided, we will store data in default key.
Example:
```jsx
const { middleware, updateLiveStateDataId } = createLiveStateMiddleware({
allowedActionTypes: new Set(['action1', 'action2']),
disabledActionTypes: new Set(['disabledAction1', 'disabledAction2']),
allowAction: (action) => {
// return true to allow this action, false to restrict this action from syncing
},
liveStateDataId: 'custom-live-state-data-id'
});
```
It is recommended to first set a custom `liveStateDataId` in the middleware configuration. You can then change it dynamically later using the `updateLiveStateDataId` method.
To set `liveStateDataId` dynamically, call the `updateLiveStateDataId` method that was previously created and exported from your store.
```jsx
import { updateLiveStateDataId } from 'path-to-store.js';
function YourComponent() {
// You can update liveStateDataId any time based on your requirements and all the actions called after that will be synced on new ID path.
const setLiveStateDataId = (id) => {
updateLiveStateDataId(id);
}
return (
Your HTML Template
);
}
```
```jsx React / Next.js
// store.js
import { configureStore } from "@reduxjs/toolkit";
import { createLiveStateMiddleware } from "@veltdev/react";
//create middleware and updateLiveStateDataId method
const { middleware, updateLiveStateDataId } = createLiveStateMiddleware({
allowedActionTypes: new Set(['action1', 'action2']),
disabledActionTypes: new Set(['disabledAction1', 'disabledAction2']),
allowAction: (action) => {
// return true to allow this action, false to restrict this action from syncing
},
liveStateDataId: 'custom-live-state-data-id'
});
//add middleware to your Redux store configuration
export const store = configureStore({
reducer: {
... your reducers here
},
// You just have to concat our liveStateMiddleware in store
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(middleware)
});
// you can optionally export the updateLiveStateDataId method if you want to set liveStateDataId dynamically
export { updateLiveStateDataId };
```
# null
# Live State Sync
With `Live State Sync`, you can create a shared data object that persists and syncs across all clients live on a given document.
```jsx
import { useLiveStateData, useSetLiveStateData, useLiveState} from '@veltdev/react';
```
The `useSetLiveStateData()` hook can be used to set or update a custom shared data object that will be synced across all clients live on the current document.
Pass in a unique ID and the data object you want to sync. If the object already exists with that ID, then it will be updated.
Input params:
* `liveStateDataId`: `string`;
* `liveStateData`: `any`;
```jsx
const liveStateDataId = 'uniqueId';
const liveStateData = { 'key': 'value' }; // any type of data
useSetLiveStateData(liveStateDataId, liveStateData)
```
The hook will automatically unsubscribe from the subscription when the component dismounts.
Data that is added via Live State Sync will persist until it is manually cleaned up. We don't auto reset the live state data after any period of time.
The `useLiveStateData()` hook can be used to get the live state data object.
Pass in the unique ID that you had used to set the data.
Input params:
* `liveStateDataId`: `string`; (optional)
```jsx
const liveStateDataId = 'uniqueId';
let data = useLiveStateData(liveStateDataId)
```
By default, the `useLiveStateData()` subscription will return all changes to the shared live data object including changes that occured when the current client was not subscribed.
If you want to only receive changes starting from when the client subscribed to `useLiveStateData()`, you can pass a config object as shown below:
```jsx
const liveStateDataConfig = {
listenToNewChangesOnly: true // default is false
};
const liveStateData = useLiveStateData(LIVE_STATE_DATA_KEY, liveStateDataConfig)
```
If you are familiar with React's `useState()` hook, we have a similar hook called `useLiveState()` that can be used to sync data realtime for specific state variables in your code.
Hook syntax:
```jsx
const [value, setValue] = useLiveState(UNIQUE_ID, INITIAL_VALUE, OPTIONS);
```
The hook takes in 3 parameters:
* `UNIQUE_ID` -> unique id in string to be synced across the screens
* `INITIAL_VALUE` -> initial value of the state
* `OPTIONS` (object)
* `syncDuration` -> debounce duration in milliseconds to sync realtime (optional, default value 50ms)
* `resetLiveState` -> Boolean to reset locatl state value on server side on initiatlize of hook (default: `false`)
The hook returns a setter and getter:
* `value` -> current state value (similar to useState hook)
* `setValue` -> function to be called to set next value (similar to useState hook)
Example:
```tsx
import { useLiveState } from "@veltdev/react";
export function MyReactComponent() {
const [counter, setCounter] = useLiveState < number > ("counter", 0, {
syncDuration: 100,
resetLiveState: true
});
return (
Counter: {counter}
);
}
```
By default, `useLiveState()` will return all changes to the shared live data object including changes that occured when the current client was not subscribed.
If you want to only receive changes starting from when the client subscribed to `useLiveState()`, you can pass a config object as shown below:
```jsx
const [counter, setCounter] = useLiveState < number > ("counter", 0, {
syncDuration: 100,
resetLiveState: true,
listenToNewChangesOnly: true // default is false
});
```
You can listen to changes in the server connection state with the `useServerConnectionStateChangeHandler()` hook.
```jsx
const serverConnectionState = useServerConnectionStateChangeHandler();
```
The server connection state will be an ENUM with the following states:
```jsx
export enum ServerConnectionState {
ONLINE = 'online',
OFFLINE = 'offline',
PENDING_INIT = 'pendingInit',
PENDING_DATA = 'pendingData',
}
```
The `useLiveState()` hook can also return the server connection state as the third return value.
```jsx
import { useEffect } from 'react';
import { useLiveState, useServerConnectionStateChangeHandler } from '@veltdev/react';
function YourComponent() {
const serverConnectionState = useServerConnectionStateChangeHandler();
useEffect(() => {
console.log('serverConnectionState', serverConnectionState);
}, [serverConnectionState]);
// Returning serverConnectionState in useLiveState
const [stateValue, setStateValue, serverConnectionStateInLiveState] = useLiveState(STATE_KEY, INITIAL_VALUE);
useEffect(() => {
console.log('stateValue:', stateValue, 'serverConnectionStateInLiveState:', serverConnectionStateInLiveState);
}, [stateValue, serverConnectionStateInLiveState]);
return (
// ... your component code
);
}
```
# Live State Sync
With `Live State Sync`, you can create a shared data object that persists and syncs across all clients live on a given document.
You can get an instance of the `liveStateSyncElement` element by calling `client.getLiveStateSyncElement()`.
```jsx
const { client } = useVeltClient();
useEffect( () => {
if(client){
const liveStateSyncElement = client.getLiveStateSyncElement();
}
})
```
Or you can use the `useLiveStateSyncUtils()` hook, which avoids a lot of boilerplate code:
```jsx
const liveStateSyncElement = useLiveStateSyncUtils();
```
The `liveStateSyncElement.setLiveStateData()` method sets or updates a custom shared data object that will be synced across all clients live on the current document.
Pass in a unique ID and the data object you want to sync. If the object already exists with that ID, then it will be updated.
Input params:
* `liveStateDataId`: `string`;
* `liveStateData`: `any`;
```jsx
const liveStateDataId = 'uniqueId';
const liveStateData = { 'key': 'value' }; // any type of data
liveStateSyncElement.setLiveStateData(liveStateDataId, liveStateData);
```
Data that is added via Live State Sync will persist until it is manually cleaned up. We don't auto reset the live state data after any period of time.
The `liveStateSyncElement.getLiveStateData()` method subscribes to an existing shared live data object. Pass in the unique ID that you had used to set the data.
Input params:
* `liveStateDataId`: `string`; (optional)
```jsx
const liveStateDataId = 'uniqueId';
let subscription = liveStateSyncElement.getLiveStateData(liveStateDataId).subscribe((data) => {
// your logic here...
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
By default, the `getLiveStateData()` subscription will return all changes to the shared live data object including changes that occured when the current client was not subscribed.
If you want to only receive changes starting from when the client subscribed to `getLiveStateData()`, you can pass a config object as shown below:
```jsx
const liveStateDataConfig = {
listenToNewChangesOnly: true // default is false
};
let subscription = liveStateSyncElement.getLiveStateData(LIVE_STATE_DATA_KEY, liveStateDataConfig).subscribe((data) => {
console.log('[Debug] getLiveStateData 31-05-2024-2 in html', data);
});
```
You can listen to changes in the server connection state with the `onServerConnectionStateChange()` method:
```jsx
const liveStateSyncElement = client.getLiveStateSyncElement();
let subscription = liveStateSyncElement.onServerConnectionStateChange().subscribe((data) => {
console.log('server connection state change: ', data);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
The server connection state will be an ENUM with the following states:
```jsx
export enum ServerConnectionState {
ONLINE = 'online',
OFFLINE = 'offline',
PENDING_INIT = 'pendingInit',
PENDING_DATA = 'pendingData',
}
```
# Live State Sync
With `Live State Sync`, you can create a shared data object that persists and syncs across all clients live on a given document.
```jsx
const liveStateSyncElement = Velt.getLiveStateSyncElement();
```
The `liveStateSyncElement.setLiveStateData()` method sets or updates a custom shared data object that will be synced across all clients live on the current document.
Pass in a unique ID and the data object you want to sync. If the object already exists with that ID, then it will be updated.
Input params:
* `liveStateDataId`: `string`;
* `liveStateData`: `any`;
```jsx
const liveStateDataId = 'uniqueId';
const liveStateData = { 'key': 'value' }; // any type of data
const liveStateSyncElement = Velt.getLiveStateSyncElement();
liveStateSyncElement.setLiveStateData(liveStateDataId, liveStateData);
```
Data that is added via Live State Sync will persist until it is manually cleaned up. We don't auto reset the live state data after any period of time.
The `liveStateSyncElement.getLiveStateData()` method subscribes to an existing shared live data object. Pass in the unique ID that you had used to set the data.
Input params:
* `liveStateDataId`: `string`; (optional)
```jsx
const liveStateDataId = 'uniqueId';
const liveStateSyncElement = Velt.getLiveStateSyncElement();
let subscription = liveStateSyncElement.getLiveStateData(liveStateDataId).subscribe((data) => {
// your logic here...
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
By default, the `getLiveStateData()` subscription will return all changes to the shared live data object including changes that occured when the current client was not subscribed.
If you want to only receive changes starting from when the client subscribed to `getLiveStateData()`, you can pass a config object as shown below:
```jsx
const liveStateDataConfig = {
listenToNewChangesOnly: true // default is false
};
liveStateSyncElement.getLiveStateData(LIVE_STATE_DATA_KEY, liveStateDataConfig).subscribe((data) => {
console.log('[Debug] getLiveStateData 31-05-2024-2 in html', data);
});
```
You can listen to changes in the server connection state with the `onServerConnectionStateChange()` method:
```jsx
const liveStateSyncElement = Velt.getLiveStateSyncElement();
let subscription = liveStateSyncElement.onServerConnectionStateChange().subscribe((data) => {
console.log('server connection state change: ', data);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
The server connection state will be an ENUM with the following states:
```jsx
export enum ServerConnectionState {
ONLINE = 'online',
OFFLINE = 'offline',
PENDING_INIT = 'pendingInit',
PENDING_DATA = 'pendingData',
}
```
```jsx React / Next.js with Hooks
import { useLiveStateData, useSetLiveStateData, useLiveState} from '@veltdev/react';
export default function YourDocument() {
const liveStateDataId = "uniqueId";
const liveStateData = { "key": "value" }; // any type of data
//sets the live state data object
useSetLiveStateData(liveStateDataId, liveStateData)
//Gets the live state data object
let data = useLiveStateData("myUniqueId")
//Setter and Getter for useLiveState,
//Third return value is an ENUM showing the connection state
const [counter, setCounter, serverConnectionStateInLiveState] = useLiveState("counter", 0, {
syncDuration: 100,
resetLiveState: true
});
//Gets the server connection state
const serverConnectionState = useServerConnectionStateChangeHandler();
return (
{data?.myKey}
Counter: {counter}
);
}
```
```jsx React / Next.js
import { useVeltClient, useEffect } from '@veltdev/react';
export default function YourDocument() {
const { client } = useVeltClient();
useEffect(()=>{
if(client){
const liveStateDataId = "uniqueId";
const liveStateData = { "key": "value" }; // any type of data
const liveStateSyncElement = client.getLiveStateSyncElement();
//Set Live State Data
liveStateSyncElement.setLiveStateData(liveStateDataId, liveStateData);
//Subscribe to Live State Data
let subscription = liveStateSyncElement.getLiveStateData(liveStateDataId).subscribe((data) => {
// your logic here...
});
// To unsubscribe from the subscription:
subscription?.unsubscribe()
//Subscribe to changes in server connection state
liveStateSyncElement.onServerConnectionStateChange().subscribe((data) => {
console.log('server connection state change: ', data);
});
}
})
return (
...
);
}
```
```jsx HTML
if(Velt){
const liveStateDataId = "uniqueId";
const liveStateData = { "key": "value" }; // any type of data
const liveStateSyncElement = Velt.getLiveStateSyncElement();
//Set Live State Data
liveStateSyncElement.setLiveStateData(liveStateDataId, liveStateData);
//Subscribe to Live State Data
let subscription = liveStateSyncElement.getLiveStateData(liveStateDataId).subscribe((data) => {
// your logic here...
});
//To unsubscribe from the subscription:
subscription?.unsubscribe()
//Subscribe to changes in server connection state
liveStateSyncElement.onServerConnectionStateChange().subscribe((data) => {
console.log('server connection state change: ', data);
});
}
```
# Customize Behavior
## 1. Set inactivity time
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/inactivity.png)
Set the time it takes for a `User` to go inactive in milliseconds.
`Default: 300000ms (5 min)`
```js
```
By default we mark a `User` as inactive if they do not take any action on the document within a 5 mins timeframe.
If they unfocus the tab, we mark them inactive immediately.
## 2. Add Presence to a Location
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Location.png)
Show `Presence` on a `Location`.
Set the `location` attribute on the `Presence` element. When there are `Users` at that location, their avatars will show in this `Presence` element.
```js
```
Eg: For a Presentation tool, you can add `Presence` component at the main `document` level and add another `Presence` component on the slide thumbnails. This will render avatars at both presentation level & slide thumbnail level. For slide thumbnails, it will only show `Users` active on that slide.
## 3. Set max users
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Presence_Max_Users.png)
Set how many `Presence` avatars to display at a time.
You can set this via the `maxUsers` attribute. Any extra avatars will be hidden and shown in an avatar which indicates the number of extra `Users`.
```js
```
## 4. Enable Follow Me Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Flock_Mode.png)
To enable `Follow Me` Mode, set the `flockMode` attribute to `true`.
```js
```
This will enable `Follow Me mode` as an option for your `Users` globally, wherever Presence`is shown.`
To start the shared flock session, click on a `User's` avatar to start following them.
Learn more about it in the [Flock Mode feature section](/realtime-collaboration/flock-mode/overview).
## 5. Subscribe to changes in User Presence
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/onPresenceUserChange.png)
Whenever the `Presence` for any `User` changes, we emit this event with the updated list of `Users` currently online on this document.
```js
yourMethod(presenceUsers)} />
```
## 6. Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Mode.png)
Whether dark mode is enabled.
`Default: false`
```js
```
## 7. Include Self in List of Presence Users
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/self.png)
Whether to include yourself in the list of `Presence` users.
`Default: true`
```js
```
API Method:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.enableSelf();
presenceElement.disableSelf();
```
## 8. Handle Presence User Click Events
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/onPresenceUserClick.png)
To handle click events on `Presence` avatar circles, pass an event handler to the `onPresenceUserClick` event.
```jsx
const onPresenceUserClickEvent = (user) => {
console.log("Clicked presence user: ", user);
}
onPresenceUserClickEvent(user)} />
```
## 1. Set inactivity time
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/inactivity.png)
Set the time it takes for a `User` to go inactive.
```html
```
By default a `User` will go inactive after 5 minutes. If they unfocus the tab, then they will immediately go inactive.
## 2. Add Presence to a Location
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Location.png)
Show `Presence` on a `Location`.
Set the `location` attribute on the `Presence` element. When there are `Users` at that location, their avatars will show in this `Presence` element.
```js
```
Eg: For a Presentation tool, you can add `Presence` component at the main `document` level and add another `Presence` component on the slide thumbnails. This will render avatars at both presentation level & slide thumbnail level. For slide thumbnails, it will only show `Users` active on that slide.
## 3. Set max users
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Presence_Max_Users.png)
Max users determines how many Presence avatars to display at a time.
You can set this via the max-users attribute. Any extra avatars will be hidden and shown in an avatar which indicates the number of extra `Users`.
```html
```
## 4. Enable Follow Me Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Flock_Mode.png)
To enable `Follow Me Mode`, set the `flock-mode` attribute to `true`.
```html
```
This will enable `Follow Me mode` as an option for your `Users` globally, wherever Presence is shown.
To start the shared `Follow Me` session, click on a `User's `avatar to start following them.
Learn more about it in the [Flock Mode feature section](/realtime-collaboration/flock-mode/overview).
## 5. Subscribe to changes in User Presence
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/onPresenceUserChange.png)
Whenever the `Presence` for any `User `changes, we emit the `onPresenceUserChange` event with the updated list of `Users` currently online on this document.
```js
```
## 6. Dark Mode
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/Mode.png)
Whether dark mode is enabled.
`Default: false`
```js
```
## 7. Include Self in List of Presence Users
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/self.png)
Whether to include yourself in the list of `Presence` users.
`Default: true`
```js
```
API Method:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.enableSelf();
presenceElement.disableSelf();
```
## 8 . Handle Presence User Click Events
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/presence/onPresenceUserClick.png)
To handle click events on `Presence` avatar circles, add an event listern on the `onPresenceUserClick` event.
```jsx
const presenceTag = document.querySelector('velt-presence');
presenceTag.addEventListener('onPresenceUserClick', (event) => {
console.log("Clicked presence user: ", event.detail);
});
```
```jsx React / Next.js
import { VeltPresence, useVeltClient } from '@veltdev/react';
import { useEffect } from 'react';
export default function App() {
let client = useVeltClient()
const onPresenceUserClickEvent = (user) => {
console.log("Clicked presence user: ", user);
}
useEffect(() => {
if(client){
const presenceElement = client.getPresenceElement();
presenceElement.enableSelf(); // 7
presenceElement.disableSelf(); // 7
}
})
return (
yourMethod(presenceUsers)} {/* 5) Subscribe to changes in User Presence */}
darkMode={true} {/* 6) Dark Mode */}
self={false} {/* 7) Disclude Self in list of Presence Users */}
onPresenceUserClick={(user) => onPresenceUserClickEvent(user)} {/* 8) Handle Presence User Click Events */}
/>
```
# Create Your Own UI
Import the `usePresenceUsers` hook.
```js
import { usePresenceUsers } from '@veltdev/react';
```
```js
const presenceUsers = usePresenceUsers()
```
The `usePresenceUsers` hook will return an array of [User objects](/api-reference/models/User).
You can map the Presence Users List to generate your own custom UI.
```jsx
return (
Presence Users: {presenceUsers.length}
{
presenceUsers.map( x =>
{x.name}
)
}
)
```
The hook will automatically unsubscribe from the subscription when the component dismounts.
Import the `useVeltClient` React hook.
You can use this hook within your component to fetch the Velt client.
```js
import { useVeltClient } from '@veltdev/react';
```
```js
const { client } = useVeltClient();
```
Create an effect with the client as a dependency.
Make sure to check if the client is null or undefined before you use it.
```js React
useEffect(() => {
if (client) {
//...
}
}, [client]);
```
Subscribe to the realtime `Presence` users data on the current document and location.
We will send you a new list of users everytime there is a change in the status of any user, so you can build out your own `Presence` UI.
```js React
useEffect(() => {
if (client) {
const presenceElement = client.getPresenceElement();
let subscription = presenceElement.getOnlineUsersOnCurrentDocument().subscribe((users) => {
// Take user list data and render your own component
});
// To unsubscribe from the subscription:
subscription?.unsubscribe()
}
}, [client]);
```
Subscribe to the realtime `Presence` users data on the current document and location.
We will send you a new list of users everytime there is a change in the status of any user, so you can build out your own `Presence` UI.
```js
```
```jsx React / Next.js with Hooks
import { usePresenceUsers } from '@veltdev/react';
export default function App() {
const presenceUsers = usePresenceUsers()
return (
Presence Users: {presenceUsers.length}
{
presenceUsers.map( x =>
{x.name}
)
}
);
}
```
```js React / Next.js
import { useVeltClient } from '@veltdev/react';
import {useEffect} from 'react';
export default function App() {
const { client } = useVeltClient();
useEffect(() => {
if (client) {
const presenceElement = client.getPresenceElement();
let subscription = presenceElement.getOnlineUsersOnCurrentDocument().subscribe((users) => {
// Take users list data and render your own component
});
// To unsubscribe from the subscription:
subscription?.unsubscribe()
}
}, [client]);
return (
// example of rendering your own component
// presenceUsers.map((presenceUser) => { return (
{/* Add custom UI code here */}
) })
);
}
```
```html HTML
Presence documentation
```
# Parts
We offer several parts which can be used like classes. Full list below.
The component is encapsulated in Shadow DOM, which is isolated from the normal DOM.
Set whatever CSS rules you want.
The part lets you target a specific element within a Shadow DOM.
Reference the table below to see what parts we expose.
Alternatively, you can directly inspect the component HTML to see what parts are available.
| Part Name | What does it do? |
| -------------- | ---------------------------------- |
| tooltip | Targets the tooltip container |
| tooltip-avatar | Targets the tooltip avatar |
| tooltip-text | Targets the tooltip text container |
| user-avatar | Targets the user avatar container |
| more-users | Targets the more users container |
```css Parts
velt-presence::part(user-avatar)::before {
content: "";
position: absolute;
border-radius: 50%;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(45deg, rgba(255, 0, 0, 0.5), rgba(0, 255, 0, 0.5));
pointer-events: none;
}
```
# Variables
You can select the `Presence` component.
Our CSS variables are set at the component level.
Set the variable to anything you want.
We expose a set of variables so you can customize the component to better match your UI.
Reference the table below to see what variables we expose.
Alternatively, you can directly inspect the component CSS to see what variables are available.
| Variable Name | Default (light) | Default (dark) | What does it do? |
| ------------------------------------ | --------------- | -------------- | --------------------------------- |
| --velt-presence-avatar-size | 2rem | 2rem | Sets the Presence avatar size |
| --velt-presence-primary-text-color | #141416 | #E5E5E9 | Sets the primary text color |
| --velt-presence-secondary-text-color | #777E90 | #91919C | Sets the secondary text color |
| --velt-presence-tooltip-bg-color | #FCFCFD | #141416 | Sets the tooltip background color |
| --velt-presence-tooltip-border-color | #E6E8EC | #303034 | Sets the tooltip border color |
| --velt-pressence-more-users-color | #D4D6DF | #404044 | Sets the more users color |
```css Variables
velt-presence, velt-presence-tooltip {
// Below values are for demo purpose only,
// you can use different avatar-size and colors according to your usecase
--velt-presence-avatar-size: 6rem !important;
--velt-presence-primary-text-color: red !important;
--velt-presence-secondary-text-color: blue !important;
--velt-presence-tooltip-bg-color: grey !important;
--velt-presence-tooltip-border-color: red !important;
--velt-presence-more-users-color: purple !important;
}
```
# Overview
Your users can see other users online on the document. This makes your app feel alive.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=presence)
# Setup
Import the `Presence` component from the React library.
```js
import { VeltPresence } from '@veltdev/react';
```
Add it anywhere you want to see the `User` avatars.
```js
```
This component renders the avatars of `Users` on the same document in your web app. We automactically detect when `Users` are active on the document and render their avatars.
Test it out by opening the target page in two browsers with two different `Users` logged in.
You should see the avatars of the `Users` rendered where you added the `Presence` component.
Place the component wherever you want the avatars to appear.
```html
```
Test it out by opening the target page in two browsers with two different `Users` logged in.
You should see the avatars of the `Users` rendered where you added the `Presence` component.
```js React
import { VeltPresence } from '@veltdev/react';
export default function App() {
return (
);
}
```
```html HTML
Presence documentation
```
# Overview
Lock down forms or settings dashboards so only a single editor can edit at once.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=single-editor-mode\&layout=horizontal)
# null
## Single Editor Mode
With Single Editor Mode, only one user will be able to edit the document at any given time. All other users live on the document will automatically be read-only.
The `LiveStateSyncElement` contains all of the methods used to interact with the `single editor mode` and `live state sync` features.
There are two ways to access the `LiveStateSyncElement`.
### Accessing via the Velt client object
```jsx
const liveStateSyncElement = client.getLiveStateSyncElement();
```
### Accessing via a hook
```jsx
let liveStateSyncElement = useLiveSelectionUtils();
```
Enables single editor mode.
`Disabled by default`
```jsx
liveStateSyncElement.enableSingleEditorMode(); //enables single editor mode
```
#### (Optional) Set your DOM to read-only yourself, or let our SDK do it for you.
`Default: We handle it for you.`
You can pass a configuration object to enable `customMode` on Single Editor Mode.
If custom mode is enabled, we will not make the viewer's DOM read-only. You will have to handle the logic yourself.
```jsx
liveStateSyncElement.enableSingleEditorMode({ customMode: true });
```
#### (Optional) Lock Edit access to Single-Tab.
`Default: true`
The configuration `{singleTabEditor: boolean}` limits editing to one tab at a time. If an editor opens the same `document` in multiple tabs and begins editing in one (e.g., Tab 1), edit access is automatically disabled in the others (e.g., Tab 2). To enable editing in a different tab, a button can be implemented to trigger the `editCurrentTab()` method, granting edit access in that specific tab.
```tsx
liveStateSyncElement.enableSingleEditorMode({ singleTabEditor: false });
```
Disables single editor mode.
```jsx
liveStateSyncElement.disableSingleEditorMode(); // disables single editor mode
```
In Single Editor mode, by default there is a small toast at the bottom of the screen that shows:
* whether you are the editor or viewer
* whether any other viewer has requested editor access
* access request timeout coundown
* option to reject request
To enable this toast, you can use the `enableDefaultSingleEditorUI()` method.
`Default: enabled`
```jsx
liveStateSyncElement.enableDefaultSingleEditorUI();
```
In Single Editor mode, by default there is a small toast at the bottom of the screen that shows:
* whether you are the editor or viewer
* whether any other viewer has requested editor access
* access request timeout coundown
* option to reject request
You can choose to disable this toast in favor of using your own custom UI.
To disable this pop up, you can use the `disableDefaultSingleEditorUI()` method.
```jsx
liveStateSyncElement.disableDefaultSingleEditorUI();
```
Sets the current user as Editor. Note this will mark all the other users on the document as readonly users.
```jsx
liveStateSyncElement.setUserAsEditor();
```
`isUserEditor()` returns a subscription that emits the following `UserEditorAccess` object:
```jsx
{
isEditor: true, // `true` if the user is the editor
isEditorOnCurrentTab: true // true if the user is the editor on the current tab
}
```
```jsx
let subscription = liveStateSyncElement.isUserEditor().subscribe((userEditorAccess) => {
//userEditorAccess.isEditor <-- True if user is editor
//userEditorAccess.isEditorOnCurrentTab <-- true if the user is editor on current tab
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
**Using Hooks:**
The `useUserEditorState()` hook is used to call `liveStateSyncElement.isUserEditor()` without having to handle the subscription.
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
import { useUserEditorState} from '@veltdev/react';
export default function YourDocument() {
let {isEditor, isEditorOnCurrentTab} = useUserEditorState()
console.log("Is User Editor?: ", isEditor)
return (
Is User Editor?: {isEditor}
)
}
```
`getEditor()` returns a subscription that emits the `User` object representing the current editor.
`User` object schema:
```jsx
{
"email": "userX@example.com",
"name": "User X",
"photoUrl": "https://i.pravatar.cc/302",
"userId": "X",
}
```
```jsx
let subscription = liveStateSyncElement.getEditor().subscribe((user) => {
// user contains User object data
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
**Using Hooks:**
The `useEditor()` hook is used to call `liveStateSyncElement.getEditor()` without having to handle the subscription.
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
import { useEditor} from '@veltdev/react';
export default function YourDocument() {
let user = useEditor()
console.log("Editor User Data: ", user)
return (
)
}
```
Resets access for all users.
```jsx
liveStateSyncElement.resetUserAccess();
```
Call `requestEditorAccess()` from a Viewer's client to get editor access from the current editor.
It returns a subscription that emits:
* `null` if the request is pending
* `true` if the request went accepted by the editor
* `false` if the request was rejected by the editor
```jsx
let subscription = liveStateSyncElement.requestEditorAccess().subscribe((data) => {
// data == null -> Request is pending
// data == true -> Request accepted by editor
// data == false -> Request rejected by editor
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
The `isEditorAccessRequested()` method is used to detect on the `Editor's` client if a `Viewer` has requested editor access or not.
If the user is not the editor or the request has been canceled, the subscription will emit a `null` value.
It returns a subscription that emits:
* `null` if the user is not the editor or the request has been canceled
* a `isUserEditor` object if the user is the editor and there is an ongoing request
`isUserEditor` Object Schema:
```jsx
{
requestStatus: 'requested', // currently always is equal to 'requested' if there is an ongoing request
requestedBy: { // User Object
"email": "userX@example.com",
"name": "User X",
"photoUrl": "https://i.pravatar.cc/302",
"userId": "X",
}
}
```
```jsx
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
**Using Hooks:**
The `useEditorAccessRequestHandler()` hook is used to call `liveStateSyncElement.isEditorAccessRequested()` without having to handle the subscription.
The hook will automatically unsubscribe from the subscription when the component dismounts.
```jsx
import { useEditorAccessRequestHandler } from '@veltdev/react';
export default function YourDocument= () {
const editorAccessRequested = useEditorAccessRequestHandler();
// editorAccessRequested == null --> if user is not editor or the request has been canceled
// editorAccessRequested.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// editorAccessRequestedrequestedBy -> contains data about the User that requested access
return (
)
}
```
Once the Editor gets an Editor Access request, call 'acceptEditorAccessRequest()' method to `accept` the request.
```jsx
liveStateSyncElement.acceptEditorAccessRequest();
```
Use with `isEditorAccessRequested()`:
```jsx
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
if(data.requestStatus === 'requested'){
liveStateSyncElement.acceptEditorAccessRequest();
}
});
```
Once the Editor gets an Editor access request, call 'rejectEditorAccessRequest()' method to `reject` the request.
```jsx
liveStateSyncElement.rejectEditorAccessRequest();
```
Use with `isEditorAccessRequested()`:
```jsx
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
if(data.requestStatus === 'requested'){
liveStateSyncElement.rejectEditorAccessRequest();
}
});
```
The `cancelEditorAccessRequest()` method is used to cancel an existing edit access request by the Viewer.
```jsx
liveStateSyncElement.cancelEditorAccessRequest();
```
The `editCurrentTab()` method is used to make the current tab editable when the editor has opened multiple tabs on the same page.
```jsx
liveStateSyncElement.editCurrentTab();
```
The `setEditorAccessTimeout()` method is used to set the editor access timeout value in seconds.
`Default: 5 seconds`
```jsx
liveStateSyncElement.setEditorAccessTimeout(15); // in seconds
```
The `enableEditorAccessTransferOnTimeOut()` method is used to enable automatic transferring of editor access after timeout.
`Default: enabled`
```jsx
liveStateSyncElement.enableEditorAccessTransferOnTimeOut();
```
The `disableEditorAccessTransferOnTimeOut()` method is used to disable automatic transferring of editor access after timeout.
```jsx
liveStateSyncElement.disableEditorAccessTransferOnTimeOut();
```
Auto syncs the content of the following Text HTML elements, such that when `editor` types, the element's content is synced across all active users currently live on the document.
* input
* textarea
* ContentEditable Divs
To enable syncing, follow these two steps:
1. Enable Auto Sync State feature:
```jsx
liveStateSyncElement.enableAutoSyncState();
```
2. Set the `data-velt-sync-state` attribute to `true` on the Text HTML element you want to sync:
You can set an `id` to the HTML element to make the syncing more robust.
```jsx
```
You must add the `data-velt-sync-state` attribute to a native HTML element (e.g. input, textarea). It will not work directly on React components.
By default in Single Editor Mode, we control making these html elements editable or readonly on the entire DOM:
* input
* textarea
* select
* button
* contentEditable divs
For Viewers, the default elements listed above will be disabled, meaning `click`, `mouseup` and `mousedown` events will be prevented.
If there are any other elements that you want us to control beyond the default elements we listed above, such as `div`, `span` elements etc, then you can add `data-velt-sync-access="true"` to that element.
```jsx
```
You must add the `data-velt-sync-access` attribute to native HTML elements (e.g. div, span). It will not work directly on React components.
In Single Editor Mode, we control enabling and disabling of certain html elements on the entire DOM.
If you want to exclude any of those html elements from this behavior, you can add `data-velt-sync-access-disabled="true"` to that element.
```jsx
```
You must add the `data-velt-sync-access-disabled` attribute to native HTML elements (e.g. button, input). It will not work directly on React components.
By default Single Editor Mode is enabled at the entire DOM level. You can restrict this feature to only certain HTML containers & their children by using the `singleEditorModeContainerIds()` method. It takes in an array of element ID strings. These IDs represent the parent elements within which Single Editor Mode will be enabled.
```jsx
liveStateElement.singleEditorModeContainerIds(["rightPanel"]);
```
With the `useEditorAccessTimer` hook, you can get the Editor Access Timer state.
```jsx
import { useEditorAccessTimer } from '@veltdev/react';
function YourReactComponent {
const editorAccessTimer = useEditorAccessTimer();
useEffect(() => {
if (editorAccessTimer?.state === 'completed') {
onEditorAccessTimerCompleted();
}
}, [editorAccessTimer]);
const onEditorAccessTimerCompleted = () => {
// If user is editor, make requester as editor
acceptEditorAccessRequest();
// If user is requester, make it as editor
setUserAsEditor();
}
}
```
The `editorAccessTimer` class has the following schema:
* `state`: `'idle'` | `'inProgress'` | `'completed' `
* `durationLeft`: `number`
## Single Editor Mode
With Single Editor Mode, only one user will be able to edit the document at any given time. All other users live on the document will automatically be read-only.
The `LiveStateSyncElement` contains all of the methods used to interact with the `single editor mode` and `live state sync` features.
### Accessing via the Velt client object
```jsx
const liveStateSyncElement = client.getLiveStateSyncElement();
```
Enables single editor mode.
`Disabled by default`
```jsx
liveStateSyncElement.enableSingleEditorMode(); //enables single editor mode
```
#### (Optional) Set your DOM to read-only yourself, or let our SDK do it for you.
`Default: We handle it for you.`
You can pass a configuration object to enable `customMode` on Single Editor Mode.
If custom mode is enabled, we will not make the viewer's DOM read-only. You will have to handle the logic yourself.
```jsx
liveStateSyncElement.enableSingleEditorMode({ customMode: true });
```
#### (Optional) Lock Edit access to Single-Tab.
`Default: true`
The configuration `{singleTabEditor: boolean}` limits editing to one tab at a time. If an editor opens the same `document` in multiple tabs and begins editing in one (e.g., Tab 1), edit access is automatically disabled in the others (e.g., Tab 2). To enable editing in a different tab, a button can be implemented to trigger the `editCurrentTab()` method, granting edit access in that specific tab.
```tsx
liveStateSyncElement.enableSingleEditorMode({ singleTabEditor: false });
```
Disables single editor mode.
```jsx
liveStateSyncElement.disableSingleEditorMode(); // disables single editor mode
```
In Single Editor mode, by default there is a small toast at the bottom of the screen that shows:
* whether you are the editor or viewer
* whether any other viewer has requested editor access
* access request timeout coundown
* option to reject request
To enable this toast, you can use the `enableDefaultSingleEditorUI()` method.
`Default: enabled`
```jsx
liveStateSyncElement.enableDefaultSingleEditorUI();
```
In Single Editor mode, by default there is a small toast at the bottom of the screen that shows:
* whether you are the editor or viewer
* whether any other viewer has requested editor access
* access request timeout coundown
* option to reject request
You can choose to disable this toast in favor of using your own custom UI.
To disable this pop up, you can use the `disableDefaultSingleEditorUI()` method.
```jsx
liveStateSyncElement.disableDefaultSingleEditorUI();
```
Sets the current user as Editor. Note this will mark all the other users on the document as readonly users.
```jsx
liveStateSyncElement.setUserAsEditor();
```
`isUserEditor()` returns a subscription that emits the following `UserEditorAccess` object:
```jsx
{
isEditor: true, // `true` if the user is the editor
isEditorOnCurrentTab: true // true if the user is the editor on the current tab
}
```
```jsx
let subscription = liveStateSyncElement.isUserEditor().subscribe((userEditorAccess) => {
// userEditorAccess.isEditor <-- True if user is editor
// userEditorAccess.isEditorOnCurrentTab <-- true if the user is editor on current tab
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
`getEditor()` returns a subscription that emits the `User` object that represents the current editor.
`User` object schema:
```jsx
{
"email": "userX@example.com",
"name": "User X",
"photoUrl": "https://i.pravatar.cc/302",
"userId": "X",
}
```
```jsx
let subscription = liveStateSyncElement.getEditor().subscribe((user) => {
// user contains User object data
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
Resets access for all users.
```jsx
liveStateSyncElement.resetUserAccess();
```
Call `requestEditorAccess()` from a Viewer's client to get editor access from the current editor.
It returns a subscription that emits:
* `null` if the request is pending
* `true` if the request went accepted by the editor
* `false` if the request was rejected by the editor
```jsx
let subscription = liveStateSyncElement.requestEditorAccess().subscribe((data) => {
// data == null -> Request is pending
// data == true -> Request accepted by editor
// data == false -> Request rejected by editor
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
The `isEditorAccessRequested()` method is used to detect on the Editor's client if a Viewer has requested editor access or not.
If the user is not the editor or the request has been canceled, the subscription will emit a `null` value.
It returns a subscription that emits:
* `null` if the user is not the editor or the request has been canceled
* a `isUserEditor` object if the user is the editor and there is an ongoing request
`isUserEditor` Object Schema:
```jsx
{
requestStatus: 'requested', // currently always is equal to 'requested' if there is an ongoing request
requestedBy: { // User Object
"email": "userX@example.com",
"name": "User X",
"photoUrl": "https://i.pravatar.cc/302",
"userId": "X",
}
}
```
```jsx
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
Once the Editor gets an Editor Access request, call 'acceptEditorAccessRequest()' method to `accept` the request.
```jsx
liveStateSyncElement.acceptEditorAccessRequest();
```
Use with `isEditorAccessRequested()`:
```jsx
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
if(data.requestStatus === 'requested'){
liveStateSyncElement.acceptEditorAccessRequest();
}
});
```
Once the Editor gets an Editor access request, call 'rejectEditorAccessRequest()' method to `reject` the request.
```jsx
liveStateSyncElement.rejectEditorAccessRequest();
```
Use with `isEditorAccessRequested()`:
```jsx
liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
if(data.requestStatus === 'requested'){
liveStateSyncElement.rejectEditorAccessRequest();
}
});
```
The `cancelEditorAccessRequest()` method is used to cancel an existing edit access request by the Viewer.
```jsx
liveStateSyncElement.cancelEditorAccessRequest();
```
The `editCurrentTab()` method is used to make the current tab editable when the editor has opened multiple tabs on the same page.
```jsx
liveStateSyncElement.editCurrentTab();
```
The `setEditorAccessTimeout()` method is used to set the editor access timeout value in seconds.
`Default: 5 seconds`
```jsx
liveStateSyncElement.setEditorAccessTimeout(15); // in seconds
```
The `enableEditorAccessTransferOnTimeOut()` method is used to enable automatic transferring of editor access after timeout.
`Default: enabled`
```jsx
liveStateSyncElement.enableEditorAccessTransferOnTimeOut();
```
The `disableEditorAccessTransferOnTimeOut()` method is used to disable automatic transferring of editor access after timeout.
```jsx
liveStateSyncElement.disableEditorAccessTransferOnTimeOut();
```
Auto syncs the content of the following Text HTML elements, such that when `editor` types, the element's content is synced across all active users currently live on the document.
* input
* textarea
* ContentEditable Divs
To enable syncing, follow these two steps:
1. Enable Auto Sync State feature:
```jsx
liveStateSyncElement.enableAutoSyncState();
```
2. Set the `data-velt-sync-state` attribute to `true` on the Text HTML element you want to sync:
You can set an `id` to the HTML element to make the syncing more robust.
```jsx
```
You must add the `data-velt-sync-state` attribute to a native HTML element (e.g. input, textarea). It will not work directly on React components.
By default in Single Editor Mode, we control making these html elements editable or readonly on the entire DOM:
* input
* textarea
* select
* button
* contentEditable divs
For Viewers, the default elements listed above will be disabled, meaning `click`, `mouseup` and `mousedown` events will be prevented.
If there are any other elements that you want us to control beyond the default elements we listed above, such as `div`, `span` elements etc, then you can add `data-velt-sync-access="true"` to that element.
```jsx
```
You must add the `data-velt-sync-access` attribute to native HTML elements (e.g. div, span). It will not work directly on React components.
In Single Editor Mode, we control enabling and disabling of certain html elements on the entire DOM.
If you want to exclude any of those html elements from this behavior, you can add `data-velt-sync-access-disabled="true"` to that element.
```jsx
```
You must add the `data-velt-sync-access-disabled` attribute to native HTML elements (e.g. button, input). It will not work directly on React components.
By default Single Editor Mode is enabled at the entire DOM level. You can restrict this feature to only certain HTML containers & their children by using the `singleEditorModeContainerIds()` method. It takes in an array of element ID strings. These IDs represent the parent elements within which Single Editor Mode will be enabled.
```jsx
liveStateElement.singleEditorModeContainerIds(["rightPanel"]);
```
```jsx React / Next.js
//Get Velt Client
import { useVeltClient } from '@veltdev/react';
import { useEffect } from 'react';
function MyComponent(){
const { client } = useVeltClient();
useEffect( () => {
//Get LiveStateSyncElement from Velt Client
const liveStateSyncElement = client.getLiveStateSyncElement();
//Enable Single Editor Mode
liveStateSyncElement.enableSingleEditorMode({
customMode:false,
singleTabEditor:true
});
//Disable Single Editor Mode
liveStateSyncElement.disableSingleEditorMode();
let subscription = liveStateSyncElement.isUserEditor().subscribe((userEditorAccess) => {
//userEditorAccess.isEditor <-- True if user is editor
//userEditorAccess.isEditorOnCurrentTab <-- true if the user is editor on current tab
});
let subscription = liveStateSyncElement.getEditor().subscribe((user) => {
// user contains User object data
});
//Set Current User as Editor
liveStateSyncElement.setUserAsEditor();
//Reset Editor Access
liveStateSyncElement.resetUserAccess();
//Enable Auto Sync State
liveStateSyncElement.enableAutoSyncState();
//Restrict Auto Sync Access to Specified Parent Elements
liveStateSyncElement.singleEditorModeContainerIds(['element-1','element-2'])
//Request editor access from the current editor
let subscription = liveStateSyncElement.requestEditorAccess().subscribe((data) => {
// data == null -> Request is pending
// data == true -> Request accepted by editor
// data == false -> Request rejected by editor
});
// Check whether editor access is being requested
let subscription = liveStateSyncElement.isEditorAccessRequested().subscribe((data) => {
// data == null --> if user is not editor or the request has been canceled
// data.requestStatus == 'requested' -> Current user is Editor & access requested by Viewer
// data.requestedBy -> contains data about the User that requested access
if(data.requestStatus === 'requested'){
// To accept an editor access request
liveStateSyncElement.acceptEditorAccessRequest();
// To reject an editor access request
liveStateSyncElement.rejectEditorAccessRequest();
}
});
// To unsubscribe from the subscription:
subscription?.unsubscribe();
// Setting the editor access timeout value
liveStateSyncElement.setEditorAccessTimeout(15); // in seconds
// Enable automatic transfer of edit access after timeout
liveStateSyncElement.enableEditorAccessTransferOnTimeOut();
// Disable automatic transfer of edit access after timeout
liveStateSyncElement.disableEditorAccessTransferOnTimeOut();
// Enable single editor UI
liveStateSyncElement.enableDefaultSingleEditorUI();
// Disable single editor UI
liveStateSyncElement.disableDefaultSingleEditorUI();
// To cancel an edit access request by the Viewer
liveStateSyncElement.cancelEditorAccessRequest();
// To make the current tab editable when the editor has opened multiple tabs
liveStateSyncElement.editCurrentTab();
},[])
return (
{/* Enable Auto Sync State across on Text Elements */}
{/* Enable Auto Sync Access on Custom Elements */}
{/* Disable Auto Sync Access on Default Elements */}
)
}
```
```js HTML
```
# null
## Video Player Sync
`Video Player Sync` allows users to sync their video player with other `Users` in the same document.
This lets all `Users` watch the same video at the same time.
# Setup
To enable `Video Player Sync` in any video, add the `data-sync-video-player="true"` attribute to your video player.
```html
```
Test out `Video Player Sync` by opening two clients side-by-side and having one client play the video player.
To enable `Video Player Sync` in any video, add the `data-sync-video-player="true"` attribute to your video player.
```html
```
# null
## Versions
* Latest SDK: [1.0.107](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.119](https://www.npmjs.com/package/@veltdev/types)
## Method to Hide Comments from Users on specific Locations
The [client.excludeLocationIds()](https://docs.velt.dev/async-collaboration/comments/customize-behavior/general-controls) method can be used to hide `Comments` at specific `Locations` from `Users`.
```jsx
const locationIds = ['location1', 'location2']; // list of location ids
client.excludeLocationIds(locationIds);
```
## Configuration to Disable Recording Summaries
If you want to [disable the Recording summaries](https://docs.velt.dev/async-collaboration/comments/customize-behavior/multimedia) that appear when you record you audio, voice or screen, you can can now do so.
Example:
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
// to show recording summary
commentElement.enableRecordingSummary();
// to hide recording summary
commentElement.disableRecordingSummary();
```
## Configuration to Disable Recording countdown
If you want to [disable the countdown](https://docs.velt.dev/async-collaboration/comments/customize-behavior/multimedia) that appears when you begin recording your audio, voice, or screen, you can now do so. When the countdown is disabled, recordings will begin instantly.
Example
```jsx
```
API Methods:
```jsx
// API method - comment element
const commentElement = client.getCommentElement();
// To enable recording countdown
commentElement.enableRecordingCountdown();
// To disable recording countdown
commentElement.disableRecordingCountdown();
// API method - recorder element
const recorderElement = client.getRecorderElement();
// To enable recording countdown
recorderElement.enableRecordingCountdown();
// To disable recording countdown
recorderElement.disableRecordingCountdown();
```
## Method to Remove the Document ID
You can now unset the `Document Id` to pause the Velt SDK functions.
```jsx
client.unsetDocumentId()
```
## More File Type Support in Comment attachments
We've added support for more file types in `Comment` attachments (`csv`, `docx`, `pptx`, `xlsx`, `webp`, `mp4` etc.).
# null
## Versions
* Latest SDK: [1.0.112](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.127](https://www.npmjs.com/package/@veltdev/types)
## Option to Disable ShadowDOM
We've added an option for you to [disable the ShadowDOM on certain components](https://docs.velt.dev/async-collaboration/comments/customize-behavior/ui-controls).
By default, a ShadowDOM is used with certain components to ensure that your application's CSS does not interfere with the styling of the SDK components.
If you want your application's CSS to affect the styling of the SDK components, you can disable the ShadowDOM.
```jsx
```
API methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.enablePinShadowDOM();
commentElement.disablePinShadowDOM();
commentElement.enableDialogShadowDOM();
commentElement.disableDialogShadowDOM();
commentElement.enableSidebarShadowDOM();
commentElement.disableSidebarShadowDOM();
```
## Method to Unsubscribe from Subscriptions
We added an `unsubscribe()` method to unsubscribe from any event subscriptions.
Example subscription:
```jsx
let subscription = commentElement.getAllCommentAnnotations().subscribe((comments) => {
// Do something with comments
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
## Added webhook notifications for `Huddle` Create and Join Events
Your users will now receive [webhook notifications](https://docs.velt.dev/webhooks/huddle-webhooks) when a `Huddle` is created or joined.
Example notification schema:
```jsx
{
"from": {
// UserObject
},
"projectName": "string",
"projectUrl": "string",
"actionType": "string", // created | joined
"notificationSource": "huddle",
"documentId": "string",
"clientDocumentId": "string",
"id": "string",
"timestamp": 1234567890,
"actionUser": {
// UserObject
},
}
```
Click here to see the [Notification](https://docs.velt.dev/api-reference/models/Notification) schema.
## Added Validations to check for Audio and Video permissions on `Huddles`
Your users will now be prompted to check for Audio and Video permissions on `Huddles`
## Option to disable `Chat` feature on `Huddles`
You can now [disable the Chat feature](https://docs.velt.dev/realtime-collaboration/huddle/customize-behavior) that is enabled by default on `Huddle` component.
## Option to disable `flockMode` feature on `Huddles`
You can now [disable the flockMode feature](https://docs.velt.dev/realtime-collaboration/huddle/customize-behavior) that is enabled by default on `Huddle` component when clicking on another user's Avatar.
## Added support to set a custom order of locations to sort by in the `Comments Sidebar`.
You can now define the [order in which locations appear](https://docs.velt.dev/async-collaboration/comments-sidebar/customize-behavior) in the `Comments Sidebar` filter through the 'order' field in the `filterConfig` object.
```jsx
```
## Added configuration for Unread Comments Indicator
You can now configure the [Unread Comments Indicator](https://docs.velt.dev/async-collaboration/comments/customize-behavior/display-metadata) to be either `minimal` or `verbose`.
In `minimal` mode unread comments will be indicated by a small red dot.
In `verbose` mode unread comments will be indicated by a larger badge that says UNREAD
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/verbose-mode-comments.png)
## Added hook for `useUnsetDocumentId`
There is now a hook called [useUnsetDocumentId()](https://docs.velt.dev/api-reference/hooks/hooks#useunsetdocumentid) that calls [client.unsetDocumentId()](https://docs.velt.dev/api-reference/api-methods/velt-client)
```jsx
import { useUnsetDocumentId} from '@veltdev/react'
import React from 'react'
export default function YourComponent() {
useUnsetDocumentId();
return (
...
)
}
```
## Added option to use a specific version of the Velt React SDK.
You can now use a [specific version of the Velt React SDK](https://docs.velt.dev/get-started/setup/advanced).
To do so, in your `VeltProvider` component, set the `config` props object to `{ version: '1.0.XYZ' }`.
Example:
```jsx
...
```
# null
# 2.0.30
### New Features
* \[**Comments**]: Added `resolvedByUserId` field on comment resolve. This allows tracking which user resolved a comment thread.
* \[**Comments**]: Added a configuration on sidebar to open the filter panel as a bottom sheet (default) or overlap on the filter button.
* Prop: `filterPanelLayout`. Values: `'menu' | 'bottomSheet'` (default)
* Usage:
```jsx
```
```html
```
# null
# 2.0.31
### New Features
* \[**UI Customization**]: Added [Confirm Dialog Wireframe](/async-collaboration/comments/customize-ui/confirm-dialog/overview) and added support for light and dark modes.
* This is a common UI component used for multiple features like deleting comment thread, deleting a recorded content and more.
* It will automatically inherit the light or dark mode property from the parent feature.
# null
# 2.0.33
### New Features
* \[**UI Customization**]: Added conditional dynamic classes for different states of various interactive components:
* `velt-sidebar-tool-open`: For the sidebar button when the sidebar is open.
* `velt-notification-tool-open`: For the notification tool when the notification panel is open.
* `velt-comments-sidebar-status-open`: For the status dropdown trigger when the dropdown is open.
* `velt-comments-sidebar-filter-button-open`: For the filter button when the filter panel is open.
### Bug Fixes
* \[**Comments**]: Fixed an issue where after adding '@here' the cursor position was incorrectly placed at the start of the text.
# 2.0.32
### New Features
* \[**UI Customization**]: Added conditional classes for different states of various interactive components:
* `velt-comment-pin-open`: For the comment pin when the pin is selected.
* `velt-dropdown-open`: For the dropdown trigger when the dropdown is open.
* `velt-assign-dropdown-open`: For the assign dropdown trigger when the dropdown is open.
* `velt-custom-dropdown-open`: For the custom dropdown trigger when the dropdown is open.
* `velt-options-dropdown-open`: For the options dropdown trigger when the dropdown is open.
* `velt-priority-dropdown-open`: For the priority dropdown trigger when the dropdown is open.
* `velt-status-dropdown-open`: For the status dropdown trigger when the dropdown is open.
* `velt-reaction-tool-open`: For the reaction tool when the tool is open.
* `velt-comment-bubble-open`: For the comment bubble when the bubble is selected.
* `velt-comment-bubble-hover`: For the comment bubble when the bubble is hovered over.
* \[**Comments**]: Added configuration for customizing the '@here' mention label. eg: @all, @everyone, @team, etc:
Using Props:
```jsx
```
Using API:
```javascript
const contactElement = client.getContactElement();
contactElement.setAtHereLabel('@all');
```
Using Props:
```html
```
Using API:
```javascript
const contactElement = Velt.getContactElement();
contactElement.setAtHereLabel('@all');
```
### Improvements
* \[**Notifications**]: Various UI improvements on the notification panel.
### Bug Fixes
* \[**Comments**]: Fixed a bug where email contacts could have an invalid character ('.') at the end.
* \[**Comments**]: Resolved an issue where the comment sidebar filter wouldn't be sticky on page refresh.
* \[**Debugger**]: Fixed typo with event name `commentMoreReplayOptionClicked` and updated it to `commentMoreReplyOptionClicked`.
# null
# 2.0.35
### Improvements
* \[**Comments**]: Added dynamic `velt-composer-input-focused` class on the composer container whenever the input is focused.
This provides more control over the styling of the composer input when it is focused.
# 2.0.34
### Improvements
* \[**Comments**]: Added dynamic `velt-composer-open` class on the composer container whenever the composer is opened.
* \[**Comments**]: Improved contact and reaction tooltips:
* Inherited shadow DOM property from parent's shadow DOM property.
* Added light mode and dark mode support.
### Bug Fixes
* \[**Comments**]: Fixed user avatar background color in autocomplete chip tooltip to match the color assigned to the user in the SDK.
# null
# 2.0.39
### Bug Fixes
* \[**Comments**]: Fixed a css padding issue related to the action buttons in the comment dialog composer.
# 2.0.38
## Bug Fixes
* \[**Comments**]: Fixed an issue with the user-avatar component, ensuring it's consistently used across all the components.
# 2.0.37
### New Features
* \[**Comments**]: Added the ability to exclude specific location IDs from the comments sidebar. This provides more control over the displayed content.
Using Props:
```jsx
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.excludeLocationIdsFromSidebar(['location1', 'location2']);
```
Using Props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.excludeLocationIdsFromSidebar(['location1', 'location2']);
```
# 2.0.36
### Improvements
* \[**Comments**]: Applied `location` filters to the inline-comments-section component as well, following the same pattern like other comment components.
* \[**UI Customization**]: Removed `!important` declarations from some component CSS, improving overall style flexibility and reducing potential conflicts.
### Bug Fixes
* \[**Comments**]: Resolved an issue related to calculating comment pin positions, ensuring more accuracy and robustness.
# null
# 2.0.40
### Bug Fixes
* \[**Comments**]: Fixed an issue related to `addContext` in the `onCommentAdd` event. This ensures that context is consistently added when a new comment is created.
# null
# 3.0.0
### Improvements
* \[**Performance**]: Rewrote change detection and state management for the entire SDK to make it more performant. This is a major update and will benefit all developers.
# null
# 3.0.1
### New Features
* \[**Comments**]: Added feature to render a comment bubble on the comment pin or triangle instead of the comment dialog. Hovering or clicking the bubble will open the comment dialog.
* `bubbleOnPin`: Whether to show bubble on pin \[default: false]
* `bubbleOnPinHover`: If the above is true, whether to show bubble when the pin is hovered over or clicked \[default: true]
Using Props:
```jsx
```
Using API:
```javascript
const commentElement = client.getCommentElement();
// To enable showing bubble on pin
commentElement.enableBubbleOnPin();
// To disable showing bubble on pin
commentElement.disableBubbleOnPin();
// To enable option to show bubble on pin hover vs only on click
commentElement.enableBubbleOnPinHover();
// To disable option to show bubble on pin hover
commentElement.disableBubbleOnPinHover();
```
Using Props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
// To enable showing bubble on pin
commentElement.enableBubbleOnPin();
// To disable showing bubble on pin
commentElement.disableBubbleOnPin();
// To enable option to show bubble on pin hover vs only on click
commentElement.enableBubbleOnPinHover();
// To disable option to show bubble on pin hover
commentElement.disableBubbleOnPinHover();
```
### Improvements
* \[**Comments**]: Increased the max allowed size of attachments in composer from 2MB to 15MB.
* \[**Comments**]: Decreased the size of triangle in comment pin from 15px to 10px.
### Bug Fixes
* \[**Comments**]: Fixed an issue where the comment dialog composer user avatar wireframe was not working.
* \[**Comments**]: Resolved minor signal-related issues.
* \[**Comments**]: Fixed Comment Dialog Attachments and Recording UI for inline comment mode, addressing aspect ratio issues.
# null
# 2.0.27
### Bug Fixes
* \[**Comments**]: Fixed an issue with changes detection in the text comment feature.
# 2.0.26
### Improvements
* \[**Notifications**]: Renamed Notification panel "all read" container wireframe components for better clarity and consistency:
This is a breaking change.
```jsx
// Old
// New
```
```html
```
# 2.0.25
### New Features
* \[**Comments**]: For [Custom Comment Annotation dropdown](/async-collaboration/comments/customize-behavior/custom-lists#1-add-custom-lists-on-comment-annotation) on comment dialog:
* Added a default placeholder for the custom dropdown and made it configurable.
* Added it to the composer by default.
```jsx
let customList = [
{ id: 'violent', label: 'Violent' },
{ id: 'inappropriate', label: 'Inappropriate' },
{ id: 'robbery', label: 'Robbery' },
{ id: 'nsfw', label: 'NSFW' },
];
const customListDataOnCommentAnnotation = {
type: 'multi', // choose from 'multi' or 'single'
placeholder: 'Custom Placeholder',
data: customList, // your customList data here
};
```
**Using Props:**
```jsx
```
**Using API:**
```jsx
const commentElement = useCommentUtils();
commentElement.createCustomListDataOnAnnotation(customListDataOnCommentAnnotation);
```
Using Wireframe:
```jsx
Custom Placeholder
```
**Using Props:**
```jsx
```
**Using API:**
```jsx
const commentElement = client.getCommentElement();
commentElement.createCustomListDataOnAnnotation(customListDataOnCommentAnnotation);
```
Using Wireframe:
```jsx
Custom Placeholder
```
**Using API:**
```jsx
const commentElement = Velt.getCommentElement();
commentElement.createCustomListDataOnAnnotation(customListDataOnCommentAnnotation);
```
**Using Wireframe:**
```html
Custom Placeholder
```
### Bug Fixes
* \[**Comments**]: Fixed an issue related to `disableReactions` in comment dialog customization.
# null
# 2.0.29
### New Features
* \[**Comments**]: Added feature to render a comment bubble on the comment pin or triangle instead of the comment dialog. Hovering or clicking the bubble will open the comment dialog.
Using props:
```jsx
```
Using API:
```javascript
const commentElement = client.getCommentElement();
commentElement.showBubbleOnHover(); // Enable bubble on hover
commentElement.hideBubbleOnHover(); // Disable bubble on hover
```
Using props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.showBubbleOnHover(); // Enable bubble on hover
commentElement.hideBubbleOnHover(); // Disable bubble on hover
```
* \[**Location**]: For multiple location setup, added support for using `data-velt-location-id` vs full location object for marking additional locations.
### Improvements
* \[**Comments**]:Refactored comment components code for better maintainability.
### Bug Fixes
* \[**Comments**]: Fixed an issue where assignee banner text color was not being applied correctly for custom statuses.
* \[**Notifications**]: Fixed an issue where the document name in the notifications documents tab was not being displayed correctly.
# 2.0.28
### New Features
* \[**Notifications**]: Added ability to customize tabs on the Notifications Panel.
```jsx
```
Using APIs:
```jsx
const notificationElement = useNotificationUtils();
const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Documents',
enable: true,
},
"all": {
name: "Custom All",
enable: false,
},
};
notificationElement.setTabConfig(tabConfig);
```
```jsx
```
Using APIs:
```jsx
const notificationElement = client.getNotificationElement();
const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Documents',
enable: true,
},
"all": {
name: 'Custom All',
enable: false,
},
};
notificationElement.setTabConfig(tabConfig);
```
```js
const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Documents',
enable: true,
},
"all": {
name: 'Custom All',
enable: false,
},
};
const notificationsTool = document.querySelector('velt-notifications-tool');
notificationsTool?.setAttribute("tab-config", JSON.stringify(tabConfig));
```
Using APIs:
```jsx
const notificationElement = Velt.getNotificationElement();
const tabConfig = {
"forYou": {
name: 'Custom For You',
enable: true,
},
"documents": {
name: 'Custom Documents',
enable: true,
},
"all": {
name: 'Custom All',
enable: false,
},
};
notificationElement.setTabConfig(tabConfig);
```
* \[**@ mention**]: Added ability to override contact list on the client side. [Learn more](/async-collaboration/comments/customize-behavior/@mentions).
* The `merge` parameter is used to determine if the new contact list should be merged with the existing contact list or replaced. Default: `false`.
```jsx
const contactElement = useContactUtils();
useEffect(() => {
contactElement.updateContactList([{userId: 'userId1', name: 'User Name', email: 'user1@velt.dev'}], {merge: true});
}, [contactElement]);
```
```jsx
const contactElement = client.getContactElement();
contactElement.updateContactList([{userId: 'userId1', name: 'User Name', email: 'user1@velt.dev'}], {merge: true});
```
```jsx
const contactElement = Velt.getContactElement();
contactElement.updateContactList([{userId: 'userId1', name: 'User Name', email: 'user1@velt.dev'}], {merge: true});
```
* \[**Document**]: Added ability to set metadata on the document while setting the `documentId`.
* You can set any key/value pair in the metadata object. `documentName` is a special field that we use to display the document name in some Velt Components.
```jsx
useSetDocument('unique-document-id', {documentName: 'Document Name'});
```
```jsx
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
}, [client]);
```
```jsx
if(Velt){
Velt.setDocument('unique-document-id', {documentName: 'Document Name'});
}
```
```jsx
this.client.setDocument('unique-document-id', {documentName: 'Document Name'});
```
```jsx
client.setDocument('unique-document-id', {documentName: 'Document Name'});
```
### Improvements
* \[**Comments**]: Improved the get comment annotations API to determine data loading state in React:
```jsx
const [loading, setLoading] = useState(true);
const commentAnnotations = useCommentAnnotations();
useEffect(() => {
if (commentAnnotations) {
setLoading(false);
} else {
setLoading(true);
}
}, [commentAnnotations]);
```
* \[**Core**]:Updated SDK CDN URL in React and Client libraries to point to `cdn.velt.dev`.
# null
## Summary
The new features in this release give you more control over the sidebar button, Recorder media options, reactions, and contact list.
## Enable Sidebar Button on Comment Dialog
By default, each Comment Dialog has a button at the bottom that will open the Comments Sidebar when clicked. You can now disable this button by setting the sidebarButtonOnCommentDialog prop to false.
## Subscribe to Sidebar Button Clicks on Comment Dialog
You can now subscribe to clicks on the Sidebar Button at the bottom of the Comment Dialog by passing a callback to the onSidebarButtonOnCommentDialogClick event handler.
## Set Recorder Media Options
You can now set the Recorder media options within Comments: (audio, screen, video, all, or none). By default, only audio recording is enabled. To set the Recorder media options, pass in a comma separated string of the features you want to enable.
## Enable Reactions
You can now enable or disable emoji reactions in Comments. By default, reactions are enabled.
## Add Comments on specific elements
You can now add a Comment on a specific element by ID. To do this, use the addCommentOnElement() API method and pass in an object with a specific schema.
## Reset Contact List using POST Method API
You can also replace an entire Group Ids contact list using a POST Method on the following API endpoint: `https://updatecontactlist-4mfhcuyw2q-uc.a.run.app`
# null
## Versions
* Latest SDK: 1.0.83
* Latest Types: 1.0.100
## Location Support in System Comments
Users can now specify location when adding a new comment using [System Comments](/async-collaboration/system-comments/setup).
```jsx
{
"data": {
"apiKey": "YOUR_API_KEY",
"authToken": "YOUR_AUTH_TOKEN",
"documentId": "snippyly-tinymce-13-oct",
// You can pass location object to set comment to any specific location
"location": {
"locationName": "YOUR_LOCATION",
// You can optionally pass version in location object
// to show comment on specific version only
// Note: if you set version then id and name fields are mandatory
"version": {
"id": "v1",
"name": "Version 1"
}
},
"targetElement": {
"elementId": "systemCommentTest",
"targetText": "we want our custom elements",
"occurrence": 1
},
"commentData": [
{
"commentText": "Replace: dolor sit amet consectetur adipisicing elit with this is test comment",
"replaceContentHtml": "this is bold text test comment",
"from": {
"email": "test@velt.dev",
"name": "Test User"
}
}
]
}
}
```
## Comment Read Receipts
Newly added comments that have not been seen by Users will now display with a small red dot.
## Specifying where comments are allowed using Class Names and Query Selectors
Expanded options for specifying where comments are allowed using class names and query selectors in addition to ids.
Added the `allowedElementClassNames` and `allowedElementQuerySelectors` properties on the `` component to provide more flexibility in determining where comments can be placed.
[See Docs](/async-collaboration/comments/customize-behavior/general-controls)
HTML:
```html
```
React:
```jsx
;
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
commentElement.allowedElementClassNames(["class-name-1", "class-name-2"]);
commentElement.allowedElementQuerySelectors(["#id1.class-name-1"]);
```
# null
## Versions
* Latest SDK: 1.0.84
* Latest Types: 1.0.101
## Option to Remove Comment Tool from Velt Video Player
We have introduced a new option to the [Velt Video Player](/async-collaboration/video-player-comments/overview) component, allowing users to [remove the Comment Tool](/async-collaboration/video-player-comments/customize-behavior). This feature offers greater flexibility in customizing the player interface according to your specific needs.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/comment-tool-velt-video-player.png)
To remove the `Comment Tool` from the `VeltVideoPlayer` control panel, set the `commentTool` property to `false`.
```jsx
```
## View Analytics Component
Introducing the [View Analytics](/async-collaboration/view-analytics/overview) component, a powerful tool for tracking visitor engagement with your documents. Simply integrate this component into your project to gain insights into document viewership. Customize it to display visitor data tailored to specific locations, enhancing your analytics capabilities.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=view-analytics)
## React Hooks Integration
We're excited to announce the addition of custom [React Hooks](/api-reference/hooks/hooks) to our SDK, streamlining the development process for our users. These hooks provide an alternative method for interacting with the SDK, offering a smoother developer experience. By utilizing hooks, you can access SDK functionalities without the need for additional wrappers, such as useEffect hooks. This enhancement simplifies your codebase and improves overall efficiency.
```jsx
import { useSetDocumentId } from '@veltdev/react';
export default function YourDocument() {
useSetDocumentId("my-document-id")
return (
Your Document Template
)
}
```
```jsx
import { useVeltClient } from '@veltdev/react';
import { useEffect, useState } from 'react';
export default function YourDocument() {
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.setDocumentId('unique-document-id');
}
}, [client]);
return (
Your Document Template
);
}
```
# null
## Versions
* Latest SDK: [1.0.87](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.103](https://www.npmjs.com/package/@veltdev/types)
## Comment Thread Component
We've abstacted out the UI we use for the threads in our `Comments`component into its own component. Now you can use our [Comment Thread](/async-collaboration/comment-thread/overview) component with your comment data to create your own custom UIs.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=comment-thread)
## Update Contact List
The [Contact List](/users/client-side/setup) is the list of emails you see when you try to `@mention` one of your teammates in a comment thread.
We now have two ways for you to keep your users' contact lists up to date.
* [Server Side](/users/server-side/api-reset-contact-list): Implement updates to your contact lists directly through a server-side API call. This method ensures that your contact lists are always synchronized with the latest data.
* [Client Side](/users/client-side/setup): For more dynamic updates, the client-side `identify()` method can be re-called with a configuration object to reinitialize your logged in users' contact list.
## PDF Viewer Support
We've added support for you to [leave comments on PDF Viewers](/async-collaboration/comments/customize-behavior/general-controls). To enable this, simply add the `data-velt-pdf-viewer` attribute on the element containing your PDF.
```jsx
```
# null
## Summary
These new features are designed to improve the collaboration experience in Velt by making it easier for users to work together on documents. Single Editor Mode can be used to prevent conflicts when multiple users are editing the same document, and Text Element Sync can be used to keep everyone in sync with the latest changes to text-based elements. These features are in beta.
## Single Editor Mode
### This feature allows you to make it so that only one user can be editing elements on the document at the same time.
* To enable single editor mode, use the enableSingleEditorMode() method.
* To disable single editor mode, use the disableSingleEditorMode() method.
* To check if the current user is the editor in single editor mode, subscribe to the isUserEditor\$() event.
* To set the current user as the editor in single editor mode, use the setUserAsEditor() method.
* To reset editor access in single editor mode, use the resetUserAccess() method.
## Text Element Sync
### This feature allows you to sync Input-type elements so when one user types, it gets filled for other users viewing the element.
* To allow Input, Textarea or Contentedible elements to sync across clients, set the data-velt-sync-state attribute to true on the element you are trying to sync.
* To disable clicks on elements, set the data-velt-sync-access attribute to false on the element you are trying to disable.
# null
## Versions
* Latest React SDK: [1.0.160](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [1.0.160](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [1.0.178](https://www.npmjs.com/package/@veltdev/types)
## Added Email ID Validation in Token
We have introduced email ID validation. This enhancement ensures that the email provided in the token matches the email used during the user identification process (identify call), adding an extra layer of security to prevent user impersonation.
# null
## Versions
* Latest React SDK: [1.0.162](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [1.0.162](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [1.0.180](https://www.npmjs.com/package/@veltdev/types)
## Added "velt-comment-pin" Component
We have introduced the velt-comment-pin component. You can use the Comment Pin component to manually set the position of a Comment Annotation.
```jsx
```
```jsx
```
# null
## Versions
* Latest React SDK: [2.0.0](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [2.0.0](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [2.0.0](https://www.npmjs.com/package/@veltdev/types)
## Added Shadow Dom Support in Notification and Video Component
We have now added Shadow DOM support in both the notification (for notification panel and notification tool) and video components.
```jsx
```
```jsx
```
# null
## Versions
* Latest React SDK: [2.0.3](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [2.0.3](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [2.0.3](https://www.npmjs.com/package/@veltdev/types)
## Added `selectCommentByAnnotationId` Method in Comment Element
A new method [`selectCommentByAnnotationId`](/api-reference/api-methods/comments#selectcommentbyannotationid) has been added to the comment element, allowing you to select a comment based on its annotation ID.
```jsx
const commentElement = client.getCommentElement();
commentElement.selectCommentByAnnotationId("COMMENT_ANNOTATION_ID");
```
```jsx
const commentElement = Velt.getCommentElement();
commentElement.selectCommentByAnnotationId("COMMENT_ANNOTATION_ID");
```
# null
## Versions
* Latest SDK: [1.0.147](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.164](https://www.npmjs.com/package/@veltdev/types)
## Updated JWT Token to include user properties such as groupId and isAdmin
We updated the body that is sent to the `https://api.velt.dev/v1/auth/token/get` [JWT Token endpoint](/security/jwt-tokens) to include user properties such as `groupId` and `isAdmin`.
The `groupId` field is used to validate that the `groupId` provided in the `identify` call is the same as the one passed to the JWT Token.
The `isAdmin` field is used to set a user as an `admin`.
| Field | Required | Description |
| ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `apiKey` | Yes | Velt API Key |
| `authToken ` | Yes | Auth Token from the Velt console |
| `userId ` | Yes | Unique user id of the user |
| `userProperties.groupId` | No | If `groupId` is provided, it will be validated with the groupId used in the identify call. Recommended if you are setting `groupIds` |
| `userProperties.isAdmin` | No | Set to `true` if you want to set user as `admin`. This is the only way to set a user as an `admin` User |
```jsx
{
"data": {
"apiKey": "YOUR_API_KEY", //Velt API Key
"authToken": "YOUR_AUTH_TOKEN", // Auth Token from the Velt console
"userId": "yourUserId", // unique user id of the user you are generating a JWT Token for
"userProperties": {
groupId: "YOUR_USER_GROUP_ID", // If groupId is provided here then we will validate it with the groupId used in the identify call
isAdmin: true, // Set to true if you want to set user as admin
}
}
}
```
## Option to force re-login user on identify call
By default when you [identify](/get-started/setup/authenticate) a user, we maintain the user auth in the browser unless you explicitly change the logged in user with another identify call.
If you are granting a user additional access or have changed some metadata about the user and want those changes to be reflected immediately, then you should re-call the identify method with `forceReset` set to `true`.
```jsx
await client.identify(user, {
forceReset: true
});
```
# null
## Versions
* Latest React SDK: [1.0.158](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [1.0.158](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [1.0.178](https://www.npmjs.com/package/@veltdev/types)
## Added `onContactSelected` API Method
We have introduced the [`onContactSelected`](/api-reference/api-methods/velt-client#oncontactselected) API method in the Contact Element. This new method allows organization clients to add selected users to their organization or document-level IAM directly.
API Method:
```jsx
const contactElement = client.getContactElement();
contactElement.onContactSelected().subscribe((data: any) => {
console.log('contact selected: ', data);
});
```
```jsx
const contactElement = Velt.getContactElement();
contactElement.onContactSelected().subscribe((data: any) => {
console.log('contact selected: ', data);
});
```
React Hook:
```jsx
import React, { useEffect } from 'react';
import { useContactUtils, useContactSelected } from '@veltdev/react';
function YourComponent() {
const contactUtils = useContactUtils();
useEffect(() => {
console.log('contactUtils: ', contactUtils);
}, [contactUtils]);
const onContactSelected = useContactSelected();
useEffect(() => {
console.log('onContactSelected: ', onContactSelected);
}, [onContactSelected]);
return (
// Your component code
);
}
```
API Method Response Payload:
```jsx
export class UserContactSelectedPayload {
/**
* Selected user contact details.
*/
contact!: UserContact;
/**
* Is user part of organization contact.
*/
isOrganizationContact!: boolean;
/**
* Is user part of document contact.
*/
isDocumentContact!: boolean;
/**
* Document access type.
*/
documentAccessType!: string;
}
```
# null
## Versions
* Latest React SDK: [1.0.159](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [1.0.159](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [1.0.177](https://www.npmjs.com/package/@veltdev/types)
## Added `organizationGroups` Support in Contacts and Autocomplete Feature
Within the contacts and autocomplete features we have added the support for `organizationGroups`.
## Added Various Methods in Contact Element
We have introduced three new methods in the Contact Element: [`enableAtHere`](/api-reference/api-methods/velt-client#enableathere), [`disableAtHere`](/api-reference/api-methods/velt-client#disableathere), and [`updateContactListScopeForOrganizationUsers`](/api-reference/api-methods/velt-client#updatecontactlistscopefororganizationusers).
API Methods:
```jsx
const contactElement = client.getContactElement();
// To enable @here for contact list
contactElement.enableAtHere();
// To disable @here for contact list
contactElement.disableAtHere();
/**
* Update contact list scope for organization users.
* @param scope ContactListScopeForOrganizationUsers[]
*/
contactElement.updateContactListScopeForOrganizationUsers(['all', 'organization', 'organizationUserGroup', 'document']);
```
```jsx
const contactElement = Velt.getContactElement();
// To enable @here for contact list
contactElement.enableAtHere();
// To disable @here for contact list
contactElement.disableAtHere();
/**
* Update contact list scope for organization users.
* @param scope ContactListScopeForOrganizationUsers[]
*/
contactElement.updateContactListScopeForOrganizationUsers(['all', 'organization', 'organizationUserGroup', 'document']);
```
# null
## Versions
* Latest React SDK: [2.0.4](https://www.npmjs.com/package/@veltdev/react)
* Latest Non-React SDK: [2.0.4](https://www.npmjs.com/package/@veltdev/client)
* Latest Types: [2.0.4](https://www.npmjs.com/package/@veltdev/types)
## Added REST APIs
We have added REST APIs so that developers have more control when it comes to accessing and setting data.
REST APIs is a featureset that will have continued expansion but the current REST APIs give improved control over **Organizations**, **Documents**, **Users**, **Organization User Groups**, and **Comments**.
More REST APIs will be coming soon!
### [Organizations APIs](/api-reference/rest-apis/organizations/add-organizations)
1. Add Organizations
2. Update Organizations
3. Delete Organizations
4. Get Organizations
5. Update Disabled State for Organizations
### [Documents APIs](/api-reference/rest-apis/documents/add-documents)
1. Add Documents
2. Update Documents
3. Delete Documents
4. Get Documents
5. Update Access for Documents
6. Update Disabled State for Documents
### [Users APIs](/api-reference/rest-apis/users/add-users)
1. Add Users
2. Update Users
3. Delete Users
4. Get Users
### [Organization User Groups APIs](/api-reference/rest-apis/users/add-users)
1. Add User Groups
2. Add Users to Groups
3. Delete Users from Groups
### [Commenting APIs](/api-reference/rest-apis/comments-feature/comment-annotations/add-comment-annotations)
1. Add Comment Annotations
2. Update Comment Annotations
3. Delete Comment Annotations
4. Get Comment Annotations
5. Add Comments
6. Update Comments
7. Delete Comments
8. Get Comments
# null
## Versions
* Latest SDK: [1.0.144](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.161](https://www.npmjs.com/package/@veltdev/types)
# 1. Major Improvements to Comments Sidebar Customization
In this update, we made several improvements to the `Customization` of the `Comments Sidebar` and `Sidebar Button`
## Support for customizing additional Subcomponents within the Comments Sidebar and Sidebar Button
We modified or added support for customizing the following `subcomponents` of the `Comments Sidebar`:
* [Filter](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/overview)
* [Filter Category](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/category)
* [Filter Close Button](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/close-button)
* [Filter Done Button](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/done-button)
* [Filter Item](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/filter-item)
* [Filter Groupby](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/groupby)
* [Filter Location](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/location)
* [Filter People](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/people)
* [Filter Priority](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/priority)
* [Filter Title](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/title)
* [Filter Versions](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/filter/subcomponents/versions)
* [Header](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/header/overview)
* [Header Status](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/header/subcomponents/status/overview)
* [Header Status Trigger](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/header/subcomponents/status/subcomponents/trigger)
* [Header Status Content](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/header/subcomponents/status/subcomponents/content)
* [List](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/list/overview)
* [List Dialog Container](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/list/subcomponents/dialog-container)
* [List Group](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/subcomponents/list/subcomponents/group)
We modified or added support for customizing the following `subcomponents` of the `Sidebar Button`:
* [Sidebar Button](/async-collaboration/comments-sidebar/customize-components/sidebar-button/overview)
* [Sidebar Button Icon](/async-collaboration/comments-sidebar/customize-components/sidebar-button/subcomponents/icon)
## Subcomponent Name Changes
We changed the names of several subcomponents:
* `` -> ``
* `` -> ``
The `Private Badge` subcomponent was moved from being a child of `Comment Dialog` to being a child of the `Comment Dialog Composer`:
* `` -> ``
* `` -> ``
# 2. Live State Sync changes
## `getLiveStateData()` now has a config option to only subscribe to new changes
By default, the [getLiveStateData()](/realtime-collaboration/live-state-sync/setup) subscription will return all changes to the shared live data object including changes that occured when the current client was not subscribed.
If you want to only receive changes starting from when the client subscribed to `getLiveStateData()`, you can pass a config object as shown below:
```jsx
const liveStateDataConfig = {
listenToNewChangesOnly: true // default is false
};
let subscription = liveStateSyncElement.getLiveStateData(LIVE_STATE_DATA_KEY, liveStateDataConfig).subscribe((data) => {
console.log('[Debug] getLiveStateData 31-05-2024-2 in html', data);
});
```
This also applies to the `useLiveStateData()` hook:
```jsx
const liveStateDataConfig = {
listenToNewChangesOnly: true // default is false
};
const liveStateData = useLiveStateData(LIVE_STATE_DATA_KEY, liveStateDataConfig)
```
As well as the `useLiveState()` hook:
```jsx
const [counter, setCounter] = useLiveState < number > ("counter", 0, {
syncDuration: 100,
resetLiveState: true,
listenToNewChangesOnly: true // default is false
});
```
## Method to listen to changes in server connection state
You can listen to changes in the server connection state with the [onServerConnectionStateChange()](/realtime-collaboration/live-state-sync/setup) method:
```jsx
const liveStateSyncElement = client.getLiveStateSyncElement();
let subscription = liveStateSyncElement.onServerConnectionStateChange().subscribe((data) => {
console.log('server connection state change: ', data);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
The server connection state will be an ENUM with the following states:
```jsx
export enum ServerConnectionState {
ONLINE = 'online',
OFFLINE = 'offline',
PENDING_INIT = 'pendingInit',
PENDING_DATA = 'pendingData',
}
```
You can also listen to changes in the server connection state using the `useServerConnectionStateChangeHandler()` hook as well:
```jsx
const serverConnectionState = useServerConnectionStateChangeHandler();
```
# 3. Updates to Popover Comments
## Added support for Popover comments using Single Comment Tool
There are now two patterns to add the `Comment Tool` component with [Popover comments](/async-collaboration/comments/setup/popover):
* (Already Existed) Add a `Comment Tool` next to each element you want to have `Popover` comments
* (New) Have a single `Comment Tool` and use it to pin a `Popover `comment on a particular element
### Single Comment Tool
If you want to have a single `Comment Tool` in a single location such as the navigation bar, you can do so as well.
To do this, add `data-velt-target-comment-element-id` as an attribute on each element you want to add comments on.
Now, when you click on the `Comment Tool` and click on the target element, it will attach a `Popover` comment to the element.
You will now notice that you can only add one `Comment Annotation` per element.
If you don't add the `data-velt-target-comment-element-id` attribute, you will be adding multiple `Comment Annotations` on the same element.
```jsx
```
# null
## Versions
* Latest SDK: [1.0.96](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.111](https://www.npmjs.com/package/@veltdev/types)
## Notifications Component
The [Notifications](/async-collaboration/notifications/setup) component can be used to notify your users in-app when they receive a reply to a comment or are `@mentioned` by a teammate.
You will need to [enable Notifications in your console](https://console.velt.dev/dashboard/config/notification) in order for Notifications component to work.
[Open in larger window](https://landing-page-demo-velt.vercel.app/?feature=notifications)
To implement `Notifications`, simply add it to your app like this:
```jsx React / Next.js
import { VeltNotificationsTool, VeltNotificationsHistoryPanel} from '@veltdev/react';
function YourComponent() {
return (
)
}
```
## Custom Notifications
Additionally, you can [send custom notifications](/async-collaboration/notifications/api-add-notification) to this component using our `https://api.velt.dev/v1/notifications/add` API end point.
Sample Post Request:
```jsx
const options = {method: 'POST', body: JSON.stringify(body)};
fetch('https://api.velt.dev/v1/notifications/add', options)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error(err));
```
Sample Body:
```jsx
{
"data": {
"apiKey": "YOUR_API_KEY",
"authToken": "YOUR_AUTH_TOKEN",
"documentId": "YOUR_DOCUMENT_ID",
"actionUser": {
"email": "actionuseremail@domain", // required
"name": "Action Username", // optional
"photoUrl": "Action User Photo URL", // optional
"userId": "User ID", // required
},
"displayHeadlineMessageTemplate": "This is main template, you can pass variables using curly brackets like this: {actionUser}, {recipientUser}, {yourCustomVariableWithStringValue}",
"displayHeadlineMessageTemplateData": {
"actionUser": {
"email": "actionuseremail@domain", // required
"name": "Action Username", // optional
"photoUrl": "Action User Photo URL", // optional
"userId": "User ID", // required
},
"recipientUser": {
"email": "recipientuseremail@domain", // required
"name": "Recipient Username", // optional
"photoUrl": "Recipient User Photo URL", // optional
"userId": "User ID", // required
},
"yourCustomVariableWithStringValue": "Variable will be replaced with this text"
},
"displayBodyMessage": "This is body message (Secondary message)",
// Pass list of users who should be notified, notification will be shown to all the users in all section in notification panel and notification history panel, but it will be shown in 'forYou' section also to notifyUsers.
"notifyUsers": [
{
"email": "notifyuseremail@domain", // required
"name": "Notify User Name", // optional
"photoUrl": "Notify User Photo URL", // optional
"userId": "User ID", // required
}
]
}
}
```
## Dark Mode on All Tool and Button Components
You can now configure Dark Mode individually on all Tool and Button Components in the Velt SDK.
Example:
```jsx
```
If you want to configure Dark Mode globally, you can also use:
```jsx
client.setDarkMode(true)
```
## Bug Fixes
* Fixed bug where you could still drag comments into restricted areas
* Fixed user email related issue for admin users in invite dialog
* Fixed typed related issues in darkMode
* Added option to disable live selection with disableFeatures()
# null
## Versions
* Latest SDK: [1.0.91](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.106](https://www.npmjs.com/package/@veltdev/types)
## Method to return the XPath of the DOM element where a Comment was added.
Introducing the [getElementRefByAnnotationId()](/async-collaboration/comments/customize-behavior/action-methods) method that returns the XPath of the DOM element where the specified comment was added.
```jsx
const commentElement = client.getCommentElement();
let elementRef = commentElement.getElementRefByAnnotationId('annotationId')
```
## Method to scroll the page to a specific Comment
Added the [scrollToCommentByAnnotationId()](/async-collaboration/comments/customize-behavior/action-methods) method that scrolls the page to the specified element where the comment was added. This functionality is contingent upon the presence of the element in the DOM.
```jsx
const commentElement = client.getCommentElement();
commentElement.scrollToCommentByAnnotationId('annotationId')
```
## Live selection style configuration
Added the ability to [customize the styling of Live Selection](/realtime-collaboration/live-selection/customize-behavior) by passing a style object to the `data-live-selection-config` attribute.
```jsx
/**
* live selection configuration:
* position:
* - horizontal: 'left'
* border:
* - border: false
*/
Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolorum, consequatur.
```
## Event Handler for Click Events on Presence Users
Implemented the [onPresenceUserClick](/realtime-collaboration/presence/customize-behavior) event handler for click events on Presence avatar circles.
```jsx
const onPresenceUserClickEvent = (user) => {
console.log("Clicked presence user: ", user);
}
onPresenceUserClickEvent(user)} />
```
## Option to disclude current user from list of Presence Users
The [self](/realtime-collaboration/presence/customize-behavior) property can be used to include or disclude the current user from the list of Presence users. By default the current user is added.
```js
```
API Method:
```jsx
const presenceElement = client.getPresenceElement();
presenceElement.enableSelf();
presenceElement.disableSelf();
```
# null
## Versions
* Latest SDK: [1.0.119](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.135](https://www.npmjs.com/package/@veltdev/types)
## Updates to `useLiveState` Hook
The [useLiveState()](https://docs.velt.dev/realtime-collaboration/live-state-sync/setup) Hook was updated to include a config object as a third parameter.
Some background on the `useLiveState()` Hook:
If you are familiar with React's `useState()` hook, we have a similar hook called `useLiveState()` that can be used to sync data realtime for specific state variables in your code.
Hook syntax:
```jsx
const [value, setValue] = useLiveState(UNIQUE_ID, INITIAL_VALUE, OPTIONS);
```
The hook takes in 3 parameters:
* `UNIQUE_ID` -> unique id in string to be synced across the screens
* `INITIAL_VALUE` -> initial value of the state
* `OPTIONS` (object)
* `syncDuration` -> debounce duration in milliseconds to sync realtime (optional, default value 50ms)
* `resetLiveState` -> Boolean to reset locatl state value on server side on initiatlize of hook (default: `false`)
The hook returns a setter and getter:
* `value` -> current state value (similar to useState hook)
* `setValue` -> function to be called to set next value (similar to useState hook)
Example:
```tsx
import { useLiveState } from "@veltdev/react";
export function MyReactComponent() {
const [counter, setCounter] = useLiveState < number > ("counter", 0, {
syncDuration: 100,
resetLiveState: true
});
return (
Counter: {counter}
);
}
```
## Option to [Enable DOM Change Detection in Comment Mode](https://docs.velt.dev/async-collaboration/comments/customize-behavior/general-controls)
By default, we skip `Comment` `DOM Change Detection` when users are in `Comment Mode` to improve performance.
However, you can turn `Comment` `DOM Change Detection` back on with the `changeDetectionInCommentMode` property.
This will make `Comment's` reposition themselves if the DOM happens to change while in `Comment Mode`.
`Default: false`
```jsx
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
// To enable change detection when comment mode is on
commentElement.enableChangeDetectionInCommentMode();
// To disable change detection when comment mode is on
commentElement.disableChangeDetectionInCommentMode();
```
## Option to [Sort Order of Comments in Sidebar](http://localhost:3000/async-collaboration/comments-sidebar/customize-behavior)
By default, the `Comments` in the `Sidebar` are ordered in descending order of when they were last updated.
You can change this sorting order with the `sort-data` property.
There are three options for sorting:
* `asc` - to show comments in descendending order of when they were last updated
* `desc` - to show comments in ascending order of when they were last updated
* `none` - to show comments in the sequence they were added
```jsx
```
# null
## Versions
* Latest SDK: [1.0.129](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.146](https://www.npmjs.com/package/@veltdev/types)
## Added Variant support in customization
If you want to have multiple versions of customized components that you can swap between, you can use [variants](/advanced-customization/variants).
To define multiple `variants`, add multiple templates of the same component `wireframe` to the `VeltWireframe` component and give them each a `variant` name.
```jsx
#Your wireframe for variant sidebar1
#Your wireframe for variant sidebar2
```
To use a specific variant, define it on the `variant` props when using the Velt component in your app.
```jsx
```
## Added Custom Dropdown Lists
You can have [custom dropdown lists](/async-collaboration/comments/customize-behavior/autocomplete) appear when certain `hotkeys` are pressed.
When you press a `hotkey` inside the `Comment Dialog` composer, it will open a dropdown list of items that you can select.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/custom-dropdown.png)
Selecting an item frop the dropdown list will create a `chip` that is placed in the comment text.
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/chip.png)
## Merged useSetLocation and useAddLocation hook
The second parameter of `useSetLocation` can be set to `true` to add additional locations instead of using `useAddLocation`.
```jsx
import { useSetLocation } from "@veltdev/react";
function YourComponent() {
// to set main location
useSetLocation(YOUR_LOCATION_OBJECT);
// to set extra location
useSetLocation(YOUR_EXTRA_LOCATION_OBJECT, true);
}
```
## Option to submit Comment on Enter Key Press
By default, pressing `enter` will add a new line and pressing `shift` + `enter` will submit a comment.
If you want to [change this default behavior](/async-collaboration/comments/customize-behavior/general-controls) so that pressing `enter` will submit a comment, you can set the `enterKeyToSubmit` property to `true`.
```jsx
```
```jsx
// API methods
const commentElement = client.getCommentElement();
// To enable enter key to submit a comment
commentElement.enableEnterKeyToSubmit();
// To disable enter key to submit a comment
commentElement.disableEnterKeyToSubmit();
```
## Added support to disable Shadow DOM on a few more components
There are now a few more components that can have the [Shadow DOM disabled](/advanced-customization/customize-css/remove-shadow-dom).
```jsx
```
# null
## Versions
* Latest SDK: [1.0.130](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.147](https://www.npmjs.com/package/@veltdev/types)
## Changes to Wireframe Subcomponent Names
### Comment Dialog
`VeltCommentDialogWireframe.ThreadCard.Files` -> `VeltCommentDialogWireframe.ThreadCard.Attachments`
`VeltCommentDialogWireframe.Composer.Files` -> `VeltCommentDialogWireframe.Composer.Attachments`
`VeltCommentDialogWireframe.CommentCount` -> `VeltCommentDialogWireframe.CommentIndex`
### Comments Sidebar
`VeltCommentsSidebarWireframe.PageMode` -> `VeltCommentsSidebarWireframe.PageModeComposer`
### Text Comment Toolbar
`VeltTextCommentToolbarWireframe.Seperator` -> `VeltTextCommentToolbarWireframe.Divider`
### Options Dropdown
`VeltOptionsDropdownContent.MakePrivate.On` -> `VeltOptionsDropdownContent.MakePrivate.Enable`
`VeltOptionsDropdownContent.MakePrivate.Off` -> `VeltOptionsDropdownContent.MakePrivate.Disable`
### Comments Sidebar
`VeltCommentsSidebarWireframe.PageMode` -> `VeltCommentsSidebarWireframe.PageModeComposer`
# null
## Versions
* Latest SDK: [1.0.133](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.150](https://www.npmjs.com/package/@veltdev/types)
## Added Skeleton loader subcomponent to Comments Sidebar Wireframe
The Comments Sidebar Wireframe now has a [Skeleton loader subcomponent](/async-collaboration/comments-sidebar/customize-components/comments-sidebar/overview) that you can modify.
## Added functionality to search by name in @mention dropdown
You can now search by name, in addition to just email, in the `@mention` dropdown
## Added option to show resolved Comments on DOM
By default, resolved Comments are not shown on the DOM.
There is now an option to [show resolved Comments on the DOM](/async-collaboration/comments/customize-behavior/visibility-controls).
```jsx
// Html
// React
// API Methods
const commentElement = client.getCommentElement();
// To show resolved comments on dom
commentElement.showResolvedCommentsOnDom();
// To hide resolved comments on dom
commentElement.hideResolvedCommentsOnDom();
```
## User background color
You can now pass in a color when you identify a user using [client.identify()](/get-started/setup/authenticate)
```jsx
const user = {
userId: uid,
name: displayName,
email: email,
photoUrl: photoURL,
color: colorCode, // Hex code value goes in the place of colorCode
groupId: groupId // this is the organization id the user belongs to.
};
await client.identify(user);
```
## Added additional subcomponents to the Comments Dialog Wireframe
The following subcomponents were added to the Comments Dialog Wireframe:
* [Priority Dropdown](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/priority-dropdown)
* [Status Dropdown](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/status-dropdown)
* [Options Dropdown](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/options-dropdown)
* [Reaction Tool](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/reaction-tool)
* [Reaction Pin](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/reaction-pin)
* [Reactions Panel](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/reactions-panel)
* [Reactions Pin Tooltip](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/reaction-pin-tooltip)
* [Autocomplete Option](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/autocomplete-option)
* [Autocomplete Chip Tooltip](/async-collaboration/comments/customize-components/comment-dialog/subcomponents/autocomplete-chip-tooltip)
## Added support to set custom reactions
You can [set custom reactions](/async-collaboration/comments/customize-behavior/multimedia) by passing a map that contains information about the reactions you want to add.
The map keys should be the reaction ID, and the map value should contain an object with either an `url`, `svg`, or `emoji` field to represent the reaction icon you want to use.
```jsx
const customReactions = {
"URL_EMOJI_ID": {
"url": "EMOJI_URL"
},
"SVG_EMOJI_ID": {
"svg": "EMOJI_SVG"
},
"TEXT_EMOJI_ID": {
"emoji": "🤣" // emoji as a text
}
};
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
const customReactions = {
"URL_EMOJI_ID": {
"url": "EMOJI_URL"
},
"SVG_EMOJI_ID": {
"svg": "EMOJI_SVG"
},
"TEXT_EMOJI_ID": {
"emoji": "🤣" // emoji as a text
}
}
commentElement.setCustomReactions(customReactions);
```
## Changed type in VeltCommentDialogWireframe.Composer.ActionButton from `file` to `attachments`. Keeping legacy support for `file`.
In the [Comment Dialog Wireframe](/async-collaboration/comments/customize-components/comment-dialog/overview), we changed the type from `file` to `attachments`
`` -> ``
## Added support for customizing attachments in Comment Dialog
The `VeltCommentDialogWireframe.Composer.Attachments` and `VeltCommentDialogWireframe.ThreadCard.Attachments` subcomponents within the [Comment Dialog Wireframe](/async-collaboration/comments/customize-components/comment-dialog/overview) now support customization.
## Added method listen to Comment Selection changes.
The [onCommentSelectionChange()](/async-collaboration/comments/customize-behavior/event-handlers) method can be used to listen to Comment selection changes.
```jsx
const onCommentSelectionChange = (data) => {
console.log('onCommentSelectionChange', data);
}
onCommentSelectionChange(data)} />
```
Callback response schema:
```jsx
export class CommentSelectionChangeData {
selected!: boolean;
annotation!: CommentAnnotation;
}
```
API Methods:
```jsx
const commentElement = client.getCommentElement();
let subscription = commentElement.onCommentSelectionChange().subscribe((data) => {
console.log('onCommentSelectionChange: ', data);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
Using Hooks:
The `useCommentSelectionChangeHandler` hook can be used to subscribe to Comment selection changes.
```jsx
import React, { useEffect } from 'react';
import { useCommentSelectionChangeHandler } from '@veltdev/react';
function YourComponent() {
const commentSelectionChange = useCommentSelectionChangeHandler();
useEffect(() => {
console.log('commentSelectionChange', commentSelectionChange);
}, [commentSelectionChange]);
return (
<>
Selected Comment: {commentSelectionChange.annotation.id}
>
);
}
```
## Added prop to enable or disable Comment Pin Highlighter
The API Methods already existed, but we added a prop to enable or disable the [Comment Pin Highlighter](/async-collaboration/comments/customize-behavior/ui-controls)
```jsx
// React
// API method was already added before, adding here just for refernece purpose
const commentElement = client.getCommentElement();
// To enable comment pin highlighter
commentElement.enableCommentPinHighlighter();
// To disable comment pin highlighter
commentElement.disableCommentPinHighlighter();
```
## Added flag to merge location in `updateLocation` cloud function
You can [update a Location's object fields](/key-concepts/locations/setup/api-update-location) while keeping the location id the same using an api call.
Set the `merge` flag to `true` if you want to merge the new `location` fields into the old `location` fields.
Set the flag to `false` if you want the new `location` object to completely replace the old `location` object.
```jsx
{
"data": {
"apiKey": "YOUR_API_KEY",
"authToken": "YOUR_AUTH_TOKEN",
"documentId": "YOUR_DOCUMENT_ID",
"migrate": {
"oldLocation": YOUR_OLD_LOCATION_OBJECT_HERE,
"newLocation": YOUR_NEW_LOCATION_OBJECT_HERE
},
"merge" : true
}
}
```
# null
## Versions
* Latest SDK: [1.0.137](https://www.npmjs.com/package/@veltdev/react)
* Latest Types: [1.0.157](https://www.npmjs.com/package/@veltdev/types)
## Simplified way to modify wireframe subcomponents
You can now modify subcomponent wireframes using two patterns:
#### a. `Parentless` - Modifying the subcomponent without its parent within the `` component. (recommended)
In this example, we modify the Header subcomponent, which is a subcomponent of the Velt Comment Dialog. In this pattern, we just put the Header subcomponent in the root of `` and modify it. We do not need to add its parent component or any of its siblings.
Example:
```jsx
Custom HTML
```
#### a. `With Parent` - Modifying the subcomponent within the context of its parent within the `` component. (not recommended)
In this example, we modify the Header subcomponent, which is a subcomponent of the Velt Comment Dialog component. In this pattern, we include its parent component and siblings. This makes it easier to modify several sibling components at once.
Example:
```jsx
{/* Skeleton */}
...
{/* Header */}
Custom HTML
{/* Filter */}
...
{/* List */}
{/* Empty Placeholder */}
...
{/* Page Mode Composer */}
...
```
If you modify the component in both the `Parentless` and `With Parent` pattern, the `With Parent` pattern will override the `Parentless` pattern.
## Detect if Velt SDK is initialized
[To detect if the Velt SDK is initialized](/get-started/setup/advanced), subscribe using the following method:
```jsx
let subscription = client.getVeltInitState().subscribe((veltInitState: boolean | undefined) => {
console.log('Velt Init State:', veltInitState);
});
```
To unsubscribe from the subscription:
```jsx
subscription?.unsubscribe()
```
You can also the use `useVeltInitState()` hook:
```jsx
import { useVeltInitState } from '@veltdev/react';
function YourComponent() {
const veltInitState = useVeltInitState();
useEffect(() => {
console.log('Velt Init State:', veltInitState);
if (veltInitState) {
// Velt state is initialized, so user can perform any action here
}
}, [veltInitState]);
}
```
# null
# 3.0.56
### Improvements
* \[**Comments**]: Improved the system grouping logic for locations.
* \[**Comments**]: Enhanced `updateContext` logic to prevent unnecessary updates if the passed context object remains unchanged.
* \[**Comments**]: Exposed `commentAnnotations` variable in the comments sidebar so that it can be used in `Velt Data` and `Velt If` Components.
### Bug Fixes
* \[**Comments**]: Fixed an issue where adding the `Copy Link` wireframe component in the header options menu generated undefined Comment URLs.
# null
# 3.0.63
### Bug Fixes
* \[**Comments**]: Fixed issue where bottom sheet was incorrectly opening in the sidebar on mobile devices.
# 3.0.62
### Features
* \[**Comments**]: Fixed issue where edit comment composer was not appearing in the sidebar.
* \[**Comments**]: Added offset property to Comment Player Timeline. This allows comment bubbles to be positioned relative to both parent and child video clips.
```jsx
```
```html
```
### Improvements
* \[**Dynamic Hooks**]: Now the LiveStateSync and Views hooks support dynamic input parameters for non-primitive data types.
### Bug Fixes
* \[**Comments**]: Fixed issue where edit comment composer was not appearing in the sidebar.
# null
# 3.0.64
### Bug Fixes
* \[**Comments**]: Fixed an issue where `onCommentSidebarData` event was getting triggered multiple times on sidebar clicks.
* \[**Comments**]: Fixed an issue where empty placeholder was not being displayed in the sidebar for page mode and custom action filters.
# null
# 3.0.65
### Improvements
* \[**Comments**]: Added support for light mode in the contact chip tooltip.
* \[**Comments**]: Removed unnecessary filtering in the sidebar when comment annotation selection changes.
# null
# 3.0.66
### Features
* \[**Comments**]: Added support for passing Comment Annotation objects to Standalone Comment Thread component.
* When using annotations from other documents:
* Comments will be read-only
* Reactions and recordings will not be rendered
* This enables creating Kanban boards by fetching comment annotations from multiple documents using our REST APIs.
```jsx
```
```html
```
* \[**Comments**]: Added `shortUserName` feature to control display of user names.
* For long names, this will first create an initial of the second name and if the name is still long, it will truncate it with ellipses.
* It's enabled by default.
**Using Props:**
```jsx
```
**Using API:**
```js
const commentElement = client.getCommentElement();
commentElement.enableShortUserName();
commentElement.disableShortUserName();
```
**Using Props:**
```html
```
**Using API:**
```js
const commentElement = Velt.getCommentElement();
commentElement.enableShortUserName();
commentElement.disableShortUserName();
```
# null
# 3.0.57
### Bug Fixes
* \[**Notifications**]: Optimized and fixed issues related to loading notifications on `documentId` and `organizationId` change.
# null
# 3.0.58
### Improvements
* \[**Comments**]: Added ability to sort inline comments:
* `asc`: Show comments in ascending order of last updated timestamp
* `desc`: Show comments in descending order of last updated timestamp
* `none`: Show comments in order of creation (default)
```jsx
```
```html
```
* \[**Comments**]: Further improved how empty comments are handled:
* Empty comments are now hidden from the sidebar and sidebar button count
* In popover mode, clicking a new comment button discards any previous empty comment
* \[**Comments**]: Added delete option for each comment in annotations when user has admin privileges
# null
# 3.0.59
### Bug Fixes
* \[**Comments**]: Fixed several issues with the comment dialog and inline comments:
* Fixed cursor position not being set correctly when focusing comment input
* Fixed an issue where editing a comment and saving it as a draft created a new comment
* Fixed an issue where sometimes comment pin was visible on the inline comments section
# null
# 3.0.61
### Features
* \[**REST APIs**]: Added advanced queries and pagination for GET endpoints.
* You need to upgrade to version 3.0.61 and enable this in your developer console.
* Check out the [V2 REST APIs endpoints](/api-reference/rest-apis/comments-feature/comment-annotations/get-comment-annotations-v2) for more information.
# 3.0.60
### Features
* \[**Comments**]: Added ability to enable/disable recording transcription feature on recorder:
**Using Props:**
```jsx
```
**Using API Methods:**
```javascript
// Using comment element
const commentElement = client.getCommentElement();
commentElement.enableRecordingTranscription();
commentElement.disableRecordingTranscription();
// Or using recorder element
const recorderElement = client.getRecorderElement();
recorderElement.enableRecordingTranscription();
recorderElement.disableRecordingTranscription();
```
**Using Props:**
```html
```
**Using API Methods:**
```javascript
// Using comment element
const commentElement = Velt.getCommentElement();
commentElement.enableRecordingTranscription();
commentElement.disableRecordingTranscription();
// Or using recorder element
const recorderElement = Velt.getRecorderElement();
recorderElement.enableRecordingTranscription();
recorderElement.disableRecordingTranscription();
```
### Bug Fixes
* \[**Comments**]: Fixed range error that occurred when recording without comment text
# null
# 3.0.27
### New Features
* \[**Comments**]: Added a prop to enable confirmation before deleting a reply.
Using Props:
```jsx
```
Using API:
```javascript
const commentElement = client.getCommentElement();
commentElement.enableDeleteReplyConfirmation();
commentElement.disableDeleteReplyConfirmation();
```
Using Props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.enableDeleteReplyConfirmation();
commentElement.disableDeleteReplyConfirmation();
```
* \[**Comments**]: Added a callback method for when a comment link is copied. You can use this to track when a comment link is copied or show a confirmation toast.
Using Hooks:
```jsx
const commentLink = useCommentCopyLinkHandler();
useEffect(() => {
console.log('commentLink', commentLink);
}, [commentLink]);
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.onCopyLink().subscribe((commentLink) => {
console.log(commentLink);
});
```
Using API:
```jsx
const commentElement = client.getCommentElement();
commentElement.onCopyLink().subscribe((commentLink) => {
console.log(commentLink);
});
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.onCopyLink().subscribe((commentLink) => {
console.log(commentLink);
});
```
* \[**Comments**]: Added a minimal Actions Dropdown in Multithread comment dialog. It contains actions like 'Mark all as read', 'Mark all as resolved'.
### Improvements
* \[**UI Customization**]: Renamed the `velt data` prop to `field` (old: `path`) to improve readability. This is backward compatible.
```jsx
```
```jsx
```
# null
# 3.0.40
### Bug Fixes
* \[**Mentions**]: Resolved an issue where `atHereDescription` was not rendering for non-organization users.
# 3.0.39
### New Features
* \[**Notifications**]: Added API to mark a single notification as read by notificationId.
```javascript
const notificationElement = client.getNotificationElement();
notificationElement.markNotificationAsReadById("notificationId");
```
```javascript
const notificationElement = Velt.getNotificationElement();
notificationElement.markNotificationAsReadById("notificationId");
```
* \[**Debugger**]: Added call to `setDocument` method to the debugger.
### Improvements
* \[**Mentions**]: Added `atHereDescription` to the `description` field of the @mention data object.
### Bug Fixes
* \[**Notifications**]: Fixed an issue where the "For You" tab was not displaying the updated document name on initial load.
# 3.0.38
### New Features
* \[**Mentions**]: Added styling for @mention in the composer when adding or editing a comment.
### Improvements
* \[**React Hooks**]: Updated client and comment related hooks to support dynamic input values.
### Bug Fixes
* \[**Mentions**]: Fixed an issue in the `updateContactList` API where the passed contact list data was being mutated directly.
* \[**Mentions**]: Resolved an issue where @mentions with special characters were not working correctly.
# null
# 3.0.42
### New Features
* \[**Live Selection**]: Added new configurations to customize the UI and behavior of the live selection feature:
* Enable/disable user indicator
* Set user indicator position
* Set user indicator type
* Enable/disable default elements tracking
* Enable/disable default styling
* Earlier the live selection was triggered on click, now it is triggered on keyboard actions as well.
* [Learn more](/realtime-collaboration/live-selection/customize-behavior).
# 3.0.41
### New Features
* \[**Comments**]: Added ability to apply custom filtering, sorting and grouping in comments sidebar.
* Here is the overview on how it works:
* Enable custom actions in the comments sidebar.
* Add action buttons to the sidebar wireframe.
* Implement callback and event handlers to handle custom filtering, sorting, and grouping logic.
* Set custom filtered data in the comments sidebar.
* [Learn more](/async-collaboration/comments-sidebar/customize-behavior#2-custom-filtering-sorting-and-grouping).
# null
# 3.0.44
### Improvements
* \[**Comments**]: Added "This page" label to the sidebar filters.
* \[**Comments**]: Added React wireframe component for Navigation button:
```jsx
```
```html
```
# 3.0.43
### New Features
* \[**Comments**]: Added "focused thread mode" in the comments sidebar:
* In this mode, when you click on a comment in the sidebar, it will open the thread in expanded view within the sidebar itself.
* Other threads and actions like filters, search etc are hidden behind a back button.
* Turning this on also adds a navigation button in the comment dialog. Clicking it will navigate to the comment and also trigger a callback.
* If you had previously used a wireframe for the comment dialog, you will need to add the [navigation button wireframe](/async-collaboration/comments/customize-ui/comment-dialog/subcomponents/navigation-button) and the [focused thread wireframe](/async-collaboration/comments-sidebar/customize-ui/comments-sidebar/subcomponents/focused-thread).
```jsx
```
**Handling the navigation button click:**
```jsx
// event handler for when a comment is clicked on
const onCommentNavigationButtonClick = (event) => {
console.log('onCommentNavigationButtonClick', event);
//handle custom navigation by getting location if you have used Locations
const { pageId } = event.location;
//handle custom navigation by getting context if you have used addContext()
const { pageId } = event.context;
yourNavigateToPageMethod(pageId);
};
```
```html
```
**Handling the navigation button click:**
```javascript
const commentSidebarElement = document.querySelector('velt-comments-sidebar');
commentSidebarElement.addEventListener('onCommentNavigationButtonClick', (s) => {
console.log('onCommentNavigationButtonClick', s.detail);
});
```
* \[**Comments**]: Added standalone thread wireframe component:
```jsx
{/* Velt Comment Dialog Wireframe */}
```
```html
```
* \[**Live Selection**]: Add ability to get live selection data for the document:
**Using Hook:**
```jsx
const liveSelectionData = useLiveSelectionDataHandler();
useEffect(() => {
console.log('liveSelectionData', liveSelectionData);
}, [liveSelectionData]);
```
**Using API:**
```javascript
const selectionElement = client.getSelectionElement();
selectionElement.getLiveSelectionData().subscribe((liveSelectionData: LiveSelectionData) => {
console.log("liveSelectionData: ", liveSelectionData);
});
```
**Using API:**
```javascript
const selectionElement = Velt.getSelectionElement();
selectionElement.getLiveSelectionData().subscribe((liveSelectionData: LiveSelectionData) => {
console.log("liveSelectionData: ", liveSelectionData);
});
```
* \[**Comments**]: Added standalone `Velt Comment Text` component:
* When you put any text inside this component and provide an annotationId, it will automatically highlight the text and attach the comment to it.
```jsx
{/* your content here */}
{/* your content here */}
```
```html
```
### Improvements
* \[**Notifications**]: Changed the default maximum days for which Notifications should be displayed from 30 days to 15 days.
* \[**Notifications**]: Added complete document and organization metadata objects to the Notification metadata object.
# null
# 3.0.45
### New Features
* \[**Comments**]: Added config to restrict resolve action to admin users only:
**Using props:**
```jsx
```
**Using API:**
```javascript
const commentElement = client.getCommentElement();
// To enable resolve status access admin only
commentElement.enableResolveStatusAccessAdminOnly();
// To disable resolve status access admin only
commentElement.disableResolveStatusAccessAdminOnly();
```
**Using props:**
```html
```
**Using API:**
```javascript
const commentElement = Velt.getCommentElement();
// To enable resolve status access admin only
commentElement.enableResolveStatusAccessAdminOnly();
// To disable resolve status access admin only
commentElement.disableResolveStatusAccessAdminOnly();
```
### Improvements
* \[**Comments**]: Added ability to @mention emails not present in the contact list.
* \[**Comments**]: Implemented focus on composer when clicked anywhere within the composer.
# null
# 3.0.47
### New Features
* \[**Webhooks**]: Added configuration option to encrypt webhook payloads using a secret key.
* Configure this option in the [Velt Console](https://console.velt.dev/dashboard/config/webhook).
* Encryption details:
* Payload encryption: AES-256-CBC
* Key encryption: RSA with PKCS1 OAEP padding and SHA-256 hash
* Public key format:
* Provide only the base64-encoded key string, without PEM headers/footers
* Recommended key size: 2048 bits
* Example of setting up encryption for Node.js:
```js
{
"encryptedData": "1rtsa9UVvXzkP+u0ax2TOlz6xKcwKXhmtHyQF1I4II8X4n9uYb944Q/6AfUNFc2zQj9+AWJIV1Gtoo0j+j5VI8qS4kCVnP4In6v0I3wVECldgZsNAwwD4wKp85OJZUJL4scQmJJK+XXmMNGOW094BcIIa6zKRqYKja5RBm5zEj3k1qsP3WZkUXpggJ4FNuHkWX2nkoDLP5Rby6CY186TEeBIxY+aKS6FyWmOiDDC6ZfuY++BFNJbksNvsbBogDqHB2qa30nK9oEcOKSsXdU4AYof/mPOG01fK2diK3vyk4qcL83mJ0cXm7+SbM+FBFeJpdR+A7iIez1XrdnGlAqppnSfDoNBv2WZ/lRyZJWOyW7QHySMNTn746+JDr8oltIBDVUx5c2m8A/YeQ6E3wWEjjRZcfz3GNSzpEx+jqeNxS0StN7BUXUyHt3786EaXiUtjb2OtrP56mlidXytdHhPZPOy7stRwHnwgXfm5aLsS2yJSs3gSSUubL+ka4fhaJsqxtgXQATSh0RtNXSmAbx930DKn2DipbP23fJRduju/GP1nHnKuy8bOuyB5Du//RrysvKVC4+lMd4mVIc7cSXe25qcPjJFZGpJtJdkNwOZoWCmxMSdR32HBgo7KWJeOWqnWyuLdjQOaxol+JtTu8lopeQk7qfncEXMLcT7YRVQ4t1LZ5T9o4pZEtaOg1LwyX58VQS1OHvgBFWlEPxLfdS1r4c1YzMXLNA4sfYEp06Z11IlEFVCtWobK5//tLc+sIpwfMzdJ3VtVl9Z2XB9kASlnHf88eOdtzvn5A0CRhVBY/v855CttAy/WlPINtXxXSxm9oVMjrBFueWAZ3LQiXDl25to62L5i0NR93zEBKj1BG8egy3F27o8s5kcvrwpc3NGrmDe7x3S11noDAFsxZRWpHnRIapHcsrLWOjWVEumvUxlApKGKL3Ax80XBoN+aTNG4SXGq3dRHSneIs/MNSb0BGWoOD5U5ow58R1tvpzJHtLLnmesL1Vhr23Cug8KHU2q7+e8AnGGPTJIRKfVXjocMDclhDAk5/nuvtUTYG/hRZEQ1yCx3T7H08I6GvyOv4ErtKr+r883hXSYzf1K9eqk7de5mnmxwSEiAh0zagvZ+lMYhWpayeo+xHvtoyzfTsLNyXKc6AYZxfoIVK6UuBfkDnXiAh+NuJDa3wKwig13gQX8GmdJXeSSatI6uuXI1IU5xKIXysaHeAOaHfni+cfDgvUZTtVbWc1qDcNOVEUSl9KsjOUUgdzvST1tJ1ezMNZFbhlrPB3t5y0XvM9QQh1GyyeABxHl8nH/Icrp2Shf5vBntNbRZ3PlzK7nVtgTxXaKhZnGobwY7uruPpahNfkEi83JvOOnHeHBMXrVMAr8GHDRi8099wzvJRHYcb2p6eWocQsDV1X6tcTLuxj3EHGwykWREkkTDQ5C/F40n97PP0U2cxSGJIMePUwgAYw5OFo0dJMsU1HvXjm+2JoO8DkdwPl3Bc9F22trvsA3QecUCKQDGMTuFrFxtlubtJYtVl7w3pBST0SCKx3G2QiycRz0FMWv2FJpazQl6jE4xEqeKf7fiUn/QIo4Levk745LPhfr2tzlXbkdZ2q9TtmSAs5hjpK7ndswbIbvV8Ju5V8mDJXSR0y0NKG2C/8/vTB0xfqYtW/Bv3cXj6do9UQzP6fOFC4SGvYh/l8yohJmCTFq0tETqvZr9Atw9ZOz2cIBFx76wlS/eR9iB/JZ3DGM+2THC6Mjv70ipWX32UW7620Bb5KONm3Vw0eeIHckUn6QaHGfFL/URT6mr7YCJhG5lZynWYZcLv/ffWuFcSmO9p0xCrwqqPEjdaaGs52mqmA4Ikt9MulKAEp6p65V1vxt7Tdy6m9UVjzbEy1zFuU9iOHBAAaj6A8Mj1EEUe6sNx3fLHnC2c0+2Zf3eUxMZPm5dQZPOUXLI28yoCliBIhTYTSh7ATULDDvcnNMs/ziuG7WT/U1wuIHkT5kEE73tnG1EZY4RDODbQobmpBegcuUEh64HEGS7+aK/KPYWxFxWW5oVd0Dc7kvpariXqEhlNdDY65b2T8uBw8bI/HrfvT8d0EnsPz26B1xKZYqyusWnlR+10KdYzPNoupx8vWk74PW8zI5qlcV497SPtvn12a3wvZ8adJzMuP4hsBoKHG/M2nf0lOMbo1gcbHbT0FqcHE3mixY3lU+UnNC5jpmNCs1tK8yqeQdVtHE3YM4Y5SsnBTJddUWVpUxZ6rlU+H2NW/uGcDLBs3HmERTn1l6E1mmqKB2kPA/+Y/YbILXNojbkgRE/3lki5kX4+pjHDxF/mWEEeXpjIl4yKG97mVS2J0dGoJ5CqLv6/CdHhtwu35UydBVDVGHywufVLwPgEiDA9RklM/bQw3ojdlTrn6+irDcz8/Tj7KmK2votLaN6yIEM8Ex2htyBlyX/47eEsh63nSNwSx+uPcTxjH9N5cJpWzJ2KcBMIqZsWOTgISBUndgRdoVTFySY2XwbHlDjh8RCLLBsYRhvOK+nvNqEBnrfzz81B/sqDO1whQDTKT3ZcFnZouaVImRGHcOt0sRioq/JGHAHzRjyc/V9Gb/zTlI8QQob5y5k7dfReAy1rGdkeIa3LXSwWGz8hDjEnGsGGIC4evdiefgoJHkhzEywi/QUEOOnqms/0BzexbLP+89qMgGMlEbA9iLAW/BZgsAkxm+NHqGNtz9HDJStpqewElgjMQ+wV3TUGbrmY0O/FyQn/CXyhXjdRC0/5S1tZnzBMyolHF2a5L5EAzGck2MuV7TgLs6LcvGm7kIeq0vmBCkiUB4IBHMhraU7Ba+cC+CW7tDK0Tkanri5KSMXSXamJpU869Jcsk1JLm69ATMl3eIb5rPx5+GbPUrRogEUP3HQeLMQP8jjq6fVwzGPQByF70t0fE+Z23NuCLzhVss0YkMmzcKK8GjKCJ0vnCA0qanxovpDgCOHjgxvy44N+QNWfUynIKVHS9m7FDE3RgKf7rOfSM9vJ7F/KWo7kywi36ajuFbWcON/MTvlpPUhGm5dboiz3vyfpTWkQbd9XX7SPVBWCkvGg+A87R7RSN8bsWbmYm5m2wt3jrkBVSDn5FV3rek6X0GSpTDTWJ9ktmjKtshplXn7fx7XAKtS4hpEMGhZwi/LWvfTsGqOJlqi2FwYPLI7SVunch2VSfssejrfwxJHPqF50wTv6ax28lp7wToqsVunZprdhyY++gds/LAz083dZLM3EYcbHuGVXiNRFxptpiQNjEnyjZX0fc8UF1W2icDt7Gd5Pp2ckaPERLE+tJ+ackMxomH2/HjFB3XRXlDCoKuljtJ2cbw/gVPmHtV7Qw2w6tWaCzYP3g1D47BlrIqBV4RWjcPRjthfcWPnwUSSHwlJ4dLMQ+cJ402ol+HUukAKpkh5lcjME0uaD8KKReD/Ee9r4kubIR7z9JViXjnJJl3Jxr6KtK3abrg8cG8qVFRr5NDhxbfs9NY/zGDvbgt0GMWXRTi4oMrSkDKthZSWjVezDzPk11AMQ1E+SJSoSXgwUl1rbWPg0O29prkQdfdKQmZcaO5oj7+f3kSPsIOE9+Qn43VOxOWWybkCzSvEbzLgmuov5C8EWYeJgh13qDcNSwNdt4PgAqIq+tikKNUo9qeM9/q20an+i20fatPAcvrRes+xxnIBXmlPDCj02THjX4EulV2KE+nNxFnCrNvFKYp2bEAegJ2neqfeefDDDhn+t7OK9/73v3O3qnEwSyBlt+pEyHfLjv3Cm7Ik7JA5NUQ/nsS3JdC8OYy2i1DWSvi1qsP3ixAVCR7qBVdoOF2Lv5y2GWrJ0EvVcGqaPBnUezMGMdozNjreschNJvRlp3D72dGGQgs00GHyHbIQ5wicC5p+PiZ2z1EUBN7DiDy9ShQPKEDJtISiSrSaPkDPKpW7SxmSfDaLOIxEy4daAupV0gj7yTtrkpEvJjRECpa0kuKFP3/eFVVp/nIjWDzFASfDvYiry90dDrvLxO3tosuvMVfhXcOy/zbyeCkObaFgc3OkO4z2r4X4Vwt18BoRAammiEfgCbnhywl/CmLrSwV1qSjUgALh/XUPkqXCkqerNjYTlZw5NdRUKmheUXHYGwo4Z+xPfDtiHk1N5vRgNL9/qXsgt813spju9kDMGQGiXlrOgIyhArHR5p2B4S3FjRQ/lEoP5+5wN+9tBKYrR79sZXNS8CwR0BPrOoY9GQCYFdxrBtyH6KOWg29FVXNodt2Yvot7ktofcen1zwQJOAr0KTyqF9/TIltO+hS7swSzZMjV368SEPYjrtXfnXNWYltOS2zJAWYeqr0XLrL+iHbbOQLC7Rk0mnizmUt9wdefz4MtfXZNcdKR4LPsOqYyIz5ux90XiCbvcNZJaRa2/dzecv/koLQPbKzFPGxKiUOsHAa5SEGgbWFZE4Y9CBFS4nCuEOgUnVz9XtFAEP4dazc2cxjYLVzaG5msOiOY1O5ZygYMeVZfdKaITg7gMPbkL3Lpzo7QBMXcHmT5YAUeNaSbHxvgg45Jn8r7W72EQP9tF7SPKiPvxo91xkB7MA3JOcZXC1qymTUWqjO038wSShK48kE+qgu7V9rjP5fOCDW3+3338eifxqS7Zq6FSO053c5W2c8wFR4iw==",
"encryptedKey": "OzSHFXzrXFC5wDvM5NPRkriY/NaC/USvFUPE+f4NZ30tiD2qb8sJM2XT2K7uNIZ05uDLfsJ6/BbEoYC1SOPXcFJMYqRiYFiI9RWrNgR4EtPWZ84RgrmxGcZZjzSqHzjuls8g++cuqJGRV+ePbTRH+z2OuZZu0vMKZiemaZpHL46Ewi9HUnbRDXvOlKFFHmQm5tayZ7m7Mv5iu4T5R3DPEAHlZnGqtP98ToLxUJUS2Eku/iLHXRmhmZXn55Qt5GYiyss8P5/miPqJCu1QG0CStn5Nsl4KvU+I4QYAOcMFWWUAGofOwPWtt8vPh8Bx+7t7BbayKpA4ZUEWWAjC+zASxg==",
"iv": "SHM0UHU5WXoyTG03TnExWA=="
}
```
```javascript
const crypto = require('crypto');
/**
* Decrypts the symmetric key using the provided private key.
* @param {string} encryptedKey - Base64 encoded encrypted symmetric key
* @param {string} privateKey - RSA private key
* @returns {Buffer} Decrypted symmetric key
*/
function decryptSymmetricKey(encryptedKey, privateKey) {
try {
const encryptedSymmetricKey = Buffer.from(encryptedKey, 'base64');
const decryptedSymmetricKey = crypto.privateDecrypt(
{
key: `-----BEGIN RSA PRIVATE KEY-----\n${privateKey}\n-----END RSA PRIVATE KEY-----`,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: 'sha256',
},
encryptedSymmetricKey
);
return decryptedSymmetricKey;
} catch (error) {
console.error('Error decrypting symmetric key:', error);
throw new Error('Failed to decrypt symmetric key');
}
}
/**
* Decrypts the webhook data using the provided symmetric key and IV.
* @param {string} encryptedWebhookData - Base64 encoded encrypted webhook data
* @param {Buffer} symmetricKey - Decrypted symmetric key
* @param {string} payloadIv - Base64 encoded initialization vector
* @returns {Object} Decrypted webhook data as a JSON object
*/
function decryptWebhookData(encryptedWebhookData, symmetricKey, payloadIv) {
try {
const iv = Buffer.from(payloadIv, 'base64');
const decipher = crypto.createDecipheriv('aes-256-cbc', symmetricKey, iv);
let decryptedData = decipher.update(encryptedWebhookData, 'base64', 'utf8');
decryptedData += decipher.final('utf8');
return JSON.parse(decryptedData);
} catch (error) {
console.error('Error decrypting webhook data:', error);
throw new Error('Failed to decrypt webhook data');
}
}
// Example usage:
// const decryptedKey = decryptSymmetricKey(encryptedKey, privateKey);
// const decryptedData = decryptWebhookData(encryptedWebhookData, decryptedKey, payloadIv);
```
### Improvements
* \[**Comments**]: Improved time display in comment dialog by removing "just" from timeago text to make it more concise.
# 3.0.46
### New Features
* \[**Comments**]: Added "Seen" Feature in the comment dialog. It shows which users have seen the comments. It's automatically enabled in the default component.
* If you had previously used a wireframe for the comment dialog, you will need to add the [seen component wireframe](/async-collaboration/comments/customize-ui/comment-dialog/subcomponents/body/subcomponents/threadcard).
![](https://mintlify.s3-us-west-1.amazonaws.com/velt/images/seen-feature.png)
* \[**Comments**]: Added a dynamic attribute to identify if the current user is the author of a comment inside the comment thread:
* Use the `velt-current-user-author` attribute to conditionally hide options for non-authors:
```css
[velt-current-user-author='false'] app-comment-dialog-thread-card-options {
display: none;
}
```
# null
# 3.0.49
### Improvements
* \[**Security**]: Added security updates.
# 3.0.48
### Improvements
* \[**Comments**]: Added dark mode support for the "Seen By" dropdown in the comment dialog.
# null
# 3.0.50
### New Features
* \[**Comments**]: Added variant support to the `Velt Comment Pin` component. This is useful for customizing how the pin looks on different elements like charts, tables, etc.
```jsx
```
```html
```
* \[**Access Control**]: Enabled users logged in with "Org A" to access documents belonging to "Org B".
* By default, users can only access documents within their own organization.
* You can enable cross-organization access by specifying the `organizationId` of the target document in the document metadata.
* Ensure that the user has access to the target document in the target organization.
**Using Hook:**
```jsx
useSetDocument(DOCUMENT_ID, {
organizationId: 'ANOTHER_ORGANIZATION_ID'
});
```
**Using API:**
```jsx
client.setDocument(DOCUMENT_ID, {
organizationId: 'ANOTHER_ORGANIZATION_ID'
});
```
```javascript
Velt.setDocument(DOCUMENT_ID, {
organizationId: 'ANOTHER_ORGANIZATION_ID'
});
```
* \[**Comments**]: Added ability to toggle the "Seen By" feature:
**Using Props:**
```jsx
```
**Using API:**
```jsx
const commentElement = client.getCommentElement();
commentElement.enableSeenByUsers();
commentElement.disableSeenByUsers();
```
**Using Props:**
```html
```
**Using API:**
```javascript
const commentElement = Velt.getCommentElement();
commentElement.enableSeenByUsers();
commentElement.disableSeenByUsers();
```
### Improvements
* \[**Live Selection**]: Improved the live selection UI.
* \[**Recording**]: Added new wireframes for the recording feature:
* Media Source Settings
* Recorder All Tool
* Recorder All Tool Menu
* Recorder Audio Tool
* Recorder Video Tool
* Recorder Screen Tool
* Recording Preview Steps Dialog
* Recorder Control Panel
* Recorder Player
* Video Player
* Subtitles
* Transcription
* \[**Comments**]: Updated the empty state UI and added a clear filter button in the sidebar.
* \[**Comments**]: The "Custom filters" applied by the user are now stored in session storage just like the System filters.
### Improvements
### Bug Fixes
* \[**Comments**]: Fixed an issue in TipTap editor where the comment dialog closed prematurely after adding a comment in single-thread mode.
* \[**Comments**]: Fixed an issue on minimap where clicking on it was not navigating to the comment location.
* \[**Comments**]: Fixed an issue where image attachments in comments were not opening in the lightbox.
* \[**Comments**]: Fixed an issue where the "AtHere" was not working when the label was set to "all".
# null
# 3.0.52
### Bug Fixes
* \[**Security**]: Added security updates.
# 3.0.51
### New Features
* \[**Recorder**]: Added `getRecordingData` API to fetch [recording data](/api-reference/models/RecorderData) including transcript, summary, and recording URLs.
**Using Hook:**
```jsx
const recorderData = useRecordingDataByRecorderId('-O9yTMWmEe5u6YGX8EFV');
useEffect(() => {
console.log('Recorder Data: ', recorderData);
}, [recorderData]);
```
**Using API:**
```jsx
const recorderElement = client.getRecorderElement();
recorderElement.getRecordingDataByRecorderId("-O9yGiYS8lehOXMpQf4j").subscribe((recorderData) => {
console.log('Recorder Data: ', recorderData);
});
```
```javascript
const recorderElement = Velt.getRecorderElement();
recorderElement.getRecordingDataByRecorderId("-O9yGiYS8lehOXMpQf4j").subscribe((recorderData) => {
console.log('Recorder Data: ', recorderData);
});
```
### Improvements
* \[**Comments**]: In the sidebar, changed default `isExpanded` behavior in custom filtering. If not explicitly set, the first group will be expanded while remaining groups are collapsed.
# null
# 3.0.53
### New Features
* \[**Comments**]: Now comments are supported on elements with duplicate DOM IDs.
* This is useful in cases where you have multiple instances of the same data component on a page and want the comment to appear on each instance, eg: Popover comments on a table.
* By default, comments appear on all matching elements. Use the `sourceId` attribute to control which element displays the comment dialog when adding a new comment.
```jsx
```
```html
```
### Improvements
* \[**Comments**]: In multithread mode, now you can deselect a thread by clicking on the header of the multi-thread dialog.
* \[**Comments**]: Removed self user from the list of users in the "Seen By" dropdown.
* \[**Comments**]: Removed users without name and email from the list of users in the "Seen By" dropdown.
### Bug Fixes
* \[**Tiptap Comments**]: Removed `data-velt-content` attribute. Now the highlighted text styling is only applied to text when there are comments available.
* \[**Comments**]: Fixed an issue where undefined and null string values appeared in individual and group contact lists.
# null
# 3.0.29
### New Features
* \[**Comments**]: Added ability to control whether comments inside the annotation should be collapsed.
Using Props:
```jsx
```
Using API:
```javascript
const commentElement = client.getCommentElement();
commentElement.enableCollapsedComments();
commentElement.disableCollapsedComments();
```
Using Props:
```html
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.enableCollapsedComments();
commentElement.disableCollapsedComments();
```
* \[**Comments**]: Added ability to get comment annotation by ID.
Using Hooks:
```jsx
const annotation = useCommentAnnotationById({
annotationId: '-O6W3jD0Lz3rxuDuqQFx', // AnnotationID
documentId: 'document-id' // DocumentId (Optional)
});
React.useEffect(() => {
console.log('annotation', annotation);
}, [annotation]);
```
Using API:
```javascript
const commentElement = client.getCommentElement();
commentElement.getCommentAnnotationById({
annotationId: '-O6W3jD0Lz3rxuDuqQFx', // AnnotationID
documentId: 'document-id' // DocumentId (Optional)
}).subscribe((annotation) => {
console.log('annotation', annotation);
});
```
Using API:
```javascript
const commentElement = Velt.getCommentElement();
commentElement.getCommentAnnotationById({
annotationId: '-O6W3jD0Lz3rxuDuqQFx', // AnnotationID
documentId: 'document-id' // DocumentId (Optional)
}).subscribe((annotation) => {
console.log('annotation', annotation);
});
```
* \[**Notifications**]: Added API to mark all notifications as read.
* Mark all notifications as read, either globally or for a specific tab.
* Using 'all' or 'document' as the `tabId` marks all notifications as read across all tabs (equivalent to calling `setAllNotificationsAsRead()` without arguments).
* Using 'for-you' as the `tabId` only marks notifications in the 'for-you' tab as read.
```javascript
const notificationElement = client.getNotificationElement();
// Mark all notifications as read
notificationElement.setAllNotificationsAsRead();
// Mark all notifications as read for a specific tab
notificationElement.setAllNotificationsAsRead({ tabId: 'for-you' });
notificationElement.setAllNotificationsAsRead({ tabId: 'all' });
notificationElement.setAllNotificationsAsRead({ tabId: 'document' });
```
```javascript
const notificationElement = Velt.getNotificationElement();
// Mark all notifications as read
notificationElement.setAllNotificationsAsRead();
// Mark all notifications as read for a specific tab
notificationElement.setAllNotificationsAsRead({ tabId: 'for-you' });
notificationElement.setAllNotificationsAsRead({ tabId: 'all' });
notificationElement.setAllNotificationsAsRead({ tabId: 'document' });
```
### Bug Fixes
* \[**UI Customization**]: Fixed an issue in `velt if` where string comparisions were not working as expected.
# 3.0.28
### New Features
* \[**Comments**]: Added a reset filter button wireframe in multithread comment dialog.
* \[**Notifications**]: Added a notifications panel loading state skeleton with wireframes.
```javascript
```
```html
```
### Bug Fixes
* \[**Comments**]: Fixed an issue where draft comments were not being saved for multithread comments in some cases.
* \[**Comments**]: Fixed an issue where in inline mode, when editing a comment, the annotation was not being selected.
# null
# 3.0.54
### Improvements
* \[**Comments**]: The "clear filter" button in the sidebar now only appears when comments are hidden due to active filters, not when there are no comments on the document.
### Bug Fixes
* \[**Comments**]: Fixed an issue with the Assign feature across different comment modes:
* Page mode
* Inline mode
* Multi-thread mode
* \[**Comments**]: Fixed an issue where reactions were not updated in focused thread mode.
# null
# 3.0.31
### Improvements
* \[**Notifications**]: Improved loading state for API calls, now returns null until data is fully loaded.
# 3.0.30
### New Features
* \[**Notifications**]: Added API to get unread notifications count.
* **Sample response:**
```javascript
{
forYou: 4, // # of unread notifications in the "For You" tab
all: 5 // # of unread notifications in the "All" or "Document" tab
}
```
Using Hooks:
```jsx
const unreadCount = useUnreadNotificationsCount();
useEffect(() => {
console.log('Unread Count', unreadCount);
}, [unreadCount]);
```
Using API:
```javascript
const notificationElement = client.getNotificationElement();
notificationElement.getUnreadNotificationsCount().subscribe((data) => {
console.log('Unread notifications count:', data);
});
```
```javascript
const notificationElement = Velt.getNotificationElement();
notificationElement.getUnreadNotificationsCount().subscribe((data) => {
console.log('Unread notifications count:', data);
});
```
### Improvements
* \[**Comments**]: Improved search functionality in @mentions to support spaces in search queries.
# null
# 3.0.33
### Improvements
* \[**Console Debugger**]: Added logs for `addContext` and `updateContext` methods. The context object is now included in the log event properties for better debugging and tracking.
* \[**Comments**]: Now comments will be marked as read if opened via the `selectCommentByAnnotationId()` API.
### Bug Fixes
* \[**Comment Display**]: Improved comment rendering performance for Comments Sidebar.
# 3.0.32
### New Features
* \[**Comments Sidebar**]: Added a reset filter button to easily clear all applied filters.
* This new button allows users to quickly reset all filters in the comments sidebar when no comments are available for the applied filters.
* Here is the wireframe for the reset filter button:
```jsx
```
```html
```
# null
# 3.0.35
### Bug Fixes
* \[**Notifications**]: Fixed an issue where the `document` tab was not visible when user email was not set.
# 3.0.34
### New Features
* \[**Comments**]: Added support for overlay text commenting in Tiptap editor.
* It works with all frontend frameworks that are supported by Tiptap.
* You can find the extension [here](https://www.npmjs.com/package/@veltdev/tiptap-velt-comments).
```bash
npm i @veltdev/tiptap-velt-comments
```
```javascript
import { TiptapVeltComments } from '@veltdev/tiptap-velt-comments';
const editor = new Editor({
extensions: [
TiptapVeltComments,
// ... other extensions
],
});
```
* Call this method to add a comment to selected text in the Tiptap editor. You can use this when the user clicks on the comment button in context menu or presses a keyboard shortcut.
* Args:
* `editor`: instance of the Tiptap editor.
* `tiptapVeltCommentConfig`: optional object to set the Comment Annotation's custom metadata.
* Example:
```javascript
import { addTiptapVeltComment } from '@veltdev/tiptap-velt-comments';
addTiptapVeltComment(editor, tiptapVeltCommentConfig);
```
### Improvements
* \[**Console Debugger**]: Added logs for the `updateContactList` method to improve debugging.
# null
# 3.0.37
### Bug Fixes
* \[**Notifications**]: Fixed an issue where the `unreadCommentsMap` count was not updating correctly when switching between documents with no unread comments.
# 3.0.36
### New Features
* \[**Comments**]: Added shadowDOM, dark mode, and variant support in standalone Comment Thread component.
```jsx
```
```html
```
### Bug Fixes
* \[**Comments**]: Fixed an issue where scroll was causing the "Add reply" button to hide.
* \[**Comments**]: Fixed an issue where the assign to dialog was not closing after assigning a user using the keyboard.
# null
# 3.0.7
### Improvements
* \[**Comments**]: Pass the `id` attribute on either the `