6464 </div >
6565 </div >
6666
67+ <!-- Onboarding Modal -->
68+ <OnboardingModal
69+ v-model:open =" showOnboardingModal"
70+ @complete =" handleOnboardingComplete"
71+ />
6772 </div >
6873</template >
6974
7075<script setup lang="ts">
71- import { computed , onMounted , onUnmounted , watch } from ' vue' ;
76+ import { computed , onMounted , onUnmounted , watch , ref } from ' vue' ;
7277import { router } from ' @inertiajs/vue3' ;
7378import axios from ' axios' ;
7479import {
@@ -90,6 +95,7 @@ import TalkingPoints from '@/components/RealtimeAgent/Content/TalkingPoints.vue'
9095import CommitmentsList from ' @/components/RealtimeAgent/Actions/CommitmentsList.vue' ;
9196import PostCallActions from ' @/components/RealtimeAgent/Actions/PostCallActions.vue' ;
9297import ContextualInformation from ' @/components/ContextualInformation.vue' ;
98+ import OnboardingModal from ' @/components/RealtimeAgent/OnboardingModal.vue' ;
9399
94100// Utils
95101
@@ -108,6 +114,12 @@ const realtimeStore = useRealtimeAgentStore();
108114const settingsStore = useSettingsStore ();
109115const openaiStore = useOpenAIStore ();
110116
117+ // Onboarding state
118+ const showOnboardingModal = ref (false );
119+ const hasApiKey = ref (false );
120+ const hasMicPermission = ref (false );
121+ const hasScreenPermission = ref (false );
122+
111123// Composables
112124const overlayMode = useOverlayMode ();
113125const screenProtection = useScreenProtection ();
@@ -287,9 +299,54 @@ const handleFunctionCall = (name: string, args: any) => {
287299 }
288300};
289301
302+ // Check onboarding requirements
303+ const checkOnboardingRequirements = async () => {
304+ // Check if onboarding was already completed
305+ const onboardingCompleted = localStorage .getItem (' onboarding_completed' ) === ' true' ;
306+
307+ // Check API key
308+ try {
309+ const apiResponse = await axios .get (' /api/openai/status' );
310+ hasApiKey .value = apiResponse .data .hasApiKey || false ;
311+ } catch (error ) {
312+ console .error (' Failed to check API key status:' , error );
313+ hasApiKey .value = false ;
314+ }
315+
316+ // Check permissions if macPermissions API is available
317+ if ((window as any ).macPermissions ) {
318+ try {
319+ const micResult = await (window as any ).macPermissions .checkPermission (' microphone' );
320+ hasMicPermission .value = micResult .success && micResult .status === ' authorized' ;
321+
322+ const screenResult = await (window as any ).macPermissions .checkPermission (' screen' );
323+ hasScreenPermission .value = screenResult .success && screenResult .status === ' authorized' ;
324+ } catch (error ) {
325+ console .error (' Failed to check permissions:' , error );
326+ }
327+ }
328+
329+ // Show onboarding modal if any requirement is missing
330+ if (! onboardingCompleted || ! hasApiKey .value || ! hasMicPermission .value ) {
331+ showOnboardingModal .value = true ;
332+ }
333+ };
334+
335+ // Handle onboarding completion
336+ const handleOnboardingComplete = async () => {
337+ // Re-check requirements
338+ await checkOnboardingRequirements ();
339+
340+ // If everything is good, modal will close automatically
341+ showOnboardingModal .value = false ;
342+ };
343+
290344// Initialize function
291345const initialize = async () => {
292346 try {
347+ // Check onboarding requirements first
348+ await checkOnboardingRequirements ();
349+
293350 // Fetch templates
294351 const response = await axios .get (' /templates' );
295352 // The response has templates wrapped in a templates property
@@ -318,6 +375,15 @@ const toggleSession = () => {
318375
319376
320377const startCall = async () => {
378+ // First check if all requirements are met
379+ await checkOnboardingRequirements ();
380+
381+ // If onboarding modal is shown, don't start the call
382+ if (showOnboardingModal .value ) {
383+ realtimeStore .setConnectionStatus (' disconnected' );
384+ return ;
385+ }
386+
321387 let permissions;
322388 try {
323389 realtimeStore .setConnectionStatus (' connecting' );
0 commit comments