diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a16afa6..49fca72 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -133,6 +133,7 @@ jobs:
POSTHOG_HOST: https://app.posthog.com
TELEMETRY_ENABLED: true
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
+ FEEDBACK_SURVEY_ID: ${{ secrets.FEEDBACK_SURVEY_ID }}
AUTH_CLIENT_ID: ${{ secrets.AUTH_CLIENT_ID }}
AUTHORIZATION_ENDPOINT: ${{ secrets.AUTHORIZATION_ENDPOINT }}
AUTH_TOKEN_ENDPOINT: ${{ secrets.AUTH_TOKEN_ENDPOINT }}
@@ -148,6 +149,7 @@ jobs:
POSTHOG_HOST: https://app.posthog.com
TELEMETRY_ENABLED: true
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
+ FEEDBACK_SURVEY_ID: ${{ secrets.FEEDBACK_SURVEY_ID }}
AUTH_CLIENT_ID: ${{ secrets.AUTH_CLIENT_ID }}
AUTHORIZATION_ENDPOINT: ${{ secrets.AUTHORIZATION_ENDPOINT }}
AUTH_TOKEN_ENDPOINT: ${{ secrets.AUTH_TOKEN_ENDPOINT }}
diff --git a/apps/desktop/.env.example b/apps/desktop/.env.example
index dd4bd1b..64469c9 100644
--- a/apps/desktop/.env.example
+++ b/apps/desktop/.env.example
@@ -17,6 +17,7 @@ LOG_LEVEL=info
TELEMETRY_ENABLED=true
POSTHOG_HOST=https://app.posthog.com
POSTHOG_API_KEY=your-posthog-api-key
+FEEDBACK_SURVEY_ID=your-posthog-survey-id
# OAuth Configuration
# Required for Amical Cloud transcription
diff --git a/apps/desktop/src/components/feedback-button.tsx b/apps/desktop/src/components/feedback-button.tsx
new file mode 100644
index 0000000..3469a28
--- /dev/null
+++ b/apps/desktop/src/components/feedback-button.tsx
@@ -0,0 +1,20 @@
+import { IconMessageHeart } from "@tabler/icons-react";
+import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar";
+import { usePostHog } from "@/renderer/main/lib/posthog";
+
+export function FeedbackButton() {
+ const { enabled, hasSurvey, showFeedbackSurvey } = usePostHog();
+
+ if (!enabled || !hasSurvey) {
+ return null;
+ }
+
+ return (
+
+
+
+ Feedback
+
+
+ );
+}
diff --git a/apps/desktop/src/components/nav-secondary.tsx b/apps/desktop/src/components/nav-secondary.tsx
index 37942c8..e5f6db9 100644
--- a/apps/desktop/src/components/nav-secondary.tsx
+++ b/apps/desktop/src/components/nav-secondary.tsx
@@ -9,6 +9,7 @@ import {
SidebarMenuItem,
} from "@/components/ui/sidebar";
import { AuthButton } from "@/components/auth-button";
+import { FeedbackButton } from "@/components/feedback-button";
export function NavSecondary({
items,
@@ -39,6 +40,7 @@ export function NavSecondary({
))}
+
diff --git a/apps/desktop/src/pipeline/core/context.ts b/apps/desktop/src/pipeline/core/context.ts
index c809545..a5d6e13 100644
--- a/apps/desktop/src/pipeline/core/context.ts
+++ b/apps/desktop/src/pipeline/core/context.ts
@@ -12,7 +12,8 @@ export interface PipelineContext {
import { GetAccessibilityContextResult } from "@amical/types";
export interface SharedPipelineData {
- vocabulary: Map;
+ vocabulary: string[]; // Custom vocab
+ replacements: Map; // Custom replacements
userPreferences: {
language?: string; // Optional - undefined means auto-detect
formattingStyle: "formal" | "casual" | "technical";
@@ -31,7 +32,8 @@ export function createDefaultContext(sessionId: string): PipelineContext {
return {
sessionId,
sharedData: {
- vocabulary: new Map(),
+ vocabulary: [],
+ replacements: new Map(),
userPreferences: {
language: "en",
formattingStyle: "formal",
diff --git a/apps/desktop/src/pipeline/core/pipeline-types.ts b/apps/desktop/src/pipeline/core/pipeline-types.ts
index 30c17d4..afeaaf2 100644
--- a/apps/desktop/src/pipeline/core/pipeline-types.ts
+++ b/apps/desktop/src/pipeline/core/pipeline-types.ts
@@ -9,7 +9,7 @@ export { PipelineContext, SharedPipelineData } from "./context";
// Context for transcription operations (shared between transcribe and flush)
export interface TranscribeContext {
- vocabulary?: Map;
+ vocabulary?: string[];
accessibilityContext?: GetAccessibilityContextResult | null;
previousChunk?: string;
aggregatedTranscription?: string;
@@ -28,7 +28,7 @@ export interface FormatParams {
text: string;
context: {
style?: string;
- vocabulary?: Map;
+ vocabulary?: string[];
accessibilityContext?: GetAccessibilityContextResult | null;
previousChunk?: string;
aggregatedTranscription?: string;
diff --git a/apps/desktop/src/pipeline/providers/transcription/amical-cloud-provider.ts b/apps/desktop/src/pipeline/providers/transcription/amical-cloud-provider.ts
index 6b5f93f..1137d8b 100644
--- a/apps/desktop/src/pipeline/providers/transcription/amical-cloud-provider.ts
+++ b/apps/desktop/src/pipeline/providers/transcription/amical-cloud-provider.ts
@@ -33,6 +33,7 @@ export class AmicalCloudProvider implements TranscriptionProvider {
private currentAccessibilityContext: GetAccessibilityContextResult | null =
null;
private currentAggregatedTranscription: string | undefined;
+ private currentVocabulary: string[] = [];
// Configuration
private readonly FRAME_SIZE = 512; // 32ms at 16kHz
@@ -63,6 +64,7 @@ export class AmicalCloudProvider implements TranscriptionProvider {
this.currentLanguage = context.language;
this.currentAccessibilityContext = context?.accessibilityContext ?? null;
this.currentAggregatedTranscription = context?.aggregatedTranscription;
+ this.currentVocabulary = context?.vocabulary ?? [];
// Check authentication
if (!(await this.authService.isAuthenticated())) {
@@ -107,6 +109,7 @@ export class AmicalCloudProvider implements TranscriptionProvider {
this.currentLanguage = context.language;
this.currentAccessibilityContext = context?.accessibilityContext ?? null;
this.currentAggregatedTranscription = context?.aggregatedTranscription;
+ this.currentVocabulary = context?.vocabulary ?? [];
// Check authentication
if (!(await this.authService.isAuthenticated())) {
@@ -222,6 +225,7 @@ export class AmicalCloudProvider implements TranscriptionProvider {
audioData: Array.from(audioData),
vadProbs,
language: this.currentLanguage,
+ vocabulary: this.currentVocabulary,
previousTranscription: this.currentAggregatedTranscription,
formatting: {
enabled: enableFormatting,
diff --git a/apps/desktop/src/pipeline/providers/transcription/whisper-provider.ts b/apps/desktop/src/pipeline/providers/transcription/whisper-provider.ts
index e2c9ec2..9bbbe47 100644
--- a/apps/desktop/src/pipeline/providers/transcription/whisper-provider.ts
+++ b/apps/desktop/src/pipeline/providers/transcription/whisper-provider.ts
@@ -294,17 +294,14 @@ export class WhisperProvider implements TranscriptionProvider {
}
private generateInitialPrompt(
- vocabulary?: Map,
+ vocabulary?: string[],
aggregatedTranscription?: string,
): string {
const promptParts: string[] = [];
// Add vocabulary terms if available
- if (vocabulary && vocabulary.size > 0) {
- // Extract vocabulary keys (the actual terms) and join with commas
- const vocabularyTerms = Array.from(vocabulary.keys());
- const vocabularyText = vocabularyTerms.join(", ");
- promptParts.push(vocabularyText);
+ if (vocabulary && vocabulary.length > 0) {
+ promptParts.push(vocabulary.join(", "));
}
// Add last 8 words from aggregated transcription if available
diff --git a/apps/desktop/src/renderer/main/lib/posthog.ts b/apps/desktop/src/renderer/main/lib/posthog.ts
new file mode 100644
index 0000000..66bdea1
--- /dev/null
+++ b/apps/desktop/src/renderer/main/lib/posthog.ts
@@ -0,0 +1,66 @@
+import { useEffect } from "react";
+import posthog from "posthog-js";
+import { api } from "@/trpc/react";
+
+let initialized = false;
+
+function initPostHog(apiKey: string, host: string, machineId: string): void {
+ if (initialized) return;
+
+ posthog.init(apiKey, {
+ api_host: host,
+ opt_out_capturing_by_default: true,
+ autocapture: false,
+ capture_pageview: false,
+ capture_pageleave: false,
+ disable_session_recording: true,
+ persistence: "memory",
+ bootstrap: {
+ distinctID: machineId,
+ },
+ });
+
+ initialized = true;
+}
+
+function setTelemetryEnabled(enabled: boolean): void {
+ if (!initialized) return;
+ if (enabled) {
+ posthog.opt_in_capturing();
+ } else {
+ posthog.opt_out_capturing();
+ }
+}
+
+export function usePostHog() {
+ const { data: config } = api.settings.getTelemetryConfig.useQuery();
+
+ // Initialize PostHog when config is available
+ useEffect(() => {
+ if (config?.apiKey) {
+ initPostHog(config.apiKey, config.host, config.machineId);
+ }
+ }, [config?.apiKey, config?.host, config?.machineId]);
+
+ // Sync opt-in/opt-out state when enabled changes
+ useEffect(() => {
+ if (config?.enabled !== undefined) {
+ setTelemetryEnabled(config.enabled);
+ }
+ }, [config?.enabled]);
+
+ const showFeedbackSurvey = () => {
+ if (!initialized || !config?.feedbackSurveyId) return;
+ posthog.onSurveysLoaded(() => {
+ posthog.displaySurvey(config.feedbackSurveyId);
+ });
+ };
+
+ return {
+ enabled: config?.enabled ?? false,
+ hasSurvey: !!config?.feedbackSurveyId,
+ showFeedbackSurvey,
+ };
+}
+
+export { posthog };
diff --git a/apps/desktop/src/renderer/main/pages/settings/advanced/index.tsx b/apps/desktop/src/renderer/main/pages/settings/advanced/index.tsx
index ac2ad84..86bea85 100644
--- a/apps/desktop/src/renderer/main/pages/settings/advanced/index.tsx
+++ b/apps/desktop/src/renderer/main/pages/settings/advanced/index.tsx
@@ -52,6 +52,7 @@ export default function AdvancedSettingsPage() {
api.settings.updateTelemetrySettings.useMutation({
onSuccess: () => {
utils.settings.getTelemetrySettings.invalidate();
+ utils.settings.getTelemetryConfig.invalidate();
toast.success("Telemetry settings updated");
},
onError: (error) => {
diff --git a/apps/desktop/src/renderer/main/routes/__root.tsx b/apps/desktop/src/renderer/main/routes/__root.tsx
index 97a20fd..909e5e2 100644
--- a/apps/desktop/src/renderer/main/routes/__root.tsx
+++ b/apps/desktop/src/renderer/main/routes/__root.tsx
@@ -2,6 +2,7 @@ import { createRootRoute, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { api, trpcClient } from "@/trpc/react";
+import { usePostHog } from "../lib/posthog";
// Create a client
const queryClient = new QueryClient({
@@ -17,14 +18,25 @@ export const Route = createRootRoute({
component: RootComponent,
});
+// Inner component that uses hooks requiring provider context
+function AppShell() {
+ usePostHog(); // Initialize and sync telemetry
+
+ return (
+ <>
+
+ {process.env.NODE_ENV === "development" && (
+
+ )}
+ >
+ );
+}
+
function RootComponent() {
return (
-
- {process.env.NODE_ENV === "development" && (
-
- )}
+
);
diff --git a/apps/desktop/src/services/telemetry-service.ts b/apps/desktop/src/services/telemetry-service.ts
index 6293816..77fa8e9 100644
--- a/apps/desktop/src/services/telemetry-service.ts
+++ b/apps/desktop/src/services/telemetry-service.ts
@@ -132,9 +132,11 @@ export class TelemetryService {
},
};
- this.enabled = true;
+ this.enabled = telemetrySettings?.enabled !== false;
this.initialized = true;
- logger.main.info("Telemetry service initialized successfully");
+ logger.main.info("Telemetry service initialized successfully", {
+ enabled: this.enabled,
+ });
}
private async collectSystemInfo(): Promise {
@@ -232,6 +234,7 @@ export class TelemetryService {
async optIn(): Promise {
await this.settingsService.setTelemetrySettings({ enabled: true });
+ this.enabled = true;
if (!this.posthog) {
return;
}
@@ -243,6 +246,7 @@ export class TelemetryService {
async optOut(): Promise {
await this.settingsService.setTelemetrySettings({ enabled: false });
+ this.enabled = false;
if (!this.posthog) {
return;
}
diff --git a/apps/desktop/src/services/transcription-service.ts b/apps/desktop/src/services/transcription-service.ts
index 77ec690..acad703 100644
--- a/apps/desktop/src/services/transcription-service.ts
+++ b/apps/desktop/src/services/transcription-service.ts
@@ -16,6 +16,7 @@ import { TelemetryService } from "../services/telemetry-service";
import type { NativeBridge } from "./platform/native-bridge-service";
import type { OnboardingService } from "./onboarding-service";
import { createTranscription } from "../db/transcriptions";
+import { getVocabulary } from "../db/vocabulary";
import { logger } from "../main/logger";
import { v4 as uuid } from "uuid";
import { VADService } from "./vad-service";
@@ -609,7 +610,19 @@ export class TranscriptionService {
: dictationSettings.selectedLanguage || "en";
}
- // TODO: Load actual vocabulary
+ // Load vocabulary and replacements
+ const vocabEntries = await getVocabulary({ limit: 50 });
+ for (const entry of vocabEntries) {
+ if (entry.isReplacement) {
+ context.sharedData.replacements.set(
+ entry.word,
+ entry.replacementWord || "",
+ );
+ } else {
+ context.sharedData.vocabulary.push(entry.word);
+ }
+ }
+
// TODO: Load formatter config from settings
return context;
diff --git a/apps/desktop/src/trpc/routers/settings.ts b/apps/desktop/src/trpc/routers/settings.ts
index 27335e8..f612075 100644
--- a/apps/desktop/src/trpc/routers/settings.ts
+++ b/apps/desktop/src/trpc/routers/settings.ts
@@ -495,6 +495,19 @@ export const settingsRouter = createRouter({
return telemetryService?.getMachineId() ?? "";
}),
+ // Get telemetry config for renderer (PostHog surveys)
+ getTelemetryConfig: procedure.query(async ({ ctx }) => {
+ const telemetryService = ctx.serviceManager.getService("telemetryService");
+ return {
+ apiKey: process.env.POSTHOG_API_KEY || __BUNDLED_POSTHOG_API_KEY,
+ host: process.env.POSTHOG_HOST || __BUNDLED_POSTHOG_HOST,
+ machineId: telemetryService?.getMachineId() ?? "",
+ enabled: telemetryService?.isEnabled() ?? false,
+ feedbackSurveyId:
+ process.env.FEEDBACK_SURVEY_ID || __BUNDLED_FEEDBACK_SURVEY_ID,
+ };
+ }),
+
// Download log file via save dialog
downloadLogFile: procedure.mutation(async () => {
const { dialog, BrowserWindow } = await import("electron");
diff --git a/apps/desktop/src/types/bundled-env.d.ts b/apps/desktop/src/types/bundled-env.d.ts
index 7cf2e3b..c2e9094 100644
--- a/apps/desktop/src/types/bundled-env.d.ts
+++ b/apps/desktop/src/types/bundled-env.d.ts
@@ -5,3 +5,4 @@ declare const __BUNDLED_AUTH_CLIENT_ID: string;
declare const __BUNDLED_AUTH_AUTHORIZATION_ENDPOINT: string;
declare const __BUNDLED_AUTH_TOKEN_ENDPOINT: string;
declare const __BUNDLED_API_ENDPOINT: string;
+declare const __BUNDLED_FEEDBACK_SURVEY_ID: string;
diff --git a/apps/desktop/vite.main.config.mts b/apps/desktop/vite.main.config.mts
index 5f5d014..d8f5e9c 100644
--- a/apps/desktop/vite.main.config.mts
+++ b/apps/desktop/vite.main.config.mts
@@ -19,6 +19,9 @@ export default defineConfig({
process.env.AUTH_TOKEN_ENDPOINT || "",
),
__BUNDLED_API_ENDPOINT: JSON.stringify(process.env.API_ENDPOINT || ""),
+ __BUNDLED_FEEDBACK_SURVEY_ID: JSON.stringify(
+ process.env.FEEDBACK_SURVEY_ID || "",
+ ),
},
build: {
rollupOptions: {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 528188a..4bc89e0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -250,6 +250,9 @@ importers:
openai:
specifier: ^4.98.0
version: 4.104.0(encoding@0.1.13)(ws@8.18.3)(zod@3.25.76)
+ posthog-js:
+ specifier: ^1.315.1
+ version: 1.315.1
posthog-node:
specifier: ^5.8.1
version: 5.8.1
@@ -1537,6 +1540,12 @@ packages:
'@posthog/core@1.0.2':
resolution: {integrity: sha512-hWk3rUtJl2crQK0WNmwg13n82hnTwB99BT99/XI5gZSvIlYZ1TPmMZE8H2dhJJ98J/rm9vYJ/UXNzw3RV5HTpQ==}
+ '@posthog/core@1.9.0':
+ resolution: {integrity: sha512-j7KSWxJTUtNyKynLt/p0hfip/3I46dWU2dk+pt7dKRoz2l5CYueHuHK4EO7Wlgno5yo1HO4sc4s30MXMTICHJw==}
+
+ '@posthog/types@1.315.1':
+ resolution: {integrity: sha512-m2NggfJRYby3AkAES6yHMLURvTeK+rxN+5nmkuaCbOXQPdtWacSFIG5ZwN8d3crSx+WpiFauCDdr1sc3ZFkTHg==}
+
'@radix-ui/number@1.1.1':
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
@@ -3311,6 +3320,9 @@ packages:
core-js-pure@3.45.1:
resolution: {integrity: sha512-OHnWFKgTUshEU8MK+lOs1H8kC8GkTi9Z1tvNkxrCcw9wl3MJIO7q2ld77wjWn4/xuGrVu2X+nME1iIIPBSdyEQ==}
+ core-js@3.47.0:
+ resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==}
+
create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
@@ -4038,6 +4050,9 @@ packages:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
+ fflate@0.4.8:
+ resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
+
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
@@ -5457,6 +5472,9 @@ packages:
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
engines: {node: ^10 || ^12 || >=14}
+ posthog-js@1.315.1:
+ resolution: {integrity: sha512-ambT1azidu4hKhSmB95KdLY6yHfj9vvz1XNn68syh8DtkQ0uSdjpRY6tjMp96EQtPqCrDKr+8QpcusT1KQEZSA==}
+
posthog-node@5.8.1:
resolution: {integrity: sha512-YJYlYnlpItVjHqM9IhvZx8TzK8gnx2nU+0uhiog4RN47NnV0Z0K1AdC4ul+O8VuvS/jHqKCQvL8iAONRA37+0A==}
engines: {node: '>=20'}
@@ -5466,6 +5484,9 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
+ preact@10.28.2:
+ resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==}
+
prebuild-install@7.1.3:
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
engines: {node: '>=10'}
@@ -6596,6 +6617,9 @@ packages:
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
engines: {node: '>= 14'}
+ web-vitals@4.2.4:
+ resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
+
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -7935,6 +7959,12 @@ snapshots:
'@posthog/core@1.0.2': {}
+ '@posthog/core@1.9.0':
+ dependencies:
+ cross-spawn: 7.0.6
+
+ '@posthog/types@1.315.1': {}
+
'@radix-ui/number@1.1.1': {}
'@radix-ui/primitive@1.1.3': {}
@@ -9888,6 +9918,8 @@ snapshots:
core-js-pure@3.45.1: {}
+ core-js@3.47.0: {}
+
create-require@1.1.1: {}
cross-dirname@0.1.0: {}
@@ -10715,6 +10747,8 @@ snapshots:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
+ fflate@0.4.8: {}
+
fflate@0.8.2: {}
figures@3.2.0:
@@ -12216,6 +12250,15 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ posthog-js@1.315.1:
+ dependencies:
+ '@posthog/core': 1.9.0
+ '@posthog/types': 1.315.1
+ core-js: 3.47.0
+ fflate: 0.4.8
+ preact: 10.28.2
+ web-vitals: 4.2.4
+
posthog-node@5.8.1:
dependencies:
'@posthog/core': 1.0.2
@@ -12224,6 +12267,8 @@ snapshots:
dependencies:
commander: 9.5.0
+ preact@10.28.2: {}
+
prebuild-install@7.1.3:
dependencies:
detect-libc: 2.0.4
@@ -13503,6 +13548,8 @@ snapshots:
web-streams-polyfill@4.0.0-beta.3: {}
+ web-vitals@4.2.4: {}
+
webidl-conversions@3.0.1: {}
webpack-virtual-modules@0.6.2: {}