11<template >
2- <div class =" space-y-6 mt-6" >
3- <div class =" " >
4- <p class =" text-sm text-gray-600" >
5- Customize your video background with blur effects or virtual backgrounds
6- </p >
7- </div >
8-
9- <!-- Video Preview -->
10- <div class =" flex justify-center mb-4" >
11- <div class =" w-full h-auto aspect-video bg-gray-900 rounded-lg overflow-hidden shadow-sm relative" >
12- <video
13- ref =" videoPreviewRef"
14- autoplay
15- muted
16- playsinline
17- class =" w-full h-full object-cover transform scale-x-[-1]"
18- />
19- <div v-if =" !previewStream"
20- class =" absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center" >
21- <div v-if =" isLoadingPreview" class =" text-white text-center" >
22- <lucide-loader class =" mx-auto mb-2 w-8 h-8 animate-spin" />
23- <p class =" text-sm" >
24- Loading preview...
25- </p >
2+ <SettingsLayoutBase
3+ :description =" 'Customize your video background with blur effects or virtual backgrounds'"
4+ >
5+ <template #title >
6+ Background
7+ </template >
8+ <template #content >
9+ <!-- Video Preview -->
10+ <div class =" flex justify-center mb-4" >
11+ <div class =" w-96 h-auto aspect-video bg-gray-900 rounded-lg overflow-hidden shadow-sm relative" >
12+ <video
13+ ref =" videoPreviewRef"
14+ autoplay
15+ muted
16+ playsinline
17+ class =" w-full h-full object-cover transform scale-x-[-1]"
18+ />
19+ <div v-if =" !previewStream"
20+ class =" absolute inset-0 bg-black bg-opacity-20 flex items-center justify-center" >
21+ <div v-if =" isLoadingPreview" class =" text-white text-center" >
22+ <lucide-loader class =" mx-auto mb-2 w-8 h-8 animate-spin" />
23+ <p class =" text-sm" >
24+ Loading preview...
25+ </p >
26+ </div >
2627 </div >
2728 </div >
2829 </div >
29- </div >
30-
31- <div class =" space-y-4" >
32- <!-- Image picker -->
33- <input ref =" fileInputRef" type =" file" accept =" image/*" class =" hidden" @change =" handleFileSelect" />
34-
35- <!-- Background Options -->
36- <div class =" grid grid-cols-4 gap-3" >
37- <div v-for =" option in allBackgroundOptions" :key =" option.name"
38- @click =" handleBackgroundOptionClick(option)"
39- class =" relative cursor-pointer rounded-lg border overflow-hidden transition-all duration-200 hover:shadow-sm group"
40- :class =" [
41- selectedBackgroundOption === option.name
42- ? 'border-gray-900 ring-gray-300'
43- : 'border-gray-200 hover:border-gray-300',
44- ]" >
45- <div class =" aspect-video bg-gray-100 relative" >
46- <!-- For blur option -->
47- <div v-if =" option.type === 'blur'" class =" absolute inset-0 flex items-center justify-center"
48- :class =" option.name === 'blur-low'
49- ? 'bg-[radial-gradient(circle_at_center,theme(colors.blue.200),theme(colors.blue.300),theme(colors.blue.500))]'
50- : 'bg-[radial-gradient(circle_at_center,theme(colors.blue.300),theme(colors.blue.400),theme(colors.blue.600))]'" >
51- <lucide-circle-user-round class =" w-8 h-8 text-gray-50" />
52- </div >
53-
54- <!-- For add custom option -->
55- <div v-else-if =" option.isAddButton"
56- class =" absolute inset-0 bg-gray-100 flex items-center justify-center border-2 border-dashed border-gray-300 hover:border-gray-400 transition-colors" >
57- <lucide-plus class =" w-8 h-8 text-gray-400" />
58- </div >
5930
60- <!-- For image options -->
61- <img v-else-if =" option.url" :src =" option.url" :alt =" option.label"
62- class =" w-full h-full object-cover" @error =" handleImageError" />
63-
64- <!-- For none option -->
65- <div v-else class =" absolute inset-0 bg-gray-50 flex items-center justify-center" >
66- <lucide-circle-user-round class =" w-8 h-8 text-gray-400" />
67- </div >
68-
69- <!-- Selected indicator -->
70- <div v-if =" selectedBackgroundOption === option.name"
71- class =" absolute top-1 right-1 w-5 h-5 bg-gray-900 rounded-full flex items-center justify-center" >
72- <lucide-check class =" w-3 h-3 text-white" />
31+ <div class =" space-y-4" >
32+ <!-- Image picker -->
33+ <input ref =" fileInputRef" type =" file" accept =" image/*" class =" hidden" @change =" handleFileSelect" />
34+
35+ <!-- Background Options -->
36+ <div class =" grid grid-cols-4 gap-3" >
37+ <div v-for =" option in allBackgroundOptions" :key =" option.name"
38+ @click =" handleBackgroundOptionClick(option)"
39+ class =" relative cursor-pointer rounded-lg border overflow-hidden transition-all duration-200 hover:shadow-sm group"
40+ :class =" [
41+ selectedBackgroundOption === option.name
42+ ? 'border-gray-900 ring-gray-300'
43+ : 'border-gray-200 hover:border-gray-300',
44+ ]" >
45+ <div class =" aspect-video bg-gray-100 relative" >
46+ <!-- For blur option -->
47+ <div v-if =" option.type === 'blur'" class =" absolute inset-0 flex items-center justify-center"
48+ :class =" option.name === 'blur-low'
49+ ? 'bg-[radial-gradient(circle_at_center,theme(colors.blue.200),theme(colors.blue.300),theme(colors.blue.500))]'
50+ : 'bg-[radial-gradient(circle_at_center,theme(colors.blue.300),theme(colors.blue.400),theme(colors.blue.600))]'" >
51+ <lucide-circle-user-round class =" w-8 h-8 text-gray-50" />
52+ </div >
53+
54+ <!-- For add custom option -->
55+ <div v-else-if =" option.isAddButton"
56+ class =" absolute inset-0 bg-gray-100 flex items-center justify-center border-2 border-dashed border-gray-300 hover:border-gray-400 transition-colors" >
57+ <lucide-plus class =" w-8 h-8 text-gray-400" />
58+ </div >
59+
60+ <!-- For image options -->
61+ <img v-else-if =" option.url" :src =" option.url" :alt =" option.label"
62+ class =" w-full h-full object-cover" @error =" handleImageError" />
63+
64+ <!-- For none option -->
65+ <div v-else class =" absolute inset-0 bg-gray-50 flex items-center justify-center" >
66+ <lucide-circle-user-round class =" w-8 h-8 text-gray-400" />
67+ </div >
68+
69+ <!-- Selected indicator -->
70+ <div v-if =" selectedBackgroundOption === option.name"
71+ class =" absolute top-1 right-1 w-5 h-5 bg-gray-900 rounded-full flex items-center justify-center" >
72+ <lucide-check class =" w-3 h-3 text-white" />
73+ </div >
74+
75+ <!-- Delete button for custom images -->
76+ <div v-if =" option.isCustom" @click.stop =" handleDeleteCustomImage(option.name)"
77+ class =" absolute top-1 right-1 w-5 h-5 bg-gray-500 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer hover:bg-gray-600" >
78+ <lucide-x class =" w-3 h-3 text-white" />
79+ </div >
7380 </div >
7481
75- <!-- Delete button for custom images -->
76- <div v-if =" option.isCustom" @click.stop =" handleDeleteCustomImage(option.name)"
77- class =" absolute top-1 right-1 w-5 h-5 bg-gray-500 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer hover:bg-gray-600" >
78- <lucide-x class =" w-3 h-3 text-white" />
82+ <!-- Label -->
83+ <div class =" p-2 bg-white" >
84+ <p class =" text-sm font-medium text-center text-gray-900 truncate" >
85+ {{ option.label }}
86+ </p >
7987 </div >
8088 </div >
81-
82- <!-- Label -->
83- <div class =" p-2 bg-white" >
84- <p class =" text-sm font-medium text-center text-gray-900 truncate" >
85- {{ option.label }}
86- </p >
87- </div >
8889 </div >
8990 </div >
90- </div >
9191
92- <div class =" bg-yellow-50 border border-yellow-200 rounded-lg p-3" >
93- <div class =" flex" >
94- <div class =" flex-shrink-0" >
95- <lucide-alert-triangle class =" h-5 w-5 text-yellow-400" />
96- </div >
97- <div class =" ml-3" >
98- <p class =" text-sm text-yellow-800" >
99- <strong >Performance Warning:</strong > Enabling background effects may slow down your computer,
100- especially on older devices.
92+ <div class =" bg-yellow-50 border border-yellow-200 rounded-lg p-3 mt-4 " >
93+ <div class =" flex" >
94+ <div class =" flex-shrink-0" >
95+ <lucide-alert-triangle class =" h-5 w-5 text-yellow-400" />
96+ </div >
97+ <div class =" ml-3" >
98+ <p class =" text-sm text-yellow-800" >
99+ <strong >Performance Warning:</strong > Enabling background effects may slow down your computer,
100+ especially on older devices.
101101 </p >
102102 </div >
103103 </div >
104104 </div >
105- </div >
105+ </template >
106+ </SettingsLayoutBase >
106107</template >
107108
108109<script setup>
109110import { toast } from " frappe-ui" ;
110111import { computed , onMounted , onUnmounted , ref , watch } from " vue" ;
111- import { useBackgroundEffects } from " ../composables/useBackgroundEffects" ;
112- import { useMeetingContext } from " ../composables/useMeetingContext.js" ;
112+ import { useBackgroundEffects } from " ../../ composables/useBackgroundEffects" ;
113+ import { useMeetingContext } from " ../../ composables/useMeetingContext.js" ;
113114import {
114115 addCustomBackgroundImage ,
115116 allBackgroundOptions ,
@@ -124,8 +125,9 @@ import {
124125 setBackgroundImageEnabled ,
125126 setBlurIntensity ,
126127 setSelectedBackgroundImage ,
127- } from " ../data/backgroundEffects" ;
128- import { selectedCameraId } from " ../data/mediaPreferences.js" ;
128+ } from " ../../data/backgroundEffects" ;
129+ import { selectedCameraId } from " ../../data/mediaPreferences.js" ;
130+ import SettingsLayoutBase from " ./SettingsLayoutBase.vue" ;
129131
130132const props = defineProps ({
131133 isVisible: {
0 commit comments