@veltdev/sdk 4.3.0-beta.5 → 4.3.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/models/data/attachment.model.d.ts +2 -1
- package/app/models/data/comment-resolver.data.model.d.ts +6 -6
- package/app/models/data/contact-utils.data.model.d.ts +2 -2
- package/app/models/data/core-events.data.model.d.ts +10 -0
- package/app/models/data/huddle.model.d.ts +2 -1
- package/app/models/data/live-state-data.data.model.d.ts +3 -2
- package/app/models/data/notification.model.d.ts +2 -2
- package/app/models/data/organization-groups.data.model.d.ts +1 -1
- package/app/models/data/reaction-resolver.data.model.d.ts +6 -6
- package/app/models/data/recorder-annotation.data.model.d.ts +11 -3
- package/app/models/data/recorder-events.data.model.d.ts +10 -1
- package/app/models/data/recorder.model.d.ts +3 -2
- package/app/models/element/recorder-element.model.d.ts +19 -2
- package/app/utils/enums.d.ts +43 -0
- package/models.d.ts +3 -0
- package/package.json +1 -1
- package/velt.js +2 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RecorderFileFormat } from "../../utils/enums";
|
|
1
2
|
export declare class Attachment {
|
|
2
3
|
/**
|
|
3
4
|
* Unique identifier for the comment pin annotation.
|
|
@@ -20,7 +21,7 @@ export declare class Attachment {
|
|
|
20
21
|
/**
|
|
21
22
|
* File type
|
|
22
23
|
*/
|
|
23
|
-
type?:
|
|
24
|
+
type?: RecorderFileFormat;
|
|
24
25
|
/**
|
|
25
26
|
* Download url of a file
|
|
26
27
|
*/
|
|
@@ -3,24 +3,24 @@ import { BaseMetadata } from "./base-metadata.data.model";
|
|
|
3
3
|
import { CommentAnnotation } from "./comment-annotation.data.model";
|
|
4
4
|
import { ResolverConfig, ResolverResponse } from "./resolver.data.model";
|
|
5
5
|
export interface CommentAnnotationDataProvider {
|
|
6
|
-
get: (request:
|
|
7
|
-
save: (request:
|
|
8
|
-
delete: (request:
|
|
6
|
+
get: (request: GetCommentResolverRequest) => Promise<ResolverResponse<Record<string, PartialCommentAnnotation>>>;
|
|
7
|
+
save: (request: SaveCommentResolverRequest) => Promise<ResolverResponse<undefined>>;
|
|
8
|
+
delete: (request: DeleteCommentResolverRequest) => Promise<ResolverResponse<undefined>>;
|
|
9
9
|
config?: ResolverConfig;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface GetCommentResolverRequest {
|
|
12
12
|
organizationId: string;
|
|
13
13
|
commentAnnotationIds?: string[];
|
|
14
14
|
documentIds?: string[];
|
|
15
15
|
folderId?: string;
|
|
16
16
|
allDocuments?: boolean;
|
|
17
17
|
}
|
|
18
|
-
export interface
|
|
18
|
+
export interface DeleteCommentResolverRequest {
|
|
19
19
|
commentAnnotationId: string;
|
|
20
20
|
metadata?: BaseMetadata;
|
|
21
21
|
event?: ResolverActions;
|
|
22
22
|
}
|
|
23
|
-
export interface
|
|
23
|
+
export interface SaveCommentResolverRequest {
|
|
24
24
|
commentAnnotation: {
|
|
25
25
|
[key: string]: PartialCommentAnnotation;
|
|
26
26
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UserGroup } from "./organization-groups.data.model";
|
|
2
2
|
import { User } from "./user.data.model";
|
|
3
3
|
export interface GetContactListResponse {
|
|
4
4
|
organizationUsers?: User[];
|
|
5
5
|
folderUsers?: User[];
|
|
6
6
|
documentUsers?: User[];
|
|
7
|
-
userGroups?:
|
|
7
|
+
userGroups?: UserGroup[];
|
|
8
8
|
updatedContactList?: User[];
|
|
9
9
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { CoreEventTypes } from "../../utils/enums";
|
|
2
2
|
import { VeltButtonClickEvent } from "./button.data.model";
|
|
3
|
+
import { User } from "./user.data.model";
|
|
3
4
|
export type CoreEventTypesMap = {
|
|
4
5
|
[CoreEventTypes.VELT_BUTTON_CLICK]: VeltButtonClickEvent;
|
|
6
|
+
[CoreEventTypes.USER_UPDATE]: UserUpdateEvent;
|
|
7
|
+
[CoreEventTypes.DOCUMENT_INIT]: DocumentInitEvent;
|
|
8
|
+
[CoreEventTypes.ERROR]: ErrorEvent;
|
|
9
|
+
};
|
|
10
|
+
export type UserUpdateEvent = User | null;
|
|
11
|
+
export type DocumentInitEvent = boolean | undefined;
|
|
12
|
+
export type ErrorEvent = {
|
|
13
|
+
code: string;
|
|
14
|
+
message?: string;
|
|
5
15
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HuddleType } from "../../utils/enums";
|
|
1
2
|
import { User } from "./user.data.model";
|
|
2
3
|
export declare class Attendee extends User {
|
|
3
4
|
state?: {
|
|
@@ -12,7 +13,7 @@ export declare class Attendee extends User {
|
|
|
12
13
|
screenSharing?: boolean;
|
|
13
14
|
};
|
|
14
15
|
streamMetadata?: any;
|
|
15
|
-
initialHuddleType?:
|
|
16
|
+
initialHuddleType?: HuddleType;
|
|
16
17
|
huddleOnCursorMode?: boolean;
|
|
17
18
|
}
|
|
18
19
|
export declare class Message {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SingleEditorState, SingleEditorStatus } from "../../utils/enums";
|
|
1
2
|
import { User } from "./user.data.model";
|
|
2
3
|
export declare class LiveStateData {
|
|
3
4
|
/**
|
|
@@ -24,7 +25,7 @@ export declare class SingleEditorLiveStateData {
|
|
|
24
25
|
requestEditorAccess?: {
|
|
25
26
|
user: User;
|
|
26
27
|
requestedAt: any;
|
|
27
|
-
status:
|
|
28
|
+
status: SingleEditorStatus;
|
|
28
29
|
editorAccessTimeout: number;
|
|
29
30
|
tabId?: string | null;
|
|
30
31
|
} | null;
|
|
@@ -56,7 +57,7 @@ export declare class EditorAccessTimer {
|
|
|
56
57
|
/**
|
|
57
58
|
* The time when the editor access was requested.
|
|
58
59
|
*/
|
|
59
|
-
state:
|
|
60
|
+
state: SingleEditorState;
|
|
60
61
|
/**
|
|
61
62
|
* Duration left for the editor access timer to be completed.
|
|
62
63
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AreaStatus, ArrowStatus, CommentStatus, HuddleActionTypes, TagStatus, UserActionTypes } from "../../utils/enums";
|
|
1
|
+
import { AreaStatus, ArrowStatus, CommentStatus, HuddleActionTypes, NotificationSource, TagStatus, UserActionTypes } from "../../utils/enums";
|
|
2
2
|
import { DocumentMetadata } from "./document-metadata.model";
|
|
3
3
|
import { DocumentUser } from "./document-user.data.model";
|
|
4
4
|
import { Location } from "./location.model";
|
|
@@ -19,7 +19,7 @@ export declare class NotificationRawData {
|
|
|
19
19
|
/**
|
|
20
20
|
* Source of a notification
|
|
21
21
|
*/
|
|
22
|
-
notificationSource?:
|
|
22
|
+
notificationSource?: NotificationSource;
|
|
23
23
|
/**
|
|
24
24
|
* Type of notification like 'Added', 'Updated' etc.
|
|
25
25
|
*/
|
|
@@ -3,24 +3,24 @@ import { ReactionAnnotation } from "./reaction-annotation.data.model";
|
|
|
3
3
|
import { ResolverResponse, ResolverConfig } from "./resolver.data.model";
|
|
4
4
|
import { ResolverActions } from "../../utils/enums";
|
|
5
5
|
export interface ReactionAnnotationDataProvider {
|
|
6
|
-
get: (request:
|
|
7
|
-
save: (request:
|
|
8
|
-
delete: (request:
|
|
6
|
+
get: (request: GetReactionResolverRequest) => Promise<ResolverResponse<Record<string, PartialReactionAnnotation>>>;
|
|
7
|
+
save: (request: SaveReactionResolverRequest) => Promise<ResolverResponse<undefined>>;
|
|
8
|
+
delete: (request: DeleteReactionResolverRequest) => Promise<ResolverResponse<undefined>>;
|
|
9
9
|
config?: ResolverConfig;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface GetReactionResolverRequest {
|
|
12
12
|
organizationId: string;
|
|
13
13
|
reactionAnnotationIds?: string[];
|
|
14
14
|
documentIds?: string[];
|
|
15
15
|
folderId?: string;
|
|
16
16
|
allDocuments?: boolean;
|
|
17
17
|
}
|
|
18
|
-
export interface
|
|
18
|
+
export interface DeleteReactionResolverRequest {
|
|
19
19
|
reactionAnnotationId: string;
|
|
20
20
|
metadata?: BaseMetadata;
|
|
21
21
|
event?: ResolverActions;
|
|
22
22
|
}
|
|
23
|
-
export interface
|
|
23
|
+
export interface SaveReactionResolverRequest {
|
|
24
24
|
reactionAnnotation: {
|
|
25
25
|
[key: string]: PartialReactionAnnotation;
|
|
26
26
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RecorderFileFormat, RecorderLayoutMode, RecorderType } from "../../utils/enums";
|
|
1
2
|
import { Attachment } from "./attachment.model";
|
|
2
3
|
import { BaseMetadata } from "./base-metadata.data.model";
|
|
3
4
|
import { CursorPosition } from "./cursor-position.data.model";
|
|
@@ -92,8 +93,8 @@ export declare class RecorderAnnotation {
|
|
|
92
93
|
*/
|
|
93
94
|
location?: Location | null;
|
|
94
95
|
type?: string;
|
|
95
|
-
recordingType:
|
|
96
|
-
mode:
|
|
96
|
+
recordingType: RecorderType;
|
|
97
|
+
mode: RecorderLayoutMode;
|
|
97
98
|
approved?: boolean;
|
|
98
99
|
/**
|
|
99
100
|
* Attachment object of recorded media
|
|
@@ -192,7 +193,7 @@ declare class RecorderDataAsset {
|
|
|
192
193
|
* The format/extension of the file
|
|
193
194
|
* @example 'mp3', 'mp4', 'webm'
|
|
194
195
|
*/
|
|
195
|
-
fileFormat?:
|
|
196
|
+
fileFormat?: RecorderFileFormat;
|
|
196
197
|
thumbnailUrl?: string;
|
|
197
198
|
}
|
|
198
199
|
export declare class RecorderData {
|
|
@@ -205,4 +206,11 @@ export interface RecorderRequestQuery {
|
|
|
205
206
|
export interface GetRecordingDataResponse {
|
|
206
207
|
data: Record<string, RecorderData> | null;
|
|
207
208
|
}
|
|
209
|
+
export interface GetRecordingsResponse {
|
|
210
|
+
data: Record<string, RecorderAnnotationData> | null;
|
|
211
|
+
}
|
|
212
|
+
export declare class RecorderAnnotationData extends RecorderAnnotation {
|
|
213
|
+
transcriptionData: RecorderDataTranscription;
|
|
214
|
+
assets: RecorderDataAsset[];
|
|
215
|
+
}
|
|
208
216
|
export {};
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import { RecordedData } from "../../models/data/recorder.model";
|
|
2
|
+
import { RecorderAnnotation, RecorderData } from "../../models/data/recorder-annotation.data.model";
|
|
1
3
|
import { RecorderEventTypes } from "../../utils/enums";
|
|
2
|
-
import { RecorderData } from "./recorder-annotation.data.model";
|
|
3
4
|
export interface TranscriptionDoneEvent {
|
|
4
5
|
data: Record<string, RecorderData> | null;
|
|
5
6
|
}
|
|
7
|
+
export interface RecordingDoneEvent {
|
|
8
|
+
data: Record<string, RecordedData> | null;
|
|
9
|
+
}
|
|
10
|
+
export interface RecordingDeleteEvent {
|
|
11
|
+
data: Record<string, RecorderAnnotation> | null;
|
|
12
|
+
}
|
|
6
13
|
export type RecorderEventTypesMap = {
|
|
7
14
|
[RecorderEventTypes.TRANSCRIPTION_DONE]: TranscriptionDoneEvent;
|
|
15
|
+
[RecorderEventTypes.RECORDING_DONE]: RecordingDoneEvent;
|
|
16
|
+
[RecorderEventTypes.DELETE_RECORDING]: RecordingDeleteEvent;
|
|
8
17
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
|
+
import { RecorderType } from "../../utils/enums";
|
|
2
3
|
export declare class RecorderConfig {
|
|
3
4
|
type: {
|
|
4
5
|
audio?: boolean;
|
|
@@ -26,7 +27,7 @@ export declare class RecorderInitData {
|
|
|
26
27
|
/**
|
|
27
28
|
* Type of media recorder, possible values are 'audio', 'video' and 'screen'
|
|
28
29
|
*/
|
|
29
|
-
type:
|
|
30
|
+
type: RecorderType;
|
|
30
31
|
/**
|
|
31
32
|
* Observable of recorded raw data.
|
|
32
33
|
*/
|
|
@@ -44,7 +45,7 @@ export declare class RecordedRawData {
|
|
|
44
45
|
/**
|
|
45
46
|
* Type of recorded data, possible values are 'audio' and 'video'
|
|
46
47
|
*/
|
|
47
|
-
type:
|
|
48
|
+
type: RecorderType;
|
|
48
49
|
/**
|
|
49
50
|
* Base64 encoded string of thumbnail image of one frame of recorded video
|
|
50
51
|
*/
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { Observable } from "rxjs";
|
|
3
|
-
import { RecorderData, RecorderRequestQuery, GetRecordingDataResponse } from "../data/recorder-annotation.data.model";
|
|
3
|
+
import { RecorderData, RecorderRequestQuery, GetRecordingDataResponse, GetRecordingsResponse } from "../data/recorder-annotation.data.model";
|
|
4
4
|
import { RecorderEventTypesMap } from "../data/recorder-events.data.model";
|
|
5
|
+
import { RecordedData } from "../data/recorder.model";
|
|
5
6
|
|
|
6
7
|
export declare class RecorderElement {
|
|
7
8
|
|
|
8
9
|
initRecording: (type: string, panelId?: string) => any;
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @deprecated Use on('recordingDone') method instead
|
|
13
|
+
*/
|
|
14
|
+
onRecordedData: () => Observable<RecordedData | null>;
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* To enable recording countdown
|
|
@@ -55,10 +59,18 @@ export declare class RecorderElement {
|
|
|
55
59
|
*/
|
|
56
60
|
disableVideoEditor: () => void;
|
|
57
61
|
|
|
62
|
+
/**
|
|
63
|
+
* To get recordings
|
|
64
|
+
*/
|
|
65
|
+
getRecordings: (query?: RecorderRequestQuery) => Observable<GetRecordingsResponse>;
|
|
66
|
+
|
|
58
67
|
constructor();
|
|
59
68
|
|
|
60
69
|
private _initRecording;
|
|
61
70
|
|
|
71
|
+
/**
|
|
72
|
+
* @deprecated Use on('recordingDone') method instead
|
|
73
|
+
*/
|
|
62
74
|
private _onRecordedData;
|
|
63
75
|
|
|
64
76
|
/**
|
|
@@ -106,4 +118,9 @@ export declare class RecorderElement {
|
|
|
106
118
|
* To disable video editor
|
|
107
119
|
*/
|
|
108
120
|
private _disableVideoEditor;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* To get recordings
|
|
124
|
+
*/
|
|
125
|
+
private _getRecordings;
|
|
109
126
|
}
|
package/app/utils/enums.d.ts
CHANGED
|
@@ -55,9 +55,14 @@ export declare const CommentEventTypes: {
|
|
|
55
55
|
};
|
|
56
56
|
export declare const RecorderEventTypes: {
|
|
57
57
|
readonly TRANSCRIPTION_DONE: "transcriptionDone";
|
|
58
|
+
readonly RECORDING_DONE: "recordingDone";
|
|
59
|
+
readonly DELETE_RECORDING: "deleteRecording";
|
|
58
60
|
};
|
|
59
61
|
export declare const CoreEventTypes: {
|
|
60
62
|
readonly VELT_BUTTON_CLICK: "veltButtonClick";
|
|
63
|
+
readonly USER_UPDATE: "userUpdate";
|
|
64
|
+
readonly DOCUMENT_INIT: "documentInit";
|
|
65
|
+
readonly ERROR: "error";
|
|
61
66
|
};
|
|
62
67
|
export type CommentEventType = typeof CommentEventTypes[keyof typeof CommentEventTypes];
|
|
63
68
|
export type RecorderEventType = typeof RecorderEventTypes[keyof typeof RecorderEventTypes];
|
|
@@ -177,3 +182,41 @@ export declare enum ContactListScopeForOrganizationUsers {
|
|
|
177
182
|
DOCUMENT = "document",
|
|
178
183
|
FOLDER = "folder"
|
|
179
184
|
}
|
|
185
|
+
export type RecorderMode = 'audio' | 'video' | 'screen' | 'all' | string;
|
|
186
|
+
export type RecorderType = 'audio' | 'video' | 'screen';
|
|
187
|
+
export type RecorderFileFormat = 'mp3' | 'mp4' | 'webm';
|
|
188
|
+
export type RecorderLayoutMode = 'floating' | 'thread';
|
|
189
|
+
export type SupportedMimeType = 'audio' | 'video';
|
|
190
|
+
export type FirebaseRegion = 'usCentral1' | 'asiaSouthEast1' | 'europeWest1';
|
|
191
|
+
export type VideoEventType = 'play' | 'pause' | 'ratechange' | 'seeked' | 'timeupdate' | 'volumechange';
|
|
192
|
+
export type RewriterType = 'copywriter' | 'generic';
|
|
193
|
+
export type RecorderStatusType = 'started' | 'paused' | 'resumed' | 'stopped' | 'error' | null;
|
|
194
|
+
export type HuddleType = 'audio' | 'video' | 'presentation';
|
|
195
|
+
export type NotificationTabId = 'for-you' | 'all' | 'document' | 'people';
|
|
196
|
+
export type NotificationTabType = 'forYou' | 'all' | 'documents' | 'people';
|
|
197
|
+
export type NotificationSource = 'area' | 'arrow' | 'comment' | 'tag' | 'huddle' | 'userInvite' | 'user' | 'recorder' | 'huddleInvite' | 'userFeedback' | 'userContactUs' | 'userReportBug' | 'documentViews' | 'custom';
|
|
198
|
+
export type SingleEditorStatus = 'pending' | 'accepted' | 'rejected' | 'cancelled';
|
|
199
|
+
export type SingleEditorState = 'idle' | 'inProgress' | 'completed';
|
|
200
|
+
export type AudioWaveformVariant = 'expanded' | 'minified' | 'player' | 'preview' | 'preview-mini' | 'editor';
|
|
201
|
+
export type ActionButtonType = 'button' | 'button-toggle' | 'multi-select' | 'single-select';
|
|
202
|
+
export type DropdownPosition = 'left' | 'right' | 'center';
|
|
203
|
+
export type DevicePermissionType = 'audio' | 'video';
|
|
204
|
+
export type TranscriptionMode = 'floating' | 'embed' | 'summaryMode';
|
|
205
|
+
export type ComposerPosition = 'top' | 'bottom';
|
|
206
|
+
export type SortBy = 'lastUpdated' | 'createdAt';
|
|
207
|
+
export type SortOrder = 'asc' | 'desc';
|
|
208
|
+
export type MinimapPosition = 'left' | 'right';
|
|
209
|
+
export type FilterOptionLayout = 'dropdown' | 'checkbox';
|
|
210
|
+
export type FilterPanelPosition = 'menu' | 'bottomSheet';
|
|
211
|
+
export type OverlayOriginY = 'top' | 'center' | 'bottom';
|
|
212
|
+
export type OverlayOriginX = 'start' | 'center' | 'end';
|
|
213
|
+
export type SubtitlesMode = 'floating' | 'embed';
|
|
214
|
+
export type RecorderVariant = 'default' | 'embed';
|
|
215
|
+
export type ReactionPinType = 'timeline' | 'comment';
|
|
216
|
+
export type SidebarPosition = 'left' | 'right';
|
|
217
|
+
export type SidebarSortingCriteria = 'date' | 'unread' | null;
|
|
218
|
+
export type SidebarFilterCriteria = 'all' | 'read' | 'unread' | 'resolved';
|
|
219
|
+
export type SidebarFilterSearchType = 'people' | 'assigned' | 'tagged' | 'pages' | 'statuses' | 'priorities' | 'categories' | 'versions';
|
|
220
|
+
export type InlineSortingCriteria = 'createdFirst' | 'createdLast' | 'updatedFirst' | 'updatedLast';
|
|
221
|
+
export type NotificationPanelMode = 'popover' | 'sidebar';
|
|
222
|
+
export type SidebarActionButtonType = 'default' | 'toggle';
|
package/models.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './app/models/data/comment-annotation.data.model';
|
|
|
9
9
|
export * from './app/models/data/comment-events.data.model';
|
|
10
10
|
export * from './app/models/data/comment.data.model';
|
|
11
11
|
export * from './app/models/data/comment-actions.data.model';
|
|
12
|
+
export * from './app/models/data/comment-resolver.data.model';
|
|
12
13
|
export * from './app/models/data/comment-utils.data.model';
|
|
13
14
|
export * from './app/models/data/contact-utils.data.model';
|
|
14
15
|
export * from './app/models/data/config.data.model';
|
|
@@ -62,6 +63,7 @@ export * from './app/models/data/comment-sidebar-config.model';
|
|
|
62
63
|
export * from './app/models/data/views.data.model';
|
|
63
64
|
export * from './app/models/data/autocomplete.data.model';
|
|
64
65
|
export * from './app/models/data/reaction-annotation.data.model';
|
|
66
|
+
export * from './app/models/data/reaction-resolver.data.model';
|
|
65
67
|
export * from './app/models/data/reaction.data.model';
|
|
66
68
|
export * from './app/models/data/organization-groups.data.model';
|
|
67
69
|
export * from './app/models/data/organization-metadata.model';
|
|
@@ -69,3 +71,4 @@ export * from './app/models/data/user-organization.data.model';
|
|
|
69
71
|
export * from './app/models/data/event-metadata.data.model';
|
|
70
72
|
export * from './app/models/data/user-resolver.data.model';
|
|
71
73
|
export * from './app/models/data/provider.data.model';
|
|
74
|
+
export * from './app/models/data/resolver.data.model';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltdev/sdk",
|
|
3
|
-
"version": "4.3.0-beta.
|
|
3
|
+
"version": "4.3.0-beta.6",
|
|
4
4
|
"description": "Velt is an SDK to add collaborative features to your product within minutes. Example: Comments like Figma, Frame.io, Google docs or sheets, Recording like Loom, Huddles like Slack and much more.",
|
|
5
5
|
"homepage": "https://velt.dev",
|
|
6
6
|
"keywords": [
|