@futo-org/backups-orchestrator-ui 0.3.1 → 0.4.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.
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
+ import OnboardingBootstrapError from "../../onboarding/OnboardingBootstrapError.svelte";
2
3
  import OnboardingStageBackupServices from "../../onboarding/stages/OnboardingStageBackupServices.svelte";
3
4
  import OnboardingStageKeyImport from "../../onboarding/stages/OnboardingStageKeyImport.svelte";
5
+ import OnboardingStageTelemetry from "../../onboarding/stages/OnboardingStageTelemetry.svelte";
4
6
  import RestorePointFlow from "../../onboarding/restore-point-flow/RestorePointFlow.svelte";
5
7
  import type { OnboardingStatusResponseDto } from "../../../fetch-client";
6
8
  import {
@@ -19,6 +21,7 @@
19
21
 
20
22
  let status: OnboardingStatusResponseDto | undefined = $state();
21
23
  let stage:
24
+ | "telemetry"
22
25
  | "key-import"
23
26
  | "backup-service"
24
27
  | "restore-point"
@@ -28,48 +31,55 @@
28
31
  handleOnboardingStatus().then((data) => {
29
32
  status = data;
30
33
 
31
- if (data.hasOnboardedKey) {
32
- if (data.hasBackend) {
33
- stage = "restore-point";
34
- } else {
35
- stage = "backup-service";
36
- }
34
+ if (data.status !== "ready") {
35
+ return;
36
+ }
37
+
38
+ if (data.hasTelemetry === "none") {
39
+ stage = "telemetry";
40
+ } else if (data.hasOnboardedKey) {
41
+ stage = data.hasBackend ? "restore-point" : "backup-service";
37
42
  }
38
43
  });
39
44
  });
40
45
 
41
46
  const onKeyImported = async () => {
42
47
  await handleConfirmRecoveryKey();
43
-
44
- if (status?.hasBackend) {
45
- stage = "restore-point";
46
- } else {
47
- stage = "backup-service";
48
- }
48
+ stage = status?.hasBackend ? "restore-point" : "backup-service";
49
49
  };
50
50
  </script>
51
51
 
52
- {#if typeof status === "object"}
53
- {#if stage === "key-import"}
54
- <OnboardingStageKeyImport onImported={onKeyImported} onCancel={onExit} />
55
- {:else if stage === "backup-service"}
56
- <OnboardingStageBackupServices
57
- onNext={() => (stage = "restore-point")}
58
- onCancel={onExit}
59
- restore
60
- />
61
- {:else if stage === "key-reimport"}
62
- <OnboardingStageKeyImport
63
- onImported={() => (stage = "restore-point")}
64
- onCancel={() => (stage = "restore-point")}
65
- />
66
- {:else if stage === "restore-point"}
67
- <RestorePointFlow
68
- onImportKey={() => (stage = "key-reimport")}
69
- onCancel={onExit}
70
- {onFinish}
71
- />
72
- {/if}
73
- {:else}
52
+ {#if status === undefined || status.status === "not-ready"}
74
53
  <LoadingSpinner />
54
+ {:else if status.status === "error"}
55
+ <OnboardingBootstrapError error={status.error} onQuit={onExit} />
56
+ {:else if stage === "telemetry"}
57
+ <OnboardingStageTelemetry
58
+ onContinue={() =>
59
+ (stage = status?.hasOnboardedKey
60
+ ? status.hasBackend
61
+ ? "restore-point"
62
+ : "backup-service"
63
+ : "key-import")}
64
+ onCancel={onExit}
65
+ />
66
+ {:else if stage === "key-import"}
67
+ <OnboardingStageKeyImport onImported={onKeyImported} onCancel={onExit} />
68
+ {:else if stage === "backup-service"}
69
+ <OnboardingStageBackupServices
70
+ onNext={() => (stage = "restore-point")}
71
+ onCancel={onExit}
72
+ restore
73
+ />
74
+ {:else if stage === "key-reimport"}
75
+ <OnboardingStageKeyImport
76
+ onImported={() => (stage = "restore-point")}
77
+ onCancel={() => (stage = "restore-point")}
78
+ />
79
+ {:else if stage === "restore-point"}
80
+ <RestorePointFlow
81
+ onImportKey={() => (stage = "key-reimport")}
82
+ onCancel={onExit}
83
+ {onFinish}
84
+ />
75
85
  {/if}
@@ -1,9 +1,11 @@
1
1
  <script lang="ts">
2
+ import OnboardingBootstrapError from "../../onboarding/OnboardingBootstrapError.svelte";
2
3
  import OnboardingStageBackupServices from "../../onboarding/stages/OnboardingStageBackupServices.svelte";
3
4
  import OnboardingStageKeyConfirm from "../../onboarding/stages/OnboardingStageKeyConfirm.svelte";
4
5
  import OnboardingStageKeyImport from "../../onboarding/stages/OnboardingStageKeyImport.svelte";
5
6
  import OnboardingStageKeyIntro from "../../onboarding/stages/OnboardingStageKeyIntro.svelte";
6
7
  import OnboardingStageSaveKey from "../../onboarding/stages/OnboardingStageKeySave.svelte";
8
+ import OnboardingStageTelemetry from "../../onboarding/stages/OnboardingStageTelemetry.svelte";
7
9
  import OnboardingStageWelcome from "../../onboarding/stages/OnboardingStageWelcome.svelte";
8
10
  import type { OnboardingStatusResponseDto } from "../../../fetch-client";
9
11
  import {
@@ -28,6 +30,7 @@
28
30
  let status: OnboardingStatusResponseDto | undefined = $state();
29
31
  let stage:
30
32
  | `welcome`
33
+ | `telemetry`
31
34
  | `key-${"intro" | "save" | "confirm" | "import"}`
32
35
  | `backup-${"service" | "confirm" | "create"}`
33
36
  | `finished` = $state("welcome");
@@ -36,12 +39,17 @@
36
39
  handleOnboardingStatus().then(async (data) => {
37
40
  status = data;
38
41
 
42
+ if (data.status !== "ready") {
43
+ return;
44
+ }
45
+
39
46
  if (data.hasOnboardedKey) {
40
- if (data.hasBackup) {
41
- stage = "finished";
42
- } else {
43
- stage = "backup-service";
44
- }
47
+ stage =
48
+ data.hasTelemetry === "none"
49
+ ? "telemetry"
50
+ : data.hasBackup
51
+ ? "finished"
52
+ : "backup-service";
45
53
  } else {
46
54
  const { recoveryKey } = await handleCurrentRecoveryKey();
47
55
  code = recoveryKey;
@@ -60,54 +68,64 @@
60
68
  };
61
69
  </script>
62
70
 
63
- {#if typeof status === "object"}
64
- {#if stage === "finished"}
65
- {@render children()}
66
- {:else if stage === "welcome"}
67
- <OnboardingStageWelcome
68
- onNext={() => (stage = "key-intro")}
69
- onImportKey={() => (stage = "key-import")}
70
- onCancel={onExit}
71
- />
72
- {:else if stage === "key-import"}
73
- <OnboardingStageKeyImport
74
- onStart={() => (stage = "welcome")}
75
- onImported={() => (stage = "key-confirm")}
76
- onCancel={onExit}
77
- />
78
- {:else if stage === "key-intro"}
79
- <OnboardingStageKeyIntro
80
- onNext={() => (stage = "key-save")}
81
- onCancel={onExit}
82
- />
83
- {:else if stage === "key-save"}
84
- <OnboardingStageSaveKey
85
- {code}
86
- onNext={() => (stage = "key-confirm")}
87
- onCancel={onExit}
88
- />
89
- {:else if stage === "key-confirm"}
90
- <OnboardingStageKeyConfirm
91
- {code}
92
- onBack={() => (stage = "key-save")}
93
- onCancel={onExit}
94
- {onConfirmKey}
95
- />
96
- {:else if stage === "backup-service"}
97
- <OnboardingStageBackupServices onNext={onSelectBackend} onCancel={onExit} />
98
- {:else if stage === "backup-confirm"}
99
- <ImmichConfirmDefaultBackup
100
- onCustomize={() => (stage = "backup-create")}
101
- onConfirm={() => (stage = "finished")}
102
- onCancel={onExit}
103
- />
104
- {:else if stage === "backup-create"}
105
- <ImmichConfigureBackup
106
- onFinish={() => (stage = "finished")}
107
- onCancel={onExit}
108
- {backendId}
109
- />
110
- {/if}
111
- {:else}
71
+ {#if status === undefined || status.status === "not-ready"}
112
72
  <LoadingSpinner />
73
+ {:else if status.status === "error"}
74
+ <OnboardingBootstrapError error={status.error} onQuit={onExit} />
75
+ {:else if stage === "finished"}
76
+ {@render children()}
77
+ {:else if stage === "welcome"}
78
+ <OnboardingStageWelcome
79
+ onNext={() => (stage = status?.hasTelemetry === "none" ? "telemetry" : "key-intro")}
80
+ onImportKey={() => (stage = "key-import")}
81
+ onCancel={onExit}
82
+ />
83
+ {:else if stage === "telemetry"}
84
+ <OnboardingStageTelemetry
85
+ onContinue={() =>
86
+ (stage = status?.hasOnboardedKey
87
+ ? status.hasBackup
88
+ ? "finished"
89
+ : "backup-service"
90
+ : "key-intro")}
91
+ onCancel={onExit}
92
+ />
93
+ {:else if stage === "key-import"}
94
+ <OnboardingStageKeyImport
95
+ onStart={() => (stage = "welcome")}
96
+ onImported={() => (stage = "key-confirm")}
97
+ onCancel={onExit}
98
+ />
99
+ {:else if stage === "key-intro"}
100
+ <OnboardingStageKeyIntro
101
+ onNext={() => (stage = "key-save")}
102
+ onCancel={onExit}
103
+ />
104
+ {:else if stage === "key-save"}
105
+ <OnboardingStageSaveKey
106
+ {code}
107
+ onNext={() => (stage = "key-confirm")}
108
+ onCancel={onExit}
109
+ />
110
+ {:else if stage === "key-confirm"}
111
+ <OnboardingStageKeyConfirm
112
+ {code}
113
+ onBack={() => (stage = "key-save")}
114
+ onCancel={onExit}
115
+ {onConfirmKey}
116
+ />
117
+ {:else if stage === "backup-service"}
118
+ <OnboardingStageBackupServices onNext={onSelectBackend} onCancel={onExit} />
119
+ {:else if stage === "backup-confirm"}
120
+ <ImmichConfirmDefaultBackup
121
+ onCustomize={() => (stage = "backup-create")}
122
+ onConfirm={() => (stage = "finished")}
123
+ onCancel={onExit}
124
+ />
125
+ {:else if stage === "backup-create"}
126
+ <ImmichConfigureBackup
127
+ onFinish={() => (stage = "finished")}
128
+ onCancel={onExit}
129
+ {backendId}
130
+ />
113
131
  {/if}
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import {
3
+ Button,
4
+ HStack,
5
+ Modal,
6
+ ModalBody,
7
+ ModalFooter,
8
+ Stack,
9
+ Text,
10
+ } from "@immich/ui";
11
+ import { useReportError } from "../../services/onboarding.service";
12
+
13
+ type Props = {
14
+ error?: string;
15
+ onQuit: () => void;
16
+ };
17
+
18
+ const { error, onQuit }: Props = $props();
19
+
20
+ const mutation = useReportError();
21
+
22
+ const onReportAndQuit = () =>
23
+ mutation.mutate(undefined, { onSuccess: onQuit });
24
+ </script>
25
+
26
+ <Modal size="small" title="Something went wrong" onClose={onQuit} icon={false}>
27
+ <ModalBody>
28
+ <Stack>
29
+ <Text>We ran into an error setting up backups...</Text>
30
+ {#if error}
31
+ <Text size="small" color="danger" class="font-mono whitespace-pre-wrap"
32
+ >{error}</Text
33
+ >
34
+ {/if}
35
+ </Stack>
36
+ </ModalBody>
37
+ <ModalFooter>
38
+ <HStack>
39
+ <Button
40
+ color="danger"
41
+ onclick={onReportAndQuit}
42
+ loading={mutation.isPending}>Report error and quit</Button
43
+ >
44
+ <Button variant="ghost" onclick={onQuit} disabled={mutation.isPending}
45
+ >Go back</Button
46
+ >
47
+ </HStack>
48
+ </ModalFooter>
49
+ </Modal>
@@ -0,0 +1,7 @@
1
+ type Props = {
2
+ error?: string;
3
+ onQuit: () => void;
4
+ };
5
+ declare const OnboardingBootstrapError: import("svelte").Component<Props, {}, "">;
6
+ type OnboardingBootstrapError = ReturnType<typeof OnboardingBootstrapError>;
7
+ export default OnboardingBootstrapError;
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { LoadingSpinner } from "@immich/ui";
3
3
  import { onMount, type Snippet } from "svelte";
4
+ import OnboardingBootstrapError from "./OnboardingBootstrapError.svelte";
4
5
  import SampleOnboarding from "./SampleOnboarding.svelte";
5
6
  import type { OnboardingStatusResponseDto } from "../../fetch-client";
6
7
  import { handleOnboardingStatus } from "../../services/onboarding.service";
@@ -13,14 +14,16 @@
13
14
 
14
15
  const { onExit, onFinish, children }: Props = $props();
15
16
 
16
- let status: OnboardingStatusResponseDto | undefined = $state();
17
+ let onboarding: OnboardingStatusResponseDto | undefined = $state();
17
18
 
18
19
  onMount(() => {
19
- handleOnboardingStatus().then((data) => (status = data));
20
+ handleOnboardingStatus().then((data) => (onboarding = data));
20
21
  });
21
22
 
22
23
  function onSkip() {
23
- status = {
24
+ onboarding = {
25
+ status: "ready",
26
+ hasTelemetry: "full",
24
27
  hasBackend: true,
25
28
  hasOnboardedKey: true,
26
29
  hasBackup: true,
@@ -30,19 +33,19 @@
30
33
  }
31
34
  </script>
32
35
 
33
- {#if typeof status === "object"}
34
- {#if !(status.hasBackend && status.hasOnboardedKey && (status.hasSkippedExtraConfig || (status.hasBackup && status.hasSchedule)))}
35
- <SampleOnboarding
36
- {status}
37
- onFinish={() => (onFinish ? onFinish() : onSkip())}
38
- onCancel={() => {
39
- onSkip();
40
- onExit();
41
- }}
42
- />
43
- {:else}
44
- {@render children()}
45
- {/if}
46
- {:else}
36
+ {#if onboarding === undefined || onboarding.status === "not-ready"}
47
37
  <LoadingSpinner />
38
+ {:else if onboarding.status === "error"}
39
+ <OnboardingBootstrapError error={onboarding.error} onQuit={onExit} />
40
+ {:else if onboarding.hasTelemetry === "none" || !(onboarding.hasBackend && onboarding.hasOnboardedKey && (onboarding.hasSkippedExtraConfig || (onboarding.hasBackup && onboarding.hasSchedule)))}
41
+ <SampleOnboarding
42
+ status={onboarding}
43
+ onFinish={() => (onFinish ? onFinish() : onSkip())}
44
+ onCancel={() => {
45
+ onSkip();
46
+ onExit();
47
+ }}
48
+ />
49
+ {:else}
50
+ {@render children()}
48
51
  {/if}
@@ -13,6 +13,7 @@
13
13
  import ImportKey from "./stages/OnboardingStageKeyImport.svelte";
14
14
  import KeyIntro from "./stages/OnboardingStageKeyIntro.svelte";
15
15
  import SaveKey from "./stages/OnboardingStageKeySave.svelte";
16
+ import Telemetry from "./stages/OnboardingStageTelemetry.svelte";
16
17
  import Welcome from "./stages/OnboardingStageWelcome.svelte";
17
18
 
18
19
  type Props = {
@@ -28,17 +29,20 @@
28
29
  // svelte-ignore state_referenced_locally
29
30
  let stage:
30
31
  | "welcome"
32
+ | "telemetry"
31
33
  | `key-${"intro" | "save" | "confirm" | "import"}`
32
34
  | "backup-service"
33
35
  | "backup-create"
34
36
  | "schedule-create" = $state(
35
37
  !status.hasOnboardedKey
36
38
  ? "welcome"
37
- : !status.hasBackend
38
- ? "backup-service"
39
- : !status.hasBackup
40
- ? "backup-create"
41
- : "schedule-create",
39
+ : status.hasTelemetry === "none"
40
+ ? "telemetry"
41
+ : !status.hasBackend
42
+ ? "backup-service"
43
+ : !status.hasBackup
44
+ ? "backup-create"
45
+ : "schedule-create",
42
46
  );
43
47
 
44
48
  onMount(() => {
@@ -65,10 +69,22 @@
65
69
 
66
70
  {#if stage === "welcome"}
67
71
  <Welcome
68
- onNext={() => (stage = "key-intro")}
72
+ onNext={() => (stage = status.hasTelemetry === "none" ? "telemetry" : "key-intro")}
69
73
  onImportKey={() => (stage = "key-import")}
70
74
  {onCancel}
71
75
  />
76
+ {:else if stage === "telemetry"}
77
+ <Telemetry
78
+ onContinue={() =>
79
+ (stage = !status.hasOnboardedKey
80
+ ? "key-intro"
81
+ : !status.hasBackend
82
+ ? "backup-service"
83
+ : !status.hasBackup
84
+ ? "backup-create"
85
+ : "schedule-create")}
86
+ {onCancel}
87
+ />
72
88
  {:else if stage === "key-intro"}
73
89
  <KeyIntro onNext={() => (stage = "key-save")} {onCancel} />
74
90
  {:else if stage === "key-save"}
@@ -0,0 +1,60 @@
1
+ <script lang="ts">
2
+ import {
3
+ Button,
4
+ HStack,
5
+ Icon,
6
+ Modal,
7
+ ModalBody,
8
+ ModalFooter,
9
+ Stack,
10
+ Text,
11
+ } from "@immich/ui";
12
+ import { useEnableTelemetry } from "../../../services/onboarding.service";
13
+ import { mdiChartBox, mdiEyeOff } from "@mdi/js";
14
+
15
+ type Props = {
16
+ onContinue: () => void;
17
+ onCancel: () => void;
18
+ };
19
+
20
+ const { onContinue, onCancel }: Props = $props();
21
+
22
+ const mutation = useEnableTelemetry();
23
+
24
+ const onConfirm = () => mutation.mutate(undefined, { onSuccess: onContinue });
25
+ </script>
26
+
27
+ <Modal
28
+ size="small"
29
+ title="Telemetry required for closed beta"
30
+ onClose={onCancel}
31
+ >
32
+ <ModalBody>
33
+ <Stack>
34
+ <HStack>
35
+ <Icon icon={mdiChartBox} class="shrink-0 place-self-start mt-1" />
36
+ <Text>
37
+ We collect usage and diagnostic data to understand how FUTO Backups is
38
+ used and to find problems.</Text
39
+ >
40
+ </HStack>
41
+ <HStack>
42
+ <Icon icon={mdiEyeOff} class="shrink-0 place-self-start mt-1" />
43
+ <Text>
44
+ Your photos, files, and recovery key are never collected and never
45
+ leave your device unencrypted.</Text
46
+ >
47
+ </HStack>
48
+ </Stack>
49
+ </ModalBody>
50
+ <ModalFooter>
51
+ <HStack>
52
+ <Button onclick={onConfirm} loading={mutation.isPending}>Continue</Button>
53
+ <Button
54
+ variant="ghost"
55
+ onclick={onCancel}
56
+ disabled={mutation.isPending}>Cancel</Button
57
+ >
58
+ </HStack>
59
+ </ModalFooter>
60
+ </Modal>
@@ -0,0 +1,7 @@
1
+ type Props = {
2
+ onContinue: () => void;
3
+ onCancel: () => void;
4
+ };
5
+ declare const OnboardingStageTelemetry: import("svelte").Component<Props, {}, "">;
6
+ type OnboardingStageTelemetry = ReturnType<typeof OnboardingStageTelemetry>;
7
+ export default OnboardingStageTelemetry;
@@ -82,7 +82,12 @@ export type ConfigureImmichIntegrationRequestDto = {
82
82
  libraries: "all" | string[];
83
83
  retentionPolicy?: (RetentionPolicyDto) | null;
84
84
  };
85
+ export type BootstrapStatus = "not-ready" | "ready" | "error";
86
+ export type TelemetryLevel = "full" | "none";
85
87
  export type OnboardingStatusResponseDto = {
88
+ status: BootstrapStatus;
89
+ error?: string;
90
+ hasTelemetry: TelemetryLevel;
86
91
  hasOnboardedKey: boolean;
87
92
  hasBackend: boolean;
88
93
  hasBackup: boolean;
@@ -269,6 +274,8 @@ export declare function currentRecoveryKey(opts?: Oazapfts.RequestOpts): Promise
269
274
  export declare function importRecoveryKey(importRecoveryKeyRequest: ImportRecoveryKeyRequest, opts?: Oazapfts.RequestOpts): Promise<never>;
270
275
  export declare function confirmRecoveryKey(opts?: Oazapfts.RequestOpts): Promise<never>;
271
276
  export declare function skipOnboardingExtraConfig(opts?: Oazapfts.RequestOpts): Promise<never>;
277
+ export declare function enableTelemetry(opts?: Oazapfts.RequestOpts): Promise<never>;
278
+ export declare function reportError(opts?: Oazapfts.RequestOpts): Promise<never>;
272
279
  export declare function createRepository(repositoryCreateRequestDto: RepositoryCreateRequestDto, { backend }?: {
273
280
  backend?: string;
274
281
  }, opts?: Oazapfts.RequestOpts): Promise<RepositoryCreateResponseDto>;
@@ -85,6 +85,18 @@ export function skipOnboardingExtraConfig(opts) {
85
85
  method: "POST"
86
86
  }));
87
87
  }
88
+ export function enableTelemetry(opts) {
89
+ return oazapfts.ok(oazapfts.fetchText("/api/yucca/onboarding/telemetry", {
90
+ ...opts,
91
+ method: "POST"
92
+ }));
93
+ }
94
+ export function reportError(opts) {
95
+ return oazapfts.ok(oazapfts.fetchText("/api/yucca/onboarding/report-error", {
96
+ ...opts,
97
+ method: "POST"
98
+ }));
99
+ }
88
100
  export function createRepository(repositoryCreateRequestDto, { backend } = {}, opts) {
89
101
  return oazapfts.ok(oazapfts.fetchJson(`/api/yucca/repository${QS.query(QS.explode({
90
102
  backend
@@ -9,3 +9,5 @@ export declare const handleCurrentRecoveryKey: () => Promise<sdk.CurrentRecovery
9
9
  export declare const handleConfirmRecoveryKey: () => Promise<void>;
10
10
  export declare const handleImportRecoveryKey: (dto: ImportRecoveryKeyRequest) => Promise<void>;
11
11
  export declare const handleSkipOnboardingExtraConfig: () => Promise<void>;
12
+ export declare const useEnableTelemetry: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, void, unknown>;
13
+ export declare const useReportError: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, void, unknown>;
@@ -1,7 +1,7 @@
1
1
  import { sdk } from '..';
2
2
  import { queryClient } from '../query-client';
3
3
  import { handleError } from '../utils/handle-error';
4
- import { createQuery } from '@tanstack/svelte-query';
4
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
5
5
  export const recoveryKeyKeys = {
6
6
  all: ['recovery-key'],
7
7
  };
@@ -54,3 +54,11 @@ export const handleSkipOnboardingExtraConfig = async () => {
54
54
  throw error;
55
55
  }
56
56
  };
57
+ export const useEnableTelemetry = () => createMutation(() => ({
58
+ mutationFn: () => sdk.enableTelemetry(),
59
+ onError: (error) => handleError(error, 'Failed to save preferences'),
60
+ }), () => queryClient);
61
+ export const useReportError = () => createMutation(() => ({
62
+ mutationFn: () => sdk.reportError(),
63
+ onError: (error) => handleError(error, 'Failed to report error'),
64
+ }), () => queryClient);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@futo-org/backups-orchestrator-ui",
3
3
  "repository": "https://github.com/immich-app/yucca",
4
4
  "description": "Backups orchestrator (UI library)",
5
- "version": "0.3.1",
5
+ "version": "0.4.0",
6
6
  "license": "Source First License 1.1",
7
7
  "files": [
8
8
  "dist",
@@ -64,7 +64,7 @@
64
64
  "lodash.debounce": "^4.0.8",
65
65
  "luxon": "^3.7.2",
66
66
  "socket.io-client": "^4.8.3",
67
- "@futo-org/backups-api-client": "^0.3.1"
67
+ "@futo-org/backups-api-client": "^0.4.0"
68
68
  },
69
69
  "scripts": {
70
70
  "dev": "vite dev --port 5174",