@vidtreo/recorder-vue 1.13.0 → 1.15.0

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/README.md CHANGED
@@ -291,6 +291,19 @@ const { state, actions, audioLevel, controller } = useVidtreoRecorder({
291
291
  </template>
292
292
  ```
293
293
 
294
+ ### Passing a config object or a getter
295
+
296
+ `useVidtreoRecorder` and `useMobileWebRecorder` accept either a config object or a getter `() => config`. A plain object is read once. Pass a getter when the config comes from props or other reactive state: the recorder calls it whenever it needs the latest values, so callbacks and a replaced `getAccessToken` reach it without reinitializing. Only adding or removing the `getAccessToken` provider (for example switching to `apiKey`) reinitializes the recorder.
297
+
298
+ ```ts
299
+ const props = defineProps<{ getAccessToken: () => Promise<string> }>();
300
+
301
+ const recorder = useVidtreoRecorder(() => ({
302
+ getAccessToken: props.getAccessToken,
303
+ onUploadComplete: (result) => console.log(result),
304
+ }));
305
+ ```
306
+
294
307
  ### What the composable returns
295
308
 
296
309
  ```ts
@@ -352,13 +365,83 @@ const { state, actions, audioLevel, controller } = useVidtreoRecorder({
352
365
  | `stopPreview` | `() => void` | Tear the preview stream down. |
353
366
  | `downloadVideo` | `() => void` | Download the last `recordedBlob` (demo mode). |
354
367
  | `clearUploadError` | `() => void` | Clear `uploadErrorCode` after the user dismisses the upload error. |
368
+ | `mark` | `(marker: { id: string; label: string }) => void` | Record the current position in the recording. See [Markers](#markers). |
355
369
  | `cleanup` | `() => void` | Release all resources. Useful before unmounting. |
356
370
 
371
+ ### Markers
372
+
373
+ Call `actions.mark({ id, label })` while recording to note where something happens, for example when your UI moves on to the next question. The SDK stores the position in seconds of the recording (without paused time, rounded to the millisecond) and uploads the markers with that recording. The backend returns them as `markers` on the video or audio.
374
+
375
+ ```vue
376
+ <script setup lang="ts">
377
+ import { ref } from "vue";
378
+ import { useVidtreoRecorder } from "@vidtreo/recorder-vue";
379
+
380
+ const questions = [
381
+ { id: "q1", text: "First question" },
382
+ { id: "q2", text: "Second question" },
383
+ ];
384
+ const index = ref(0);
385
+ const { state, actions } = useVidtreoRecorder({ apiKey: "your-api-key" });
386
+
387
+ function next() {
388
+ index.value += 1;
389
+ const question = questions[index.value];
390
+ actions.mark({ id: question.id, label: question.text });
391
+ }
392
+ </script>
393
+
394
+ <template>
395
+ <p>{{ questions[index].text }}</p>
396
+ <button v-if="state.recordingState === 'recording'" @click="next">
397
+ Next question
398
+ </button>
399
+ </template>
400
+ ```
401
+
402
+ For example, `actions.mark({ id: "q2", label: "Second question" })` when the second question appears gives the uploaded video `markers: [{ id: "q2", time: 42.318, label: "Second question" }]`.
403
+
404
+ - Labels are free text. The SDK never reads or changes them.
405
+ - Each recording starts with no markers. Without `mark()` nothing changes and the upload has no `markers` key.
406
+ - Markers are kept in the upload queue, so they are sent when an upload resumes after a reload.
407
+ - `mark()` throws a `RecordingMarkerError` from `@vidtreo/recorder` when no recording is running (a paused recording counts as running), when `id` is not 1 to 64 characters or `label` is not 1 to 500 characters, when the `id` is already used in the recording, or after 200 markers. Its `code` is one of `recording.marker-not-recording`, `recording.marker-invalid-id`, `recording.marker-invalid-label`, `recording.marker-duplicate-id` or `recording.marker-limit-reached`.
408
+ - The stock `VidtreoRecorder` component has no markers UI. Call `mark()` through its template ref (see [Markers with the component](#markers-with-the-component)) or use the composable.
409
+
410
+ ### Markers with the component
411
+
412
+ Put a template ref on the stock `VidtreoRecorder` to mark from your own UI:
413
+
414
+ ```vue
415
+ <script setup lang="ts">
416
+ import { ref } from "vue";
417
+ import {
418
+ VidtreoRecorder,
419
+ type VidtreoRecorderExposed,
420
+ } from "@vidtreo/recorder-vue";
421
+
422
+ const recorderRef = ref<VidtreoRecorderExposed | null>(null);
423
+
424
+ function showSecondQuestion() {
425
+ recorderRef.value?.mark({ id: "q2", label: "Second question" });
426
+ }
427
+ </script>
428
+
429
+ <template>
430
+ <VidtreoRecorder ref="recorderRef" :recorder-props="{ apiKey: 'your-api-key' }" />
431
+ <button @click="showSecondQuestion">Next question</button>
432
+ </template>
433
+ ```
434
+
435
+ - The exposed `mark()` follows the same rules as `actions.mark()`. Before the recorder is ready or when no recording is running, it throws a `RecordingMarkerError` with code `recording.marker-not-recording`.
436
+ - It marks the recording of whichever recorder the component shows: desktop, mobile embed or mobile overlay. In native camera mode (`nativeCamera` or `mobileMode: "native"`) it does nothing, because the device's camera app records outside the page.
437
+ - `StandardRecorder`, `MobileWebRecorderUI` and `NativeCameraUI` expose the same `mark()`, and so does the `<VidtreoRecorder>` that `@vidtreo/recorder-nuxt` auto-imports.
438
+ - `isRecordingMarkerError`, the `ERROR_RECORDING_MARKER_*` codes and the `RecordingMarkerError` type are exported from `@vidtreo/recorder-vue`, so you don't need to import `@vidtreo/recorder`.
439
+
357
440
  ### Specialized composables
358
441
 
359
442
  | Composable | Purpose |
360
443
  |------------|---------|
361
- | `useMobileWebRecorder(config)` | Wraps `useVidtreoRecorder` and adds `isModalOpen`, `openModal`, `closeModal`, `canCloseModal` for the mobile overlay experience. |
444
+ | `useMobileWebRecorder(config)` | Takes the same config object or `() => config` getter as `useVidtreoRecorder`. Wraps it and adds `isModalOpen`, `openModal`, `closeModal`, `canCloseModal` for the mobile overlay experience. |
362
445
  | `useNativeCamera(config)` | File-input + transcoding flow used when `mobileMode: "native"`. Returns `{ state, actions }` with `handleFileSelect`, `processAndUpload`, `downloadVideo`, `reset`. Resumes queued native uploads on mount (except in demo mode); `state` includes `uploadPhase`, `uploadRetryAttempt` and `uploadErrorCode`. |
363
446
  | `usePermissionFlow(options)` | Drives the camera/mic permission overlay. Returns `{ isActive, isComplete, currentStep, steps, recoveryData, requestPermission, retryPermission }` — useful if you want to build a fully custom permission UI. |
364
447
  | `useRecorderTranslations(translations)` | Splits a `TranslationKeys` object into `preview`, `buttons`, and `settings` subsets as `ComputedRef`s. Accepts `MaybeRefOrGetter<TranslationKeys>` so it stays reactive when the language changes. |
@@ -641,7 +724,7 @@ For other SSR setups, wrap the component in `<ClientOnly>` (Nuxt) or guard it be
641
724
 
642
725
  ## TypeScript
643
726
 
644
- Every public surface is typed. The package also re-exports `getUploadErrorCode` and `UPLOAD_ERROR_INSUFFICIENT_CREDITS` from `@vidtreo/recorder`. Import types directly:
727
+ Every public surface is typed. The package also re-exports `getUploadErrorCode`, `UPLOAD_ERROR_INSUFFICIENT_CREDITS`, `isRecordingMarkerError`, the `ERROR_RECORDING_MARKER_*` codes and the `RecordingMarkerError` type from `@vidtreo/recorder`. Import types directly:
645
728
 
646
729
  ```ts
647
730
  import type {
@@ -659,6 +742,7 @@ import type {
659
742
  MobileMode,
660
743
  AudioWarning,
661
744
  RecordingStopResult,
745
+ VidtreoRecorderExposed,
662
746
  } from "@vidtreo/recorder-vue";
663
747
  ```
664
748
 
@@ -1,7 +1,7 @@
1
- import type { UseVidtreoRecorderConfig } from "../types";
1
+ import type { UseVidtreoRecorderConfig, VidtreoRecorderExposed } from "../types";
2
2
  type __VLS_Props = {
3
3
  config: UseVidtreoRecorderConfig;
4
4
  };
5
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, VidtreoRecorderExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
6
  declare const _default: typeof __VLS_export;
7
7
  export default _default;
@@ -1,7 +1,7 @@
1
- import type { UseVidtreoRecorderConfig } from "../types";
1
+ import type { UseVidtreoRecorderConfig, VidtreoRecorderExposed } from "../types";
2
2
  type __VLS_Props = {
3
3
  config: UseVidtreoRecorderConfig;
4
4
  };
5
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, VidtreoRecorderExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
6
  declare const _default: typeof __VLS_export;
7
7
  export default _default;
@@ -1,7 +1,7 @@
1
- import type { UseVidtreoRecorderConfig } from "../types";
1
+ import type { UseVidtreoRecorderConfig, VidtreoRecorderExposed } from "../types";
2
2
  type __VLS_Props = {
3
3
  config: UseVidtreoRecorderConfig;
4
4
  };
5
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, VidtreoRecorderExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
6
  declare const _default: typeof __VLS_export;
7
7
  export default _default;
@@ -1,9 +1,9 @@
1
1
  import type { RecordingUploadResult, UploadProgressPhase } from "@vidtreo/recorder";
2
- import type { VidtreoRecorderProps } from "../types";
2
+ import type { VidtreoRecorderExposed, VidtreoRecorderProps } from "../types";
3
3
  type __VLS_Props = {
4
4
  recorderProps: VidtreoRecorderProps;
5
5
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, VidtreoRecorderExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
7
7
  error: (error: Error) => any;
8
8
  uploadProgress: (progress: number, phase: UploadProgressPhase | null) => any;
9
9
  transcodingProgress: (progress: number) => any;
@@ -1,9 +1,9 @@
1
1
  import { type RecordingUploadResult, type UploadProgressPhase } from "@vidtreo/recorder";
2
- import type { VidtreoRecorderProps } from "../types";
2
+ import type { VidtreoRecorderExposed, VidtreoRecorderProps } from "../types";
3
3
  type __VLS_Props = {
4
4
  recorderProps: VidtreoRecorderProps;
5
5
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, VidtreoRecorderExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
7
7
  error: (error: Error) => any;
8
8
  uploadProgress: (progress: number, phase: UploadProgressPhase | null) => any;
9
9
  transcodingProgress: (progress: number) => any;
@@ -1,9 +1,9 @@
1
1
  import { type RecordingUploadResult, type UploadProgressPhase } from "@vidtreo/recorder";
2
- import type { VidtreoRecorderProps } from "../types";
2
+ import type { VidtreoRecorderExposed, VidtreoRecorderProps } from "../types";
3
3
  type __VLS_Props = {
4
4
  recorderProps: VidtreoRecorderProps;
5
5
  };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
6
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, VidtreoRecorderExposed, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
7
7
  error: (error: Error) => any;
8
8
  uploadProgress: (progress: number, phase: UploadProgressPhase | null) => any;
9
9
  transcodingProgress: (progress: number) => any;
@@ -3,7 +3,6 @@ import type { TranslationKeys } from "../../i18n/translations";
3
3
  import type { VidtreoRecorderState } from "../../types";
4
4
  type __VLS_Props = {
5
5
  state: VidtreoRecorderState;
6
- audioLevel: number;
7
6
  translations: TranslationKeys;
8
7
  isStopLocked: boolean;
9
8
  isRetrying: boolean;
@@ -1,5 +1,4 @@
1
1
  type __VLS_Props = {
2
- audioLevel: number;
3
2
  isMuted: boolean;
4
3
  };
5
4
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -42,7 +42,6 @@ type __VLS_Props = {
42
42
  isRecording: boolean;
43
43
  isProcessingRecording: boolean;
44
44
  controller: RecorderController | null;
45
- audioLevel: number;
46
45
  buttonVisibility: ButtonVisibility;
47
46
  currentSourceType: SourceType;
48
47
  isStopLocked: boolean;