comfyui-frontend-package 1.38.5__py3-none-any.whl → 1.38.6__py3-none-any.whl
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.
- comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js → AboutPanel-lkjGFasi.js} +2 -2
- comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js.map → AboutPanel-lkjGFasi.js.map} +1 -1
- comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js → AudioPreviewPlayer-BxCSKPl9.js} +2 -2
- comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js.map → AudioPreviewPlayer-BxCSKPl9.js.map} +1 -1
- comfyui_frontend_package/static/assets/AudioPreviewPlayer-CkxKvcVf.js +1 -0
- comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js → BaseViewTemplate-CjODF2hh.js} +2 -2
- comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js.map → BaseViewTemplate-CjODF2hh.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js → CloudAuthTimeoutView-D-QkjPNh.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js.map → CloudAuthTimeoutView-D-QkjPNh.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js → CloudBadge-B4nmLus2.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js.map → CloudBadge-B4nmLus2.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js → CloudForgotPasswordView-DOEV9hGr.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js.map → CloudForgotPasswordView-DOEV9hGr.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js → CloudLayoutView-ShKH6rRV.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js.map → CloudLayoutView-ShKH6rRV.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js → CloudLoginView-C3Te42U9.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js.map → CloudLoginView-C3Te42U9.js.map} +1 -1
- comfyui_frontend_package/static/assets/CloudRunButtonWrapper-Cub7EB34.js +3 -0
- comfyui_frontend_package/static/assets/{CloudRunButtonWrapper-hQc4BNkX.js.map → CloudRunButtonWrapper-Cub7EB34.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js → CloudSignupView-X2oiL3ZR.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js.map → CloudSignupView-X2oiL3ZR.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js → CloudSubscriptionRedirectView-UjNv8emo.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js.map → CloudSubscriptionRedirectView-UjNv8emo.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js → CloudSurveyView-IaiucCTP.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js.map → CloudSurveyView-IaiucCTP.js.map} +1 -1
- comfyui_frontend_package/static/assets/ComfyQueueButton-BppnHbrl.js +1 -0
- comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js → ComfyQueueButton-HjSIKZKO.js} +2 -2
- comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js.map → ComfyQueueButton-HjSIKZKO.js.map} +1 -1
- comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js → ExtensionPanel-Bzb9QtKj.js} +2 -2
- comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js.map → ExtensionPanel-Bzb9QtKj.js.map} +1 -1
- comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js → GlobalToast-BSCvu6Hw.js} +2 -2
- comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js.map → GlobalToast-BSCvu6Hw.js.map} +1 -1
- comfyui_frontend_package/static/assets/{GraphView-BCkpNGgz.js → GraphView-gYVCtm1V.js} +5 -5
- comfyui_frontend_package/static/assets/GraphView-gYVCtm1V.js.map +1 -0
- comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js → KeybindingPanel-DF-bG4iO.js} +2 -2
- comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js.map → KeybindingPanel-DF-bG4iO.js.map} +1 -1
- comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js → LegacyCreditsPanel-D-CboO8k.js} +2 -2
- comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js.map → LegacyCreditsPanel-D-CboO8k.js.map} +1 -1
- comfyui_frontend_package/static/assets/Load3D-c9UwgGoI.js +1 -0
- comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js → Load3D-vYr8M3jJ.js} +2 -2
- comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js.map → Load3D-vYr8M3jJ.js.map} +1 -1
- comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js → PanelTemplate-BJda9e5J.js} +2 -2
- comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js.map → PanelTemplate-BJda9e5J.js.map} +1 -1
- comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js → ServerConfigPanel-VsC6xlZJ.js} +2 -2
- comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js.map → ServerConfigPanel-VsC6xlZJ.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js → SubscribeButton-DZBycfCA.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js.map → SubscribeButton-DZBycfCA.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js → SubscribeToRun-4YolxBOL.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js.map → SubscribeToRun-4YolxBOL.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js → SubscriptionPanel-B6txX4Vm.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js.map → SubscriptionPanel-B6txX4Vm.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js → SubscriptionRequiredDialogContent-COEF2VQ_.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js.map → SubscriptionRequiredDialogContent-COEF2VQ_.js.map} +1 -1
- comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js → UserCheckView-x-fkcYzc.js} +2 -2
- comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js.map → UserCheckView-x-fkcYzc.js.map} +1 -1
- comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js → UserPanel-7N9QknQj.js} +2 -2
- comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js.map → UserPanel-7N9QknQj.js.map} +1 -1
- comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js → UserSelectView-BYjOkfSa.js} +2 -2
- comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js.map → UserSelectView-BYjOkfSa.js.map} +1 -1
- comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js → ValueControlPopover-BPAa35QG.js} +2 -2
- comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js.map → ValueControlPopover-BPAa35QG.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js → WidgetAudioUI-Dw-r3Ews.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js.map → WidgetAudioUI-Dw-r3Ews.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js → WidgetImageCrop-kERy9g5I.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js.map → WidgetImageCrop-kERy9g5I.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetInputNumber-BOKO36G3.js → WidgetInputNumber-BaClCNAC.js} +1 -1
- comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js +3 -0
- comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js.map +1 -0
- comfyui_frontend_package/static/assets/{WidgetLegacy-Bslv9wZZ.js → WidgetLegacy-B4nipUM9.js} +1 -1
- comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js → WidgetRecordAudio-Nk8dH238.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js.map → WidgetRecordAudio-Nk8dH238.js.map} +1 -1
- comfyui_frontend_package/static/assets/WidgetSelect-DzZPpO_-.js +1 -0
- comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js → WidgetSelect-nSQrk_hd.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js.map → WidgetSelect-nSQrk_hd.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js → WidgetWithControl-Da6zUB5e.js} +3 -3
- comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js.map → WidgetWithControl-Da6zUB5e.js.map} +1 -1
- comfyui_frontend_package/static/assets/{api-CUAc7rDA.js → api-Dwq2LQIW.js} +4 -4
- comfyui_frontend_package/static/assets/api-Dwq2LQIW.js.map +1 -0
- comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js → audioService-DvVaKhuU.js} +2 -2
- comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js.map → audioService-DvVaKhuU.js.map} +1 -1
- comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js → audioUtils-DD4rUYVZ.js} +2 -2
- comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js.map → audioUtils-DD4rUYVZ.js.map} +1 -1
- comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js → auth-B9axG-yZ.js} +2 -2
- comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js.map → auth-B9axG-yZ.js.map} +1 -1
- comfyui_frontend_package/static/assets/auth-D74DTev8.js +1 -0
- comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js → cloudBadges-D5mGJbRy.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js.map → cloudBadges-D5mGJbRy.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js → cloudFeedbackTopbarButton-RZUssOmb.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js.map → cloudFeedbackTopbarButton-RZUssOmb.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js → cloudRemoteConfig-F9J0iGyF.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js.map → cloudRemoteConfig-F9J0iGyF.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js → cloudSessionCookie-MEORlqtg.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js.map → cloudSessionCookie-MEORlqtg.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js → cloudSubscription-CuWNXKVx.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js.map → cloudSubscription-CuWNXKVx.js.map} +1 -1
- comfyui_frontend_package/static/assets/{core-DBfeqMDR.js → core-IYu8XAIx.js} +4 -4
- comfyui_frontend_package/static/assets/{core-DBfeqMDR.js.map → core-IYu8XAIx.js.map} +1 -1
- comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js → dialogService-YG0RH337.js} +9 -9
- comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js.map → dialogService-YG0RH337.js.map} +1 -1
- comfyui_frontend_package/static/assets/firebaseAuthStore-DnNaPbuZ.js +1 -0
- comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js → graphHasMissingNodes-BhD1N6zI.js} +2 -2
- comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js.map → graphHasMissingNodes-BhD1N6zI.js.map} +1 -1
- comfyui_frontend_package/static/assets/{index-CGxJFSof.js → index-BMy3twho.js} +3 -3
- comfyui_frontend_package/static/assets/{index-CGxJFSof.js.map → index-BMy3twho.js.map} +1 -1
- comfyui_frontend_package/static/assets/{index-DNpOhRra.css → index-KMO9qFHH.css} +1 -1
- comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js → keybindingService-CBLPjYHI.js} +2 -2
- comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js.map → keybindingService-CBLPjYHI.js.map} +1 -1
- comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js → releaseStore-DDOxzkVb.js} +2 -2
- comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js.map → releaseStore-DDOxzkVb.js.map} +1 -1
- comfyui_frontend_package/static/assets/releaseStore-iVkqunL8.js +1 -0
- comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js → subscriptionCheckoutUtil-DswSOreM.js} +2 -2
- comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js.map → subscriptionCheckoutUtil-DswSOreM.js.map} +1 -1
- comfyui_frontend_package/static/assets/{useCurrentUser-BJcn2Vgo.js → useCurrentUser-NdaCJzIK.js} +1 -1
- comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js → useErrorHandling-Cfa5N_7c.js} +2 -2
- comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js.map → useErrorHandling-Cfa5N_7c.js.map} +1 -1
- comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js → useSubscriptionDialog-792qfEJ2.js} +3 -3
- comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js.map → useSubscriptionDialog-792qfEJ2.js.map} +1 -1
- comfyui_frontend_package/static/assets/useSubscriptionDialog-B-eGeK3j.js +1 -0
- comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js → userStore-BAS9m9W6.js} +2 -2
- comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js.map → userStore-BAS9m9W6.js.map} +1 -1
- comfyui_frontend_package/static/assets/vendor-three-BFcUNSs9.js.map +1 -1
- comfyui_frontend_package/static/index.html +1 -1
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/METADATA +1 -1
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/RECORD +126 -126
- comfyui_frontend_package/static/assets/AudioPreviewPlayer-BoEdyGI_.js +0 -1
- comfyui_frontend_package/static/assets/CloudRunButtonWrapper-hQc4BNkX.js +0 -3
- comfyui_frontend_package/static/assets/ComfyQueueButton-BbQnRThI.js +0 -1
- comfyui_frontend_package/static/assets/GraphView-BCkpNGgz.js.map +0 -1
- comfyui_frontend_package/static/assets/Load3D-DHBmC_AU.js +0 -1
- comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js +0 -3
- comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js.map +0 -1
- comfyui_frontend_package/static/assets/WidgetSelect-DsJGH12l.js +0 -1
- comfyui_frontend_package/static/assets/api-CUAc7rDA.js.map +0 -1
- comfyui_frontend_package/static/assets/auth-D3RiiqZ8.js +0 -1
- comfyui_frontend_package/static/assets/firebaseAuthStore-CZgxeMyf.js +0 -1
- comfyui_frontend_package/static/assets/releaseStore-CubqSv5t.js +0 -1
- comfyui_frontend_package/static/assets/useSubscriptionDialog-BzMzio2H.js +0 -1
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/WHEEL +0 -0
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/top_level.txt +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Load3D-ei1BUF9O.js","names":[],"sources":["../../src/components/load3d/controls/PopupSlider.vue","../../src/components/load3d/controls/PopupSlider.vue","../../src/components/load3d/controls/CameraControls.vue","../../src/components/load3d/controls/CameraControls.vue","../../src/components/load3d/controls/ExportControls.vue","../../src/components/load3d/controls/ExportControls.vue","../../src/components/load3d/controls/LightControls.vue","../../src/components/load3d/controls/LightControls.vue","../../src/components/load3d/controls/ModelControls.vue","../../src/components/load3d/controls/ModelControls.vue","../../src/components/load3d/controls/SceneControls.vue","../../src/components/load3d/controls/SceneControls.vue","../../src/components/load3d/Load3DControls.vue","../../src/components/load3d/Load3DControls.vue","../../src/components/load3d/LoadingOverlay.vue","../../src/components/load3d/LoadingOverlay.vue","../../src/components/load3d/Load3DScene.vue","../../src/components/load3d/Load3DScene.vue","../../src/components/load3d/controls/RecordingControls.vue","../../src/components/load3d/controls/RecordingControls.vue","../../src/components/load3d/controls/ViewerControls.vue","../../src/components/load3d/controls/ViewerControls.vue","../../src/components/load3d/Load3D.vue","../../src/components/load3d/Load3D.vue"],"sourcesContent":["<template>\n <div class=\"relative show-slider\">\n <Button\n v-tooltip.right=\"{ value: tooltipText, showDelay: 300 }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"tooltipText\"\n @click=\"toggleSlider\"\n >\n <i :class=\"['pi', icon, 'text-lg text-base-foreground']\" />\n </Button>\n <div\n v-show=\"showSlider\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface p-4 shadow-lg w-[150px]\"\n >\n <Slider\n v-model=\"value\"\n class=\"w-full\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst {\n icon = 'pi-expand',\n min = 10,\n max = 150,\n step = 1\n} = defineProps<{\n icon?: string\n tooltipText: string\n min?: number\n max?: number\n step?: number\n}>()\n\nconst value = defineModel<number>()\nconst showSlider = ref(false)\n\nconst toggleSlider = () => {\n showSlider.value = !showSlider.value\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-slider')) {\n showSlider.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <div class=\"relative show-slider\">\n <Button\n v-tooltip.right=\"{ value: tooltipText, showDelay: 300 }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"tooltipText\"\n @click=\"toggleSlider\"\n >\n <i :class=\"['pi', icon, 'text-lg text-base-foreground']\" />\n </Button>\n <div\n v-show=\"showSlider\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface p-4 shadow-lg w-[150px]\"\n >\n <Slider\n v-model=\"value\"\n class=\"w-full\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst {\n icon = 'pi-expand',\n min = 10,\n max = 150,\n step = 1\n} = defineProps<{\n icon?: string\n tooltipText: string\n min?: number\n max?: number\n step?: number\n}>()\n\nconst value = defineModel<number>()\nconst showSlider = ref(false)\n\nconst toggleSlider = () => {\n showSlider.value = !showSlider.value\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-slider')) {\n showSlider.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.switchCamera'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.switchCamera')\"\n @click=\"switchCamera\"\n >\n <i :class=\"['pi', 'pi-camera', 'text-lg text-base-foreground']\" />\n </Button>\n <PopupSlider\n v-if=\"showFOVButton\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { CameraType } from '@/extensions/core/load3d/interfaces'\n\nconst cameraType = defineModel<CameraType>('cameraType')\nconst fov = defineModel<number>('fov')\nconst showFOVButton = computed(() => cameraType.value === 'perspective')\n\nconst switchCamera = () => {\n cameraType.value =\n cameraType.value === 'perspective' ? 'orthographic' : 'perspective'\n}\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.switchCamera'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.switchCamera')\"\n @click=\"switchCamera\"\n >\n <i :class=\"['pi', 'pi-camera', 'text-lg text-base-foreground']\" />\n </Button>\n <PopupSlider\n v-if=\"showFOVButton\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { CameraType } from '@/extensions/core/load3d/interfaces'\n\nconst cameraType = defineModel<CameraType>('cameraType')\nconst fov = defineModel<number>('fov')\nconst showFOVButton = computed(() => cameraType.value === 'perspective')\n\nconst switchCamera = () => {\n cameraType.value =\n cameraType.value === 'perspective' ? 'orthographic' : 'perspective'\n}\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-export-formats relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.exportModel'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportModel')\"\n @click=\"toggleExportFormats\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showExportFormats\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"format in exportFormats\"\n :key=\"format.value\"\n variant=\"textonly\"\n class=\"text-base-foreground\"\n @click=\"exportModel(format.value)\"\n >\n {{ format.label }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst emit = defineEmits<{\n (e: 'exportModel', format: string): void\n}>()\n\nconst showExportFormats = ref(false)\n\nconst exportFormats = [\n { label: 'GLB', value: 'glb' },\n { label: 'OBJ', value: 'obj' },\n { label: 'STL', value: 'stl' }\n]\n\nfunction toggleExportFormats() {\n showExportFormats.value = !showExportFormats.value\n}\n\nfunction exportModel(format: string) {\n emit('exportModel', format)\n\n showExportFormats.value = false\n}\n\nfunction closeExportFormatsList(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-export-formats')) {\n showExportFormats.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeExportFormatsList)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeExportFormatsList)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-export-formats relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.exportModel'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportModel')\"\n @click=\"toggleExportFormats\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showExportFormats\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"format in exportFormats\"\n :key=\"format.value\"\n variant=\"textonly\"\n class=\"text-base-foreground\"\n @click=\"exportModel(format.value)\"\n >\n {{ format.label }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst emit = defineEmits<{\n (e: 'exportModel', format: string): void\n}>()\n\nconst showExportFormats = ref(false)\n\nconst exportFormats = [\n { label: 'GLB', value: 'glb' },\n { label: 'OBJ', value: 'obj' },\n { label: 'STL', value: 'stl' }\n]\n\nfunction toggleExportFormats() {\n showExportFormats.value = !showExportFormats.value\n}\n\nfunction exportModel(format: string) {\n emit('exportModel', format)\n\n showExportFormats.value = false\n}\n\nfunction closeExportFormatsList(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-export-formats')) {\n showExportFormats.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeExportFormatsList)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeExportFormatsList)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div v-if=\"showLightIntensityButton\" class=\"show-light-intensity relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.lightIntensity'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.lightIntensity')\"\n @click=\"toggleLightIntensity\"\n >\n <i class=\"pi pi-sun text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showLightIntensity\"\n class=\"absolute top-0 left-12 rounded-lg bg-black/50 p-4 shadow-lg\"\n style=\"width: 150px\"\n >\n <Slider\n v-model=\"lightIntensity\"\n class=\"w-full\"\n :min=\"lightIntensityMinimum\"\n :max=\"lightIntensityMaximum\"\n :step=\"lightAdjustmentIncrement\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type { MaterialMode } from '@/extensions/core/load3d/interfaces'\nimport { useSettingStore } from '@/platform/settings/settingStore'\n\nconst lightIntensity = defineModel<number>('lightIntensity')\nconst materialMode = defineModel<MaterialMode>('materialMode')\n\nconst showLightIntensityButton = computed(\n () => materialMode.value === 'original'\n)\nconst showLightIntensity = ref(false)\n\nconst lightIntensityMaximum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMaximum'\n)\nconst lightIntensityMinimum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMinimum'\n)\nconst lightAdjustmentIncrement = useSettingStore().get(\n 'Comfy.Load3D.LightAdjustmentIncrement'\n)\n\nfunction toggleLightIntensity() {\n showLightIntensity.value = !showLightIntensity.value\n}\n\nfunction closeLightSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-light-intensity')) {\n showLightIntensity.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeLightSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeLightSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div v-if=\"showLightIntensityButton\" class=\"show-light-intensity relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.lightIntensity'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.lightIntensity')\"\n @click=\"toggleLightIntensity\"\n >\n <i class=\"pi pi-sun text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showLightIntensity\"\n class=\"absolute top-0 left-12 rounded-lg bg-black/50 p-4 shadow-lg\"\n style=\"width: 150px\"\n >\n <Slider\n v-model=\"lightIntensity\"\n class=\"w-full\"\n :min=\"lightIntensityMinimum\"\n :max=\"lightIntensityMaximum\"\n :step=\"lightAdjustmentIncrement\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type { MaterialMode } from '@/extensions/core/load3d/interfaces'\nimport { useSettingStore } from '@/platform/settings/settingStore'\n\nconst lightIntensity = defineModel<number>('lightIntensity')\nconst materialMode = defineModel<MaterialMode>('materialMode')\n\nconst showLightIntensityButton = computed(\n () => materialMode.value === 'original'\n)\nconst showLightIntensity = ref(false)\n\nconst lightIntensityMaximum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMaximum'\n)\nconst lightIntensityMinimum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMinimum'\n)\nconst lightAdjustmentIncrement = useSettingStore().get(\n 'Comfy.Load3D.LightAdjustmentIncrement'\n)\n\nfunction toggleLightIntensity() {\n showLightIntensity.value = !showLightIntensity.value\n}\n\nfunction closeLightSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-light-intensity')) {\n showLightIntensity.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeLightSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeLightSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-up-direction relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.upDirection'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.upDirection')\"\n @click=\"toggleUpDirection\"\n >\n <i class=\"pi pi-arrow-up text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showUpDirection\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"direction in upDirections\"\n :key=\"direction\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'text-base-foreground',\n upDirection === direction && 'bg-blue-500'\n )\n \"\n @click=\"selectUpDirection(direction)\"\n >\n {{ direction.toUpperCase() }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"!hideMaterialMode\" class=\"show-material-mode relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.materialMode'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.materialMode')\"\n @click=\"toggleMaterialMode\"\n >\n <i class=\"pi pi-box text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showMaterialMode\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"mode in materialModes\"\n :key=\"mode\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'whitespace-nowrap text-base-foreground',\n materialMode === mode && 'bg-blue-500'\n )\n \"\n @click=\"selectMaterialMode(mode)\"\n >\n {{ formatMaterialMode(mode) }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"hasSkeleton\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.showSkeleton'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"cn('rounded-full', showSkeleton && 'bg-blue-500')\"\n :aria-label=\"t('load3d.showSkeleton')\"\n @click=\"showSkeleton = !showSkeleton\"\n >\n <i class=\"pi pi-sitemap text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n MaterialMode,\n UpDirection\n} from '@/extensions/core/load3d/interfaces'\nimport { t } from '@/i18n'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n hideMaterialMode = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n hideMaterialMode?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst materialMode = defineModel<MaterialMode>('materialMode')\nconst upDirection = defineModel<UpDirection>('upDirection')\nconst showSkeleton = defineModel<boolean>('showSkeleton')\n\nconst showUpDirection = ref(false)\nconst showMaterialMode = ref(false)\n\nconst upDirections: UpDirection[] = [\n 'original',\n '-x',\n '+x',\n '-y',\n '+y',\n '-z',\n '+z'\n]\n\nconst materialModes = computed(() => {\n const modes: MaterialMode[] = [\n 'original',\n 'normal',\n 'wireframe'\n //'depth' disable for now\n ]\n\n // Only show pointCloud mode for PLY files (point cloud rendering)\n if (isPlyModel) {\n modes.splice(1, 0, 'pointCloud')\n }\n\n return modes\n})\n\nfunction toggleUpDirection() {\n showUpDirection.value = !showUpDirection.value\n showMaterialMode.value = false\n}\n\nfunction selectUpDirection(direction: UpDirection) {\n upDirection.value = direction\n showUpDirection.value = false\n}\n\nfunction toggleMaterialMode() {\n showMaterialMode.value = !showMaterialMode.value\n showUpDirection.value = false\n}\n\nfunction selectMaterialMode(mode: MaterialMode) {\n materialMode.value = mode\n showMaterialMode.value = false\n}\n\nfunction formatMaterialMode(mode: MaterialMode) {\n return t(`load3d.materialModes.${mode}`)\n}\n\nfunction closeSceneSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-up-direction')) {\n showUpDirection.value = false\n }\n\n if (!target.closest('.show-material-mode')) {\n showMaterialMode.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSceneSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSceneSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-up-direction relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.upDirection'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.upDirection')\"\n @click=\"toggleUpDirection\"\n >\n <i class=\"pi pi-arrow-up text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showUpDirection\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"direction in upDirections\"\n :key=\"direction\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'text-base-foreground',\n upDirection === direction && 'bg-blue-500'\n )\n \"\n @click=\"selectUpDirection(direction)\"\n >\n {{ direction.toUpperCase() }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"!hideMaterialMode\" class=\"show-material-mode relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.materialMode'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.materialMode')\"\n @click=\"toggleMaterialMode\"\n >\n <i class=\"pi pi-box text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showMaterialMode\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"mode in materialModes\"\n :key=\"mode\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'whitespace-nowrap text-base-foreground',\n materialMode === mode && 'bg-blue-500'\n )\n \"\n @click=\"selectMaterialMode(mode)\"\n >\n {{ formatMaterialMode(mode) }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"hasSkeleton\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.showSkeleton'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"cn('rounded-full', showSkeleton && 'bg-blue-500')\"\n :aria-label=\"t('load3d.showSkeleton')\"\n @click=\"showSkeleton = !showSkeleton\"\n >\n <i class=\"pi pi-sitemap text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n MaterialMode,\n UpDirection\n} from '@/extensions/core/load3d/interfaces'\nimport { t } from '@/i18n'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n hideMaterialMode = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n hideMaterialMode?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst materialMode = defineModel<MaterialMode>('materialMode')\nconst upDirection = defineModel<UpDirection>('upDirection')\nconst showSkeleton = defineModel<boolean>('showSkeleton')\n\nconst showUpDirection = ref(false)\nconst showMaterialMode = ref(false)\n\nconst upDirections: UpDirection[] = [\n 'original',\n '-x',\n '+x',\n '-y',\n '+y',\n '-z',\n '+z'\n]\n\nconst materialModes = computed(() => {\n const modes: MaterialMode[] = [\n 'original',\n 'normal',\n 'wireframe'\n //'depth' disable for now\n ]\n\n // Only show pointCloud mode for PLY files (point cloud rendering)\n if (isPlyModel) {\n modes.splice(1, 0, 'pointCloud')\n }\n\n return modes\n})\n\nfunction toggleUpDirection() {\n showUpDirection.value = !showUpDirection.value\n showMaterialMode.value = false\n}\n\nfunction selectUpDirection(direction: UpDirection) {\n upDirection.value = direction\n showUpDirection.value = false\n}\n\nfunction toggleMaterialMode() {\n showMaterialMode.value = !showMaterialMode.value\n showUpDirection.value = false\n}\n\nfunction selectMaterialMode(mode: MaterialMode) {\n materialMode.value = mode\n showMaterialMode.value = false\n}\n\nfunction formatMaterialMode(mode: MaterialMode) {\n return t(`load3d.materialModes.${mode}`)\n}\n\nfunction closeSceneSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-up-direction')) {\n showUpDirection.value = false\n }\n\n if (!target.closest('.show-material-mode')) {\n showMaterialMode.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSceneSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSceneSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{ value: $t('load3d.showGrid'), showDelay: 300 }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"cn('rounded-full', showGrid && 'ring-2 ring-white/50')\"\n :aria-label=\"$t('load3d.showGrid')\"\n @click=\"toggleGrid\"\n >\n <i class=\"pi pi-table text-lg text-base-foreground\" />\n </Button>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.backgroundColor'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.backgroundColor')\"\n @click=\"openColorPicker\"\n >\n <i class=\"pi pi-palette text-lg text-base-foreground\" />\n <input\n ref=\"colorPickerRef\"\n type=\"color\"\n :value=\"backgroundColor\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @input=\"\n updateBackgroundColor(($event.target as HTMLInputElement).value)\n \"\n />\n </Button>\n </div>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.uploadBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.uploadBackgroundImage')\"\n @click=\"openImagePicker\"\n >\n <i class=\"pi pi-image text-lg text-base-foreground\" />\n <input\n ref=\"imagePickerRef\"\n type=\"file\"\n accept=\"image/*\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @change=\"uploadBackgroundImage\"\n />\n </Button>\n </div>\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.panoramaMode'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"\n cn(\n 'rounded-full',\n backgroundRenderMode === 'panorama' && 'ring-2 ring-white/50'\n )\n \"\n :aria-label=\"$t('load3d.panoramaMode')\"\n @click=\"toggleBackgroundRenderMode\"\n >\n <i class=\"pi pi-globe text-lg text-base-foreground\" />\n </Button>\n </div>\n\n <PopupSlider\n v-if=\"hasBackgroundImage && backgroundRenderMode === 'panorama'\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.removeBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.removeBackgroundImage')\"\n @click=\"removeBackgroundImage\"\n >\n <i class=\"pi pi-times text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { BackgroundRenderModeType } from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n}>()\n\nconst showGrid = defineModel<boolean>('showGrid')\nconst backgroundColor = defineModel<string>('backgroundColor')\nconst backgroundImage = defineModel<string>('backgroundImage')\nconst backgroundRenderMode = defineModel<BackgroundRenderModeType>(\n 'backgroundRenderMode',\n { default: 'tiled' }\n)\nconst fov = defineModel<number>('fov')\nconst hasBackgroundImage = computed(\n () => backgroundImage.value && backgroundImage.value !== ''\n)\n\nconst colorPickerRef = ref<HTMLInputElement | null>(null)\nconst imagePickerRef = ref<HTMLInputElement | null>(null)\n\nconst toggleGrid = () => {\n showGrid.value = !showGrid.value\n}\n\nconst updateBackgroundColor = (color: string) => {\n backgroundColor.value = color\n}\n\nconst openColorPicker = () => {\n colorPickerRef.value?.click()\n}\n\nconst openImagePicker = () => {\n imagePickerRef.value?.click()\n}\n\nconst uploadBackgroundImage = (event: Event) => {\n const input = event.target as HTMLInputElement\n\n if (input.files && input.files[0]) {\n emit('updateBackgroundImage', input.files[0])\n }\n}\n\nconst removeBackgroundImage = () => {\n emit('updateBackgroundImage', null)\n}\n\nconst toggleBackgroundRenderMode = () => {\n backgroundRenderMode.value =\n backgroundRenderMode.value === 'panorama' ? 'tiled' : 'panorama'\n}\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{ value: $t('load3d.showGrid'), showDelay: 300 }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"cn('rounded-full', showGrid && 'ring-2 ring-white/50')\"\n :aria-label=\"$t('load3d.showGrid')\"\n @click=\"toggleGrid\"\n >\n <i class=\"pi pi-table text-lg text-base-foreground\" />\n </Button>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.backgroundColor'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.backgroundColor')\"\n @click=\"openColorPicker\"\n >\n <i class=\"pi pi-palette text-lg text-base-foreground\" />\n <input\n ref=\"colorPickerRef\"\n type=\"color\"\n :value=\"backgroundColor\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @input=\"\n updateBackgroundColor(($event.target as HTMLInputElement).value)\n \"\n />\n </Button>\n </div>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.uploadBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.uploadBackgroundImage')\"\n @click=\"openImagePicker\"\n >\n <i class=\"pi pi-image text-lg text-base-foreground\" />\n <input\n ref=\"imagePickerRef\"\n type=\"file\"\n accept=\"image/*\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @change=\"uploadBackgroundImage\"\n />\n </Button>\n </div>\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.panoramaMode'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"\n cn(\n 'rounded-full',\n backgroundRenderMode === 'panorama' && 'ring-2 ring-white/50'\n )\n \"\n :aria-label=\"$t('load3d.panoramaMode')\"\n @click=\"toggleBackgroundRenderMode\"\n >\n <i class=\"pi pi-globe text-lg text-base-foreground\" />\n </Button>\n </div>\n\n <PopupSlider\n v-if=\"hasBackgroundImage && backgroundRenderMode === 'panorama'\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.removeBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.removeBackgroundImage')\"\n @click=\"removeBackgroundImage\"\n >\n <i class=\"pi pi-times text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { BackgroundRenderModeType } from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n}>()\n\nconst showGrid = defineModel<boolean>('showGrid')\nconst backgroundColor = defineModel<string>('backgroundColor')\nconst backgroundImage = defineModel<string>('backgroundImage')\nconst backgroundRenderMode = defineModel<BackgroundRenderModeType>(\n 'backgroundRenderMode',\n { default: 'tiled' }\n)\nconst fov = defineModel<number>('fov')\nconst hasBackgroundImage = computed(\n () => backgroundImage.value && backgroundImage.value !== ''\n)\n\nconst colorPickerRef = ref<HTMLInputElement | null>(null)\nconst imagePickerRef = ref<HTMLInputElement | null>(null)\n\nconst toggleGrid = () => {\n showGrid.value = !showGrid.value\n}\n\nconst updateBackgroundColor = (color: string) => {\n backgroundColor.value = color\n}\n\nconst openColorPicker = () => {\n colorPickerRef.value?.click()\n}\n\nconst openImagePicker = () => {\n imagePickerRef.value?.click()\n}\n\nconst uploadBackgroundImage = (event: Event) => {\n const input = event.target as HTMLInputElement\n\n if (input.files && input.files[0]) {\n emit('updateBackgroundImage', input.files[0])\n }\n}\n\nconst removeBackgroundImage = () => {\n emit('updateBackgroundImage', null)\n}\n\nconst toggleBackgroundRenderMode = () => {\n backgroundRenderMode.value =\n backgroundRenderMode.value === 'panorama' ? 'tiled' : 'panorama'\n}\n</script>\n","<template>\n <div\n class=\"pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-backdrop/30\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @wheel.stop\n >\n <div class=\"show-menu relative\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('menu.showMenu')\"\n class=\"rounded-full\"\n @click=\"toggleMenu\"\n >\n <i class=\"pi pi-bars text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-show=\"isMenuOpen\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"category in availableCategories\"\n :key=\"category\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'flex w-full items-center justify-start',\n activeCategory === category && 'bg-button-active-surface'\n )\n \"\n @click=\"selectCategory(category)\"\n >\n <i :class=\"getCategoryIcon(category)\" />\n <span class=\"whitespace-nowrap text-base-foreground\">{{\n $t(categoryLabels[category])\n }}</span>\n </Button>\n </div>\n </div>\n </div>\n\n <div v-show=\"activeCategory\" class=\"rounded-lg bg-smoke-700/30\">\n <SceneControls\n v-if=\"showSceneControls\"\n v-model:show-grid=\"sceneConfig!.showGrid\"\n v-model:background-color=\"sceneConfig!.backgroundColor\"\n v-model:background-image=\"sceneConfig!.backgroundImage\"\n v-model:background-render-mode=\"sceneConfig!.backgroundRenderMode\"\n v-model:fov=\"cameraConfig!.fov\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n />\n\n <ModelControls\n v-if=\"showModelControls\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n v-model:up-direction=\"modelConfig!.upDirection\"\n v-model:show-skeleton=\"modelConfig!.showSkeleton\"\n :hide-material-mode=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n />\n\n <CameraControls\n v-if=\"showCameraControls\"\n v-model:camera-type=\"cameraConfig!.cameraType\"\n v-model:fov=\"cameraConfig!.fov\"\n />\n\n <LightControls\n v-if=\"showLightControls\"\n v-model:light-intensity=\"lightConfig!.intensity\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n />\n\n <ExportControls\n v-if=\"showExportControls\"\n @export-model=\"handleExportModel\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport CameraControls from '@/components/load3d/controls/CameraControls.vue'\nimport ExportControls from '@/components/load3d/controls/ExportControls.vue'\nimport LightControls from '@/components/load3d/controls/LightControls.vue'\nimport ModelControls from '@/components/load3d/controls/ModelControls.vue'\nimport SceneControls from '@/components/load3d/controls/SceneControls.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n CameraConfig,\n LightConfig,\n ModelConfig,\n SceneConfig\n} from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n isSplatModel = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n isSplatModel?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst sceneConfig = defineModel<SceneConfig>('sceneConfig')\nconst modelConfig = defineModel<ModelConfig>('modelConfig')\nconst cameraConfig = defineModel<CameraConfig>('cameraConfig')\nconst lightConfig = defineModel<LightConfig>('lightConfig')\n\nconst isMenuOpen = ref(false)\nconst activeCategory = ref<string>('scene')\nconst categoryLabels: Record<string, string> = {\n scene: 'load3d.scene',\n model: 'load3d.model',\n camera: 'load3d.camera',\n light: 'load3d.light',\n export: 'load3d.export'\n}\n\nconst availableCategories = computed(() => {\n if (isSplatModel) {\n return ['scene', 'model', 'camera']\n }\n\n return ['scene', 'model', 'camera', 'light', 'export']\n})\n\nconst showSceneControls = computed(\n () => activeCategory.value === 'scene' && !!sceneConfig.value\n)\nconst showModelControls = computed(\n () => activeCategory.value === 'model' && !!modelConfig.value\n)\nconst showCameraControls = computed(\n () => activeCategory.value === 'camera' && !!cameraConfig.value\n)\nconst showLightControls = computed(\n () =>\n activeCategory.value === 'light' &&\n !!lightConfig.value &&\n !!modelConfig.value\n)\nconst showExportControls = computed(() => activeCategory.value === 'export')\n\nconst toggleMenu = () => {\n isMenuOpen.value = !isMenuOpen.value\n}\n\nconst selectCategory = (category: string) => {\n activeCategory.value = category\n isMenuOpen.value = false\n}\n\nconst getCategoryIcon = (category: string) => {\n const icons = {\n scene: 'pi pi-image',\n model: 'pi pi-box',\n camera: 'pi pi-camera',\n light: 'pi pi-sun',\n export: 'pi pi-download'\n }\n // @ts-expect-error fixme ts strict error\n return `${icons[category]} text-base-foreground text-lg`\n}\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n (e: 'exportModel', format: string): void\n}>()\n\nconst handleBackgroundImageUpdate = (file: File | null) => {\n emit('updateBackgroundImage', file)\n}\n\nconst handleExportModel = (format: string) => {\n emit('exportModel', format)\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-menu')) {\n isMenuOpen.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <div\n class=\"pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-backdrop/30\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @wheel.stop\n >\n <div class=\"show-menu relative\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('menu.showMenu')\"\n class=\"rounded-full\"\n @click=\"toggleMenu\"\n >\n <i class=\"pi pi-bars text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-show=\"isMenuOpen\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"category in availableCategories\"\n :key=\"category\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'flex w-full items-center justify-start',\n activeCategory === category && 'bg-button-active-surface'\n )\n \"\n @click=\"selectCategory(category)\"\n >\n <i :class=\"getCategoryIcon(category)\" />\n <span class=\"whitespace-nowrap text-base-foreground\">{{\n $t(categoryLabels[category])\n }}</span>\n </Button>\n </div>\n </div>\n </div>\n\n <div v-show=\"activeCategory\" class=\"rounded-lg bg-smoke-700/30\">\n <SceneControls\n v-if=\"showSceneControls\"\n v-model:show-grid=\"sceneConfig!.showGrid\"\n v-model:background-color=\"sceneConfig!.backgroundColor\"\n v-model:background-image=\"sceneConfig!.backgroundImage\"\n v-model:background-render-mode=\"sceneConfig!.backgroundRenderMode\"\n v-model:fov=\"cameraConfig!.fov\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n />\n\n <ModelControls\n v-if=\"showModelControls\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n v-model:up-direction=\"modelConfig!.upDirection\"\n v-model:show-skeleton=\"modelConfig!.showSkeleton\"\n :hide-material-mode=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n />\n\n <CameraControls\n v-if=\"showCameraControls\"\n v-model:camera-type=\"cameraConfig!.cameraType\"\n v-model:fov=\"cameraConfig!.fov\"\n />\n\n <LightControls\n v-if=\"showLightControls\"\n v-model:light-intensity=\"lightConfig!.intensity\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n />\n\n <ExportControls\n v-if=\"showExportControls\"\n @export-model=\"handleExportModel\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport CameraControls from '@/components/load3d/controls/CameraControls.vue'\nimport ExportControls from '@/components/load3d/controls/ExportControls.vue'\nimport LightControls from '@/components/load3d/controls/LightControls.vue'\nimport ModelControls from '@/components/load3d/controls/ModelControls.vue'\nimport SceneControls from '@/components/load3d/controls/SceneControls.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n CameraConfig,\n LightConfig,\n ModelConfig,\n SceneConfig\n} from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n isSplatModel = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n isSplatModel?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst sceneConfig = defineModel<SceneConfig>('sceneConfig')\nconst modelConfig = defineModel<ModelConfig>('modelConfig')\nconst cameraConfig = defineModel<CameraConfig>('cameraConfig')\nconst lightConfig = defineModel<LightConfig>('lightConfig')\n\nconst isMenuOpen = ref(false)\nconst activeCategory = ref<string>('scene')\nconst categoryLabels: Record<string, string> = {\n scene: 'load3d.scene',\n model: 'load3d.model',\n camera: 'load3d.camera',\n light: 'load3d.light',\n export: 'load3d.export'\n}\n\nconst availableCategories = computed(() => {\n if (isSplatModel) {\n return ['scene', 'model', 'camera']\n }\n\n return ['scene', 'model', 'camera', 'light', 'export']\n})\n\nconst showSceneControls = computed(\n () => activeCategory.value === 'scene' && !!sceneConfig.value\n)\nconst showModelControls = computed(\n () => activeCategory.value === 'model' && !!modelConfig.value\n)\nconst showCameraControls = computed(\n () => activeCategory.value === 'camera' && !!cameraConfig.value\n)\nconst showLightControls = computed(\n () =>\n activeCategory.value === 'light' &&\n !!lightConfig.value &&\n !!modelConfig.value\n)\nconst showExportControls = computed(() => activeCategory.value === 'export')\n\nconst toggleMenu = () => {\n isMenuOpen.value = !isMenuOpen.value\n}\n\nconst selectCategory = (category: string) => {\n activeCategory.value = category\n isMenuOpen.value = false\n}\n\nconst getCategoryIcon = (category: string) => {\n const icons = {\n scene: 'pi pi-image',\n model: 'pi pi-box',\n camera: 'pi pi-camera',\n light: 'pi pi-sun',\n export: 'pi pi-download'\n }\n // @ts-expect-error fixme ts strict error\n return `${icons[category]} text-base-foreground text-lg`\n}\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n (e: 'exportModel', format: string): void\n}>()\n\nconst handleBackgroundImageUpdate = (file: File | null) => {\n emit('updateBackgroundImage', file)\n}\n\nconst handleExportModel = (format: string) => {\n emit('exportModel', format)\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-menu')) {\n isMenuOpen.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <Transition name=\"fade\">\n <div\n v-if=\"loading\"\n class=\"absolute inset-0 z-50 flex items-center justify-center bg-backdrop/50\"\n >\n <div class=\"flex flex-col items-center\">\n <div class=\"spinner\" />\n <div class=\"mt-4 text-lg text-base-foreground\">\n {{ loadingMessage }}\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{\n loading: boolean\n loadingMessage: string\n}>()\n</script>\n\n<style scoped>\n.spinner {\n width: 50px;\n height: 50px;\n border: 4px solid #f3f3f3;\n border-top: 4px solid #3498db;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\n <Transition name=\"fade\">\n <div\n v-if=\"loading\"\n class=\"absolute inset-0 z-50 flex items-center justify-center bg-backdrop/50\"\n >\n <div class=\"flex flex-col items-center\">\n <div class=\"spinner\" />\n <div class=\"mt-4 text-lg text-base-foreground\">\n {{ loadingMessage }}\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{\n loading: boolean\n loadingMessage: string\n}>()\n</script>\n\n<style scoped>\n.spinner {\n width: 50px;\n height: 50px;\n border: 4px solid #f3f3f3;\n border-top: 4px solid #3498db;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\n <div\n ref=\"container\"\n class=\"relative h-full w-full min-h-[200px]\"\n data-capture-wheel=\"true\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @mousedown.stop\n @mousemove.stop\n @mouseup.stop\n @contextmenu.stop.prevent\n @dragover.prevent.stop=\"handleDragOver\"\n @dragleave.stop=\"handleDragLeave\"\n @drop.prevent.stop=\"handleDrop\"\n >\n <LoadingOverlay :loading=\"loading\" :loading-message=\"loadingMessage\" />\n <div\n v-if=\"!isPreview && isDragging\"\n class=\"pointer-events-none absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm\"\n >\n <div\n class=\"rounded-lg border-2 border-dashed border-blue-400 bg-blue-500/20 px-6 py-4 text-lg font-medium text-blue-100\"\n >\n {{ dragMessage }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport LoadingOverlay from '@/components/load3d/LoadingOverlay.vue'\nimport { useLoad3dDrag } from '@/composables/useLoad3dDrag'\n\nconst props = defineProps<{\n initializeLoad3d: (containerRef: HTMLElement) => Promise<void>\n cleanup: () => void\n loading: boolean\n loadingMessage: string\n onModelDrop?: (file: File) => void | Promise<void>\n isPreview: boolean\n}>()\n\nconst container = ref<HTMLElement | null>(null)\n\nconst { isDragging, dragMessage, handleDragOver, handleDragLeave, handleDrop } =\n useLoad3dDrag({\n onModelDrop: async (file) => {\n if (props.onModelDrop) {\n await props.onModelDrop(file)\n }\n },\n disabled: computed(() => props.isPreview)\n })\n\nonMounted(() => {\n if (container.value) {\n void props.initializeLoad3d(container.value)\n }\n})\n\nonUnmounted(() => {\n props.cleanup()\n})\n</script>\n","<template>\n <div\n ref=\"container\"\n class=\"relative h-full w-full min-h-[200px]\"\n data-capture-wheel=\"true\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @mousedown.stop\n @mousemove.stop\n @mouseup.stop\n @contextmenu.stop.prevent\n @dragover.prevent.stop=\"handleDragOver\"\n @dragleave.stop=\"handleDragLeave\"\n @drop.prevent.stop=\"handleDrop\"\n >\n <LoadingOverlay :loading=\"loading\" :loading-message=\"loadingMessage\" />\n <div\n v-if=\"!isPreview && isDragging\"\n class=\"pointer-events-none absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm\"\n >\n <div\n class=\"rounded-lg border-2 border-dashed border-blue-400 bg-blue-500/20 px-6 py-4 text-lg font-medium text-blue-100\"\n >\n {{ dragMessage }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport LoadingOverlay from '@/components/load3d/LoadingOverlay.vue'\nimport { useLoad3dDrag } from '@/composables/useLoad3dDrag'\n\nconst props = defineProps<{\n initializeLoad3d: (containerRef: HTMLElement) => Promise<void>\n cleanup: () => void\n loading: boolean\n loadingMessage: string\n onModelDrop?: (file: File) => void | Promise<void>\n isPreview: boolean\n}>()\n\nconst container = ref<HTMLElement | null>(null)\n\nconst { isDragging, dragMessage, handleDragOver, handleDragLeave, handleDrop } =\n useLoad3dDrag({\n onModelDrop: async (file) => {\n if (props.onModelDrop) {\n await props.onModelDrop(file)\n }\n },\n disabled: computed(() => props.isPreview)\n })\n\nonMounted(() => {\n if (container.value) {\n void props.initializeLoad3d(container.value)\n }\n})\n\nonUnmounted(() => {\n props.cleanup()\n})\n</script>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: isRecording\n ? $t('load3d.stopRecording')\n : $t('load3d.startRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'rounded-full',\n isRecording && 'text-red-500 recording-button-blink'\n )\n \"\n :aria-label=\"\n isRecording ? $t('load3d.stopRecording') : $t('load3d.startRecording')\n \"\n @click=\"toggleRecording\"\n >\n <i\n :class=\"[\n 'pi',\n isRecording ? 'pi-circle-fill' : 'pi-video',\n 'text-lg text-base-foreground'\n ]\"\n />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.exportRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportRecording')\"\n @click=\"handleExportRecording\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.clearRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.clearRecording')\"\n @click=\"handleClearRecording\"\n >\n <i class=\"pi pi-trash text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-if=\"recordingDuration && recordingDuration > 0 && !isRecording\"\n class=\"mt-1 text-center text-xs text-base-foreground\"\n >\n {{ formatDuration(recordingDuration) }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Button from '@/components/ui/button/Button.vue'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst hasRecording = defineModel<boolean>('hasRecording')\nconst isRecording = defineModel<boolean>('isRecording')\nconst recordingDuration = defineModel<number>('recordingDuration')\n\nconst emit = defineEmits<{\n (e: 'startRecording'): void\n (e: 'stopRecording'): void\n (e: 'exportRecording'): void\n (e: 'clearRecording'): void\n}>()\n\nfunction toggleRecording() {\n if (isRecording.value) {\n emit('stopRecording')\n } else {\n emit('startRecording')\n }\n}\n\nfunction handleExportRecording() {\n emit('exportRecording')\n}\n\nfunction handleClearRecording() {\n emit('clearRecording')\n}\n\nfunction formatDuration(seconds: number): string {\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.floor(seconds % 60)\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`\n}\n</script>\n\n<style scoped>\n.recording-button-blink {\n animation: blink 1s infinite;\n}\n\n@keyframes blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n</style>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: isRecording\n ? $t('load3d.stopRecording')\n : $t('load3d.startRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'rounded-full',\n isRecording && 'text-red-500 recording-button-blink'\n )\n \"\n :aria-label=\"\n isRecording ? $t('load3d.stopRecording') : $t('load3d.startRecording')\n \"\n @click=\"toggleRecording\"\n >\n <i\n :class=\"[\n 'pi',\n isRecording ? 'pi-circle-fill' : 'pi-video',\n 'text-lg text-base-foreground'\n ]\"\n />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.exportRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportRecording')\"\n @click=\"handleExportRecording\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.clearRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.clearRecording')\"\n @click=\"handleClearRecording\"\n >\n <i class=\"pi pi-trash text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-if=\"recordingDuration && recordingDuration > 0 && !isRecording\"\n class=\"mt-1 text-center text-xs text-base-foreground\"\n >\n {{ formatDuration(recordingDuration) }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Button from '@/components/ui/button/Button.vue'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst hasRecording = defineModel<boolean>('hasRecording')\nconst isRecording = defineModel<boolean>('isRecording')\nconst recordingDuration = defineModel<number>('recordingDuration')\n\nconst emit = defineEmits<{\n (e: 'startRecording'): void\n (e: 'stopRecording'): void\n (e: 'exportRecording'): void\n (e: 'clearRecording'): void\n}>()\n\nfunction toggleRecording() {\n if (isRecording.value) {\n emit('stopRecording')\n } else {\n emit('startRecording')\n }\n}\n\nfunction handleExportRecording() {\n emit('exportRecording')\n}\n\nfunction handleClearRecording() {\n emit('clearRecording')\n}\n\nfunction formatDuration(seconds: number): string {\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.floor(seconds % 60)\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`\n}\n</script>\n\n<style scoped>\n.recording-button-blink {\n animation: blink 1s infinite;\n}\n\n@keyframes blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n</style>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.openIn3DViewer'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.openIn3DViewer')\"\n @click=\"openIn3DViewer\"\n >\n <i class=\"pi pi-expand text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { t } from '@/i18n'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useLoad3dService } from '@/services/load3dService'\nimport { useDialogStore } from '@/stores/dialogStore'\n\nconst { node } = defineProps<{\n node: LGraphNode\n}>()\n\nconst openIn3DViewer = () => {\n const props = { node: node }\n\n useDialogStore().showDialog({\n key: 'global-load3d-viewer',\n title: t('load3d.viewer.title'),\n component: Load3DViewerContent,\n props: props,\n dialogComponentProps: {\n style: 'width: 80vw; height: 80vh;',\n maximizable: true,\n onClose: async () => {\n await useLoad3dService().handleViewerClose(props.node)\n }\n }\n })\n}\n</script>\n\n<style scoped></style>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.openIn3DViewer'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.openIn3DViewer')\"\n @click=\"openIn3DViewer\"\n >\n <i class=\"pi pi-expand text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { t } from '@/i18n'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useLoad3dService } from '@/services/load3dService'\nimport { useDialogStore } from '@/stores/dialogStore'\n\nconst { node } = defineProps<{\n node: LGraphNode\n}>()\n\nconst openIn3DViewer = () => {\n const props = { node: node }\n\n useDialogStore().showDialog({\n key: 'global-load3d-viewer',\n title: t('load3d.viewer.title'),\n component: Load3DViewerContent,\n props: props,\n dialogComponentProps: {\n style: 'width: 80vw; height: 80vh;',\n maximizable: true,\n onClose: async () => {\n await useLoad3dService().handleViewerClose(props.node)\n }\n }\n })\n}\n</script>\n\n<style scoped></style>\n","<template>\n <div\n class=\"relative size-full\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n >\n <Load3DScene\n v-if=\"node\"\n :initialize-load3d=\"initializeLoad3d\"\n :cleanup=\"cleanup\"\n :loading=\"loading\"\n :loading-message=\"loadingMessage\"\n :on-model-drop=\"isPreview ? undefined : handleModelDrop\"\n :is-preview=\"isPreview\"\n />\n <div class=\"pointer-events-none absolute top-0 left-0 size-full\">\n <Load3DControls\n v-model:scene-config=\"sceneConfig\"\n v-model:model-config=\"modelConfig\"\n v-model:camera-config=\"cameraConfig\"\n v-model:light-config=\"lightConfig\"\n :is-splat-model=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n @export-model=\"handleExportModel\"\n />\n <AnimationControls\n v-if=\"animations && animations.length > 0\"\n v-model:animations=\"animations\"\n v-model:playing=\"playing\"\n v-model:selected-speed=\"selectedSpeed\"\n v-model:selected-animation=\"selectedAnimation\"\n v-model:animation-progress=\"animationProgress\"\n v-model:animation-duration=\"animationDuration\"\n @seek=\"handleSeek\"\n />\n </div>\n <div\n v-if=\"enable3DViewer && node\"\n class=\"pointer-events-auto absolute top-12 right-2 z-20\"\n >\n <ViewerControls :node=\"node as LGraphNode\" />\n </div>\n\n <div\n v-if=\"!isPreview\"\n class=\"pointer-events-auto absolute right-2 z-20\"\n :class=\"{\n 'top-12': !enable3DViewer,\n 'top-24': enable3DViewer\n }\"\n >\n <RecordingControls\n v-model:is-recording=\"isRecording\"\n v-model:has-recording=\"hasRecording\"\n v-model:recording-duration=\"recordingDuration\"\n @start-recording=\"handleStartRecording\"\n @stop-recording=\"handleStopRecording\"\n @export-recording=\"handleExportRecording\"\n @clear-recording=\"handleClearRecording\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport type { Ref } from 'vue'\n\nimport Load3DControls from '@/components/load3d/Load3DControls.vue'\nimport Load3DScene from '@/components/load3d/Load3DScene.vue'\nimport AnimationControls from '@/components/load3d/controls/AnimationControls.vue'\nimport RecordingControls from '@/components/load3d/controls/RecordingControls.vue'\nimport ViewerControls from '@/components/load3d/controls/ViewerControls.vue'\nimport { useLoad3d } from '@/composables/useLoad3d'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport { app } from '@/scripts/app'\nimport type { ComponentWidget } from '@/scripts/domWidget'\nimport type { SimplifiedWidget } from '@/types/simplifiedWidget'\n\nconst props = defineProps<{\n widget: ComponentWidget<string[]> | SimplifiedWidget\n nodeId?: NodeId\n}>()\n\nfunction isComponentWidget(\n widget: ComponentWidget<string[]> | SimplifiedWidget\n): widget is ComponentWidget<string[]> {\n return 'node' in widget && widget.node !== undefined\n}\n\nconst node = ref<LGraphNode | null>(null)\n\nif (isComponentWidget(props.widget)) {\n node.value = props.widget.node\n} else if (props.nodeId) {\n onMounted(() => {\n node.value = app.rootGraph?.getNodeById(props.nodeId!) || null\n })\n}\n\nconst {\n // configs\n sceneConfig,\n modelConfig,\n cameraConfig,\n lightConfig,\n\n // other state\n isRecording,\n isPreview,\n isSplatModel,\n isPlyModel,\n hasSkeleton,\n hasRecording,\n recordingDuration,\n animations,\n playing,\n selectedSpeed,\n selectedAnimation,\n animationProgress,\n animationDuration,\n loading,\n loadingMessage,\n\n // Methods\n initializeLoad3d,\n handleMouseEnter,\n handleMouseLeave,\n handleStartRecording,\n handleStopRecording,\n handleExportRecording,\n handleClearRecording,\n handleSeek,\n handleBackgroundImageUpdate,\n handleExportModel,\n handleModelDrop,\n cleanup\n} = useLoad3d(node as Ref<LGraphNode | null>)\n\nconst enable3DViewer = computed(() =>\n useSettingStore().get('Comfy.Load3D.3DViewerEnable')\n)\n</script>\n","<template>\n <div\n class=\"relative size-full\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n >\n <Load3DScene\n v-if=\"node\"\n :initialize-load3d=\"initializeLoad3d\"\n :cleanup=\"cleanup\"\n :loading=\"loading\"\n :loading-message=\"loadingMessage\"\n :on-model-drop=\"isPreview ? undefined : handleModelDrop\"\n :is-preview=\"isPreview\"\n />\n <div class=\"pointer-events-none absolute top-0 left-0 size-full\">\n <Load3DControls\n v-model:scene-config=\"sceneConfig\"\n v-model:model-config=\"modelConfig\"\n v-model:camera-config=\"cameraConfig\"\n v-model:light-config=\"lightConfig\"\n :is-splat-model=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n @export-model=\"handleExportModel\"\n />\n <AnimationControls\n v-if=\"animations && animations.length > 0\"\n v-model:animations=\"animations\"\n v-model:playing=\"playing\"\n v-model:selected-speed=\"selectedSpeed\"\n v-model:selected-animation=\"selectedAnimation\"\n v-model:animation-progress=\"animationProgress\"\n v-model:animation-duration=\"animationDuration\"\n @seek=\"handleSeek\"\n />\n </div>\n <div\n v-if=\"enable3DViewer && node\"\n class=\"pointer-events-auto absolute top-12 right-2 z-20\"\n >\n <ViewerControls :node=\"node as LGraphNode\" />\n </div>\n\n <div\n v-if=\"!isPreview\"\n class=\"pointer-events-auto absolute right-2 z-20\"\n :class=\"{\n 'top-12': !enable3DViewer,\n 'top-24': enable3DViewer\n }\"\n >\n <RecordingControls\n v-model:is-recording=\"isRecording\"\n v-model:has-recording=\"hasRecording\"\n v-model:recording-duration=\"recordingDuration\"\n @start-recording=\"handleStartRecording\"\n @stop-recording=\"handleStopRecording\"\n @export-recording=\"handleExportRecording\"\n @clear-recording=\"handleClearRecording\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport type { Ref } from 'vue'\n\nimport Load3DControls from '@/components/load3d/Load3DControls.vue'\nimport Load3DScene from '@/components/load3d/Load3DScene.vue'\nimport AnimationControls from '@/components/load3d/controls/AnimationControls.vue'\nimport RecordingControls from '@/components/load3d/controls/RecordingControls.vue'\nimport ViewerControls from '@/components/load3d/controls/ViewerControls.vue'\nimport { useLoad3d } from '@/composables/useLoad3d'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport { app } from '@/scripts/app'\nimport type { ComponentWidget } from '@/scripts/domWidget'\nimport type { SimplifiedWidget } from '@/types/simplifiedWidget'\n\nconst props = defineProps<{\n widget: ComponentWidget<string[]> | SimplifiedWidget\n nodeId?: NodeId\n}>()\n\nfunction isComponentWidget(\n widget: ComponentWidget<string[]> | SimplifiedWidget\n): widget is ComponentWidget<string[]> {\n return 'node' in widget && widget.node !== undefined\n}\n\nconst node = ref<LGraphNode | null>(null)\n\nif (isComponentWidget(props.widget)) {\n node.value = props.widget.node\n} else if (props.nodeId) {\n onMounted(() => {\n node.value = app.rootGraph?.getNodeById(props.nodeId!) || null\n })\n}\n\nconst {\n // configs\n sceneConfig,\n modelConfig,\n cameraConfig,\n lightConfig,\n\n // other state\n isRecording,\n isPreview,\n isSplatModel,\n isPlyModel,\n hasSkeleton,\n hasRecording,\n recordingDuration,\n animations,\n playing,\n selectedSpeed,\n selectedAnimation,\n animationProgress,\n animationDuration,\n loading,\n loadingMessage,\n\n // Methods\n initializeLoad3d,\n handleMouseEnter,\n handleMouseLeave,\n handleStartRecording,\n handleStopRecording,\n handleExportRecording,\n handleClearRecording,\n handleSeek,\n handleBackgroundImageUpdate,\n handleExportModel,\n handleModelDrop,\n cleanup\n} = useLoad3d(node as Ref<LGraphNode | null>)\n\nconst enable3DViewer = computed(() =>\n useSettingStore().get('Comfy.Load3D.3DViewerEnable')\n)\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Load3D-vYr8M3jJ.js","names":[],"sources":["../../src/components/load3d/controls/PopupSlider.vue","../../src/components/load3d/controls/PopupSlider.vue","../../src/components/load3d/controls/CameraControls.vue","../../src/components/load3d/controls/CameraControls.vue","../../src/components/load3d/controls/ExportControls.vue","../../src/components/load3d/controls/ExportControls.vue","../../src/components/load3d/controls/LightControls.vue","../../src/components/load3d/controls/LightControls.vue","../../src/components/load3d/controls/ModelControls.vue","../../src/components/load3d/controls/ModelControls.vue","../../src/components/load3d/controls/SceneControls.vue","../../src/components/load3d/controls/SceneControls.vue","../../src/components/load3d/Load3DControls.vue","../../src/components/load3d/Load3DControls.vue","../../src/components/load3d/LoadingOverlay.vue","../../src/components/load3d/LoadingOverlay.vue","../../src/components/load3d/Load3DScene.vue","../../src/components/load3d/Load3DScene.vue","../../src/components/load3d/controls/RecordingControls.vue","../../src/components/load3d/controls/RecordingControls.vue","../../src/components/load3d/controls/ViewerControls.vue","../../src/components/load3d/controls/ViewerControls.vue","../../src/components/load3d/Load3D.vue","../../src/components/load3d/Load3D.vue"],"sourcesContent":["<template>\n <div class=\"relative show-slider\">\n <Button\n v-tooltip.right=\"{ value: tooltipText, showDelay: 300 }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"tooltipText\"\n @click=\"toggleSlider\"\n >\n <i :class=\"['pi', icon, 'text-lg text-base-foreground']\" />\n </Button>\n <div\n v-show=\"showSlider\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface p-4 shadow-lg w-[150px]\"\n >\n <Slider\n v-model=\"value\"\n class=\"w-full\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst {\n icon = 'pi-expand',\n min = 10,\n max = 150,\n step = 1\n} = defineProps<{\n icon?: string\n tooltipText: string\n min?: number\n max?: number\n step?: number\n}>()\n\nconst value = defineModel<number>()\nconst showSlider = ref(false)\n\nconst toggleSlider = () => {\n showSlider.value = !showSlider.value\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-slider')) {\n showSlider.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <div class=\"relative show-slider\">\n <Button\n v-tooltip.right=\"{ value: tooltipText, showDelay: 300 }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"tooltipText\"\n @click=\"toggleSlider\"\n >\n <i :class=\"['pi', icon, 'text-lg text-base-foreground']\" />\n </Button>\n <div\n v-show=\"showSlider\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface p-4 shadow-lg w-[150px]\"\n >\n <Slider\n v-model=\"value\"\n class=\"w-full\"\n :min=\"min\"\n :max=\"max\"\n :step=\"step\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst {\n icon = 'pi-expand',\n min = 10,\n max = 150,\n step = 1\n} = defineProps<{\n icon?: string\n tooltipText: string\n min?: number\n max?: number\n step?: number\n}>()\n\nconst value = defineModel<number>()\nconst showSlider = ref(false)\n\nconst toggleSlider = () => {\n showSlider.value = !showSlider.value\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-slider')) {\n showSlider.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.switchCamera'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.switchCamera')\"\n @click=\"switchCamera\"\n >\n <i :class=\"['pi', 'pi-camera', 'text-lg text-base-foreground']\" />\n </Button>\n <PopupSlider\n v-if=\"showFOVButton\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { CameraType } from '@/extensions/core/load3d/interfaces'\n\nconst cameraType = defineModel<CameraType>('cameraType')\nconst fov = defineModel<number>('fov')\nconst showFOVButton = computed(() => cameraType.value === 'perspective')\n\nconst switchCamera = () => {\n cameraType.value =\n cameraType.value === 'perspective' ? 'orthographic' : 'perspective'\n}\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.switchCamera'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.switchCamera')\"\n @click=\"switchCamera\"\n >\n <i :class=\"['pi', 'pi-camera', 'text-lg text-base-foreground']\" />\n </Button>\n <PopupSlider\n v-if=\"showFOVButton\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { CameraType } from '@/extensions/core/load3d/interfaces'\n\nconst cameraType = defineModel<CameraType>('cameraType')\nconst fov = defineModel<number>('fov')\nconst showFOVButton = computed(() => cameraType.value === 'perspective')\n\nconst switchCamera = () => {\n cameraType.value =\n cameraType.value === 'perspective' ? 'orthographic' : 'perspective'\n}\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-export-formats relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.exportModel'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportModel')\"\n @click=\"toggleExportFormats\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showExportFormats\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"format in exportFormats\"\n :key=\"format.value\"\n variant=\"textonly\"\n class=\"text-base-foreground\"\n @click=\"exportModel(format.value)\"\n >\n {{ format.label }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst emit = defineEmits<{\n (e: 'exportModel', format: string): void\n}>()\n\nconst showExportFormats = ref(false)\n\nconst exportFormats = [\n { label: 'GLB', value: 'glb' },\n { label: 'OBJ', value: 'obj' },\n { label: 'STL', value: 'stl' }\n]\n\nfunction toggleExportFormats() {\n showExportFormats.value = !showExportFormats.value\n}\n\nfunction exportModel(format: string) {\n emit('exportModel', format)\n\n showExportFormats.value = false\n}\n\nfunction closeExportFormatsList(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-export-formats')) {\n showExportFormats.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeExportFormatsList)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeExportFormatsList)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-export-formats relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.exportModel'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportModel')\"\n @click=\"toggleExportFormats\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showExportFormats\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"format in exportFormats\"\n :key=\"format.value\"\n variant=\"textonly\"\n class=\"text-base-foreground\"\n @click=\"exportModel(format.value)\"\n >\n {{ format.label }}\n </Button>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\n\nconst emit = defineEmits<{\n (e: 'exportModel', format: string): void\n}>()\n\nconst showExportFormats = ref(false)\n\nconst exportFormats = [\n { label: 'GLB', value: 'glb' },\n { label: 'OBJ', value: 'obj' },\n { label: 'STL', value: 'stl' }\n]\n\nfunction toggleExportFormats() {\n showExportFormats.value = !showExportFormats.value\n}\n\nfunction exportModel(format: string) {\n emit('exportModel', format)\n\n showExportFormats.value = false\n}\n\nfunction closeExportFormatsList(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-export-formats')) {\n showExportFormats.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeExportFormatsList)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeExportFormatsList)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div v-if=\"showLightIntensityButton\" class=\"show-light-intensity relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.lightIntensity'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.lightIntensity')\"\n @click=\"toggleLightIntensity\"\n >\n <i class=\"pi pi-sun text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showLightIntensity\"\n class=\"absolute top-0 left-12 rounded-lg bg-black/50 p-4 shadow-lg\"\n style=\"width: 150px\"\n >\n <Slider\n v-model=\"lightIntensity\"\n class=\"w-full\"\n :min=\"lightIntensityMinimum\"\n :max=\"lightIntensityMaximum\"\n :step=\"lightAdjustmentIncrement\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type { MaterialMode } from '@/extensions/core/load3d/interfaces'\nimport { useSettingStore } from '@/platform/settings/settingStore'\n\nconst lightIntensity = defineModel<number>('lightIntensity')\nconst materialMode = defineModel<MaterialMode>('materialMode')\n\nconst showLightIntensityButton = computed(\n () => materialMode.value === 'original'\n)\nconst showLightIntensity = ref(false)\n\nconst lightIntensityMaximum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMaximum'\n)\nconst lightIntensityMinimum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMinimum'\n)\nconst lightAdjustmentIncrement = useSettingStore().get(\n 'Comfy.Load3D.LightAdjustmentIncrement'\n)\n\nfunction toggleLightIntensity() {\n showLightIntensity.value = !showLightIntensity.value\n}\n\nfunction closeLightSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-light-intensity')) {\n showLightIntensity.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeLightSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeLightSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div v-if=\"showLightIntensityButton\" class=\"show-light-intensity relative\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.lightIntensity'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.lightIntensity')\"\n @click=\"toggleLightIntensity\"\n >\n <i class=\"pi pi-sun text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showLightIntensity\"\n class=\"absolute top-0 left-12 rounded-lg bg-black/50 p-4 shadow-lg\"\n style=\"width: 150px\"\n >\n <Slider\n v-model=\"lightIntensity\"\n class=\"w-full\"\n :min=\"lightIntensityMinimum\"\n :max=\"lightIntensityMaximum\"\n :step=\"lightAdjustmentIncrement\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Slider from 'primevue/slider'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type { MaterialMode } from '@/extensions/core/load3d/interfaces'\nimport { useSettingStore } from '@/platform/settings/settingStore'\n\nconst lightIntensity = defineModel<number>('lightIntensity')\nconst materialMode = defineModel<MaterialMode>('materialMode')\n\nconst showLightIntensityButton = computed(\n () => materialMode.value === 'original'\n)\nconst showLightIntensity = ref(false)\n\nconst lightIntensityMaximum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMaximum'\n)\nconst lightIntensityMinimum = useSettingStore().get(\n 'Comfy.Load3D.LightIntensityMinimum'\n)\nconst lightAdjustmentIncrement = useSettingStore().get(\n 'Comfy.Load3D.LightAdjustmentIncrement'\n)\n\nfunction toggleLightIntensity() {\n showLightIntensity.value = !showLightIntensity.value\n}\n\nfunction closeLightSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-light-intensity')) {\n showLightIntensity.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeLightSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeLightSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-up-direction relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.upDirection'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.upDirection')\"\n @click=\"toggleUpDirection\"\n >\n <i class=\"pi pi-arrow-up text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showUpDirection\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"direction in upDirections\"\n :key=\"direction\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'text-base-foreground',\n upDirection === direction && 'bg-blue-500'\n )\n \"\n @click=\"selectUpDirection(direction)\"\n >\n {{ direction.toUpperCase() }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"!hideMaterialMode\" class=\"show-material-mode relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.materialMode'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.materialMode')\"\n @click=\"toggleMaterialMode\"\n >\n <i class=\"pi pi-box text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showMaterialMode\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"mode in materialModes\"\n :key=\"mode\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'whitespace-nowrap text-base-foreground',\n materialMode === mode && 'bg-blue-500'\n )\n \"\n @click=\"selectMaterialMode(mode)\"\n >\n {{ formatMaterialMode(mode) }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"hasSkeleton\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.showSkeleton'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"cn('rounded-full', showSkeleton && 'bg-blue-500')\"\n :aria-label=\"t('load3d.showSkeleton')\"\n @click=\"showSkeleton = !showSkeleton\"\n >\n <i class=\"pi pi-sitemap text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n MaterialMode,\n UpDirection\n} from '@/extensions/core/load3d/interfaces'\nimport { t } from '@/i18n'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n hideMaterialMode = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n hideMaterialMode?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst materialMode = defineModel<MaterialMode>('materialMode')\nconst upDirection = defineModel<UpDirection>('upDirection')\nconst showSkeleton = defineModel<boolean>('showSkeleton')\n\nconst showUpDirection = ref(false)\nconst showMaterialMode = ref(false)\n\nconst upDirections: UpDirection[] = [\n 'original',\n '-x',\n '+x',\n '-y',\n '+y',\n '-z',\n '+z'\n]\n\nconst materialModes = computed(() => {\n const modes: MaterialMode[] = [\n 'original',\n 'normal',\n 'wireframe'\n //'depth' disable for now\n ]\n\n // Only show pointCloud mode for PLY files (point cloud rendering)\n if (isPlyModel) {\n modes.splice(1, 0, 'pointCloud')\n }\n\n return modes\n})\n\nfunction toggleUpDirection() {\n showUpDirection.value = !showUpDirection.value\n showMaterialMode.value = false\n}\n\nfunction selectUpDirection(direction: UpDirection) {\n upDirection.value = direction\n showUpDirection.value = false\n}\n\nfunction toggleMaterialMode() {\n showMaterialMode.value = !showMaterialMode.value\n showUpDirection.value = false\n}\n\nfunction selectMaterialMode(mode: MaterialMode) {\n materialMode.value = mode\n showMaterialMode.value = false\n}\n\nfunction formatMaterialMode(mode: MaterialMode) {\n return t(`load3d.materialModes.${mode}`)\n}\n\nfunction closeSceneSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-up-direction')) {\n showUpDirection.value = false\n }\n\n if (!target.closest('.show-material-mode')) {\n showMaterialMode.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSceneSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSceneSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <div class=\"show-up-direction relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.upDirection'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.upDirection')\"\n @click=\"toggleUpDirection\"\n >\n <i class=\"pi pi-arrow-up text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showUpDirection\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"direction in upDirections\"\n :key=\"direction\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'text-base-foreground',\n upDirection === direction && 'bg-blue-500'\n )\n \"\n @click=\"selectUpDirection(direction)\"\n >\n {{ direction.toUpperCase() }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"!hideMaterialMode\" class=\"show-material-mode relative\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.materialMode'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.materialMode')\"\n @click=\"toggleMaterialMode\"\n >\n <i class=\"pi pi-box text-lg text-base-foreground\" />\n </Button>\n <div\n v-show=\"showMaterialMode\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"mode in materialModes\"\n :key=\"mode\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'whitespace-nowrap text-base-foreground',\n materialMode === mode && 'bg-blue-500'\n )\n \"\n @click=\"selectMaterialMode(mode)\"\n >\n {{ formatMaterialMode(mode) }}\n </Button>\n </div>\n </div>\n </div>\n\n <div v-if=\"hasSkeleton\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.showSkeleton'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"cn('rounded-full', showSkeleton && 'bg-blue-500')\"\n :aria-label=\"t('load3d.showSkeleton')\"\n @click=\"showSkeleton = !showSkeleton\"\n >\n <i class=\"pi pi-sitemap text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n MaterialMode,\n UpDirection\n} from '@/extensions/core/load3d/interfaces'\nimport { t } from '@/i18n'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n hideMaterialMode = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n hideMaterialMode?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst materialMode = defineModel<MaterialMode>('materialMode')\nconst upDirection = defineModel<UpDirection>('upDirection')\nconst showSkeleton = defineModel<boolean>('showSkeleton')\n\nconst showUpDirection = ref(false)\nconst showMaterialMode = ref(false)\n\nconst upDirections: UpDirection[] = [\n 'original',\n '-x',\n '+x',\n '-y',\n '+y',\n '-z',\n '+z'\n]\n\nconst materialModes = computed(() => {\n const modes: MaterialMode[] = [\n 'original',\n 'normal',\n 'wireframe'\n //'depth' disable for now\n ]\n\n // Only show pointCloud mode for PLY files (point cloud rendering)\n if (isPlyModel) {\n modes.splice(1, 0, 'pointCloud')\n }\n\n return modes\n})\n\nfunction toggleUpDirection() {\n showUpDirection.value = !showUpDirection.value\n showMaterialMode.value = false\n}\n\nfunction selectUpDirection(direction: UpDirection) {\n upDirection.value = direction\n showUpDirection.value = false\n}\n\nfunction toggleMaterialMode() {\n showMaterialMode.value = !showMaterialMode.value\n showUpDirection.value = false\n}\n\nfunction selectMaterialMode(mode: MaterialMode) {\n materialMode.value = mode\n showMaterialMode.value = false\n}\n\nfunction formatMaterialMode(mode: MaterialMode) {\n return t(`load3d.materialModes.${mode}`)\n}\n\nfunction closeSceneSlider(e: MouseEvent) {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-up-direction')) {\n showUpDirection.value = false\n }\n\n if (!target.closest('.show-material-mode')) {\n showMaterialMode.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSceneSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSceneSlider)\n})\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{ value: $t('load3d.showGrid'), showDelay: 300 }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"cn('rounded-full', showGrid && 'ring-2 ring-white/50')\"\n :aria-label=\"$t('load3d.showGrid')\"\n @click=\"toggleGrid\"\n >\n <i class=\"pi pi-table text-lg text-base-foreground\" />\n </Button>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.backgroundColor'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.backgroundColor')\"\n @click=\"openColorPicker\"\n >\n <i class=\"pi pi-palette text-lg text-base-foreground\" />\n <input\n ref=\"colorPickerRef\"\n type=\"color\"\n :value=\"backgroundColor\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @input=\"\n updateBackgroundColor(($event.target as HTMLInputElement).value)\n \"\n />\n </Button>\n </div>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.uploadBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.uploadBackgroundImage')\"\n @click=\"openImagePicker\"\n >\n <i class=\"pi pi-image text-lg text-base-foreground\" />\n <input\n ref=\"imagePickerRef\"\n type=\"file\"\n accept=\"image/*\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @change=\"uploadBackgroundImage\"\n />\n </Button>\n </div>\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.panoramaMode'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"\n cn(\n 'rounded-full',\n backgroundRenderMode === 'panorama' && 'ring-2 ring-white/50'\n )\n \"\n :aria-label=\"$t('load3d.panoramaMode')\"\n @click=\"toggleBackgroundRenderMode\"\n >\n <i class=\"pi pi-globe text-lg text-base-foreground\" />\n </Button>\n </div>\n\n <PopupSlider\n v-if=\"hasBackgroundImage && backgroundRenderMode === 'panorama'\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.removeBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.removeBackgroundImage')\"\n @click=\"removeBackgroundImage\"\n >\n <i class=\"pi pi-times text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { BackgroundRenderModeType } from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n}>()\n\nconst showGrid = defineModel<boolean>('showGrid')\nconst backgroundColor = defineModel<string>('backgroundColor')\nconst backgroundImage = defineModel<string>('backgroundImage')\nconst backgroundRenderMode = defineModel<BackgroundRenderModeType>(\n 'backgroundRenderMode',\n { default: 'tiled' }\n)\nconst fov = defineModel<number>('fov')\nconst hasBackgroundImage = computed(\n () => backgroundImage.value && backgroundImage.value !== ''\n)\n\nconst colorPickerRef = ref<HTMLInputElement | null>(null)\nconst imagePickerRef = ref<HTMLInputElement | null>(null)\n\nconst toggleGrid = () => {\n showGrid.value = !showGrid.value\n}\n\nconst updateBackgroundColor = (color: string) => {\n backgroundColor.value = color\n}\n\nconst openColorPicker = () => {\n colorPickerRef.value?.click()\n}\n\nconst openImagePicker = () => {\n imagePickerRef.value?.click()\n}\n\nconst uploadBackgroundImage = (event: Event) => {\n const input = event.target as HTMLInputElement\n\n if (input.files && input.files[0]) {\n emit('updateBackgroundImage', input.files[0])\n }\n}\n\nconst removeBackgroundImage = () => {\n emit('updateBackgroundImage', null)\n}\n\nconst toggleBackgroundRenderMode = () => {\n backgroundRenderMode.value =\n backgroundRenderMode.value === 'panorama' ? 'tiled' : 'panorama'\n}\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <Button\n v-tooltip.right=\"{ value: $t('load3d.showGrid'), showDelay: 300 }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"cn('rounded-full', showGrid && 'ring-2 ring-white/50')\"\n :aria-label=\"$t('load3d.showGrid')\"\n @click=\"toggleGrid\"\n >\n <i class=\"pi pi-table text-lg text-base-foreground\" />\n </Button>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.backgroundColor'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.backgroundColor')\"\n @click=\"openColorPicker\"\n >\n <i class=\"pi pi-palette text-lg text-base-foreground\" />\n <input\n ref=\"colorPickerRef\"\n type=\"color\"\n :value=\"backgroundColor\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @input=\"\n updateBackgroundColor(($event.target as HTMLInputElement).value)\n \"\n />\n </Button>\n </div>\n\n <div v-if=\"!hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.uploadBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.uploadBackgroundImage')\"\n @click=\"openImagePicker\"\n >\n <i class=\"pi pi-image text-lg text-base-foreground\" />\n <input\n ref=\"imagePickerRef\"\n type=\"file\"\n accept=\"image/*\"\n class=\"pointer-events-none absolute m-0 h-0 w-0 p-0 opacity-0\"\n @change=\"uploadBackgroundImage\"\n />\n </Button>\n </div>\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.panoramaMode'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n :class=\"\n cn(\n 'rounded-full',\n backgroundRenderMode === 'panorama' && 'ring-2 ring-white/50'\n )\n \"\n :aria-label=\"$t('load3d.panoramaMode')\"\n @click=\"toggleBackgroundRenderMode\"\n >\n <i class=\"pi pi-globe text-lg text-base-foreground\" />\n </Button>\n </div>\n\n <PopupSlider\n v-if=\"hasBackgroundImage && backgroundRenderMode === 'panorama'\"\n v-model=\"fov\"\n :tooltip-text=\"$t('load3d.fov')\"\n />\n\n <div v-if=\"hasBackgroundImage\">\n <Button\n v-tooltip.right=\"{\n value: $t('load3d.removeBackgroundImage'),\n showDelay: 300\n }\"\n variant=\"textonly\"\n size=\"icon\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.removeBackgroundImage')\"\n @click=\"removeBackgroundImage\"\n >\n <i class=\"pi pi-times text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport PopupSlider from '@/components/load3d/controls/PopupSlider.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type { BackgroundRenderModeType } from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n}>()\n\nconst showGrid = defineModel<boolean>('showGrid')\nconst backgroundColor = defineModel<string>('backgroundColor')\nconst backgroundImage = defineModel<string>('backgroundImage')\nconst backgroundRenderMode = defineModel<BackgroundRenderModeType>(\n 'backgroundRenderMode',\n { default: 'tiled' }\n)\nconst fov = defineModel<number>('fov')\nconst hasBackgroundImage = computed(\n () => backgroundImage.value && backgroundImage.value !== ''\n)\n\nconst colorPickerRef = ref<HTMLInputElement | null>(null)\nconst imagePickerRef = ref<HTMLInputElement | null>(null)\n\nconst toggleGrid = () => {\n showGrid.value = !showGrid.value\n}\n\nconst updateBackgroundColor = (color: string) => {\n backgroundColor.value = color\n}\n\nconst openColorPicker = () => {\n colorPickerRef.value?.click()\n}\n\nconst openImagePicker = () => {\n imagePickerRef.value?.click()\n}\n\nconst uploadBackgroundImage = (event: Event) => {\n const input = event.target as HTMLInputElement\n\n if (input.files && input.files[0]) {\n emit('updateBackgroundImage', input.files[0])\n }\n}\n\nconst removeBackgroundImage = () => {\n emit('updateBackgroundImage', null)\n}\n\nconst toggleBackgroundRenderMode = () => {\n backgroundRenderMode.value =\n backgroundRenderMode.value === 'panorama' ? 'tiled' : 'panorama'\n}\n</script>\n","<template>\n <div\n class=\"pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-backdrop/30\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @wheel.stop\n >\n <div class=\"show-menu relative\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('menu.showMenu')\"\n class=\"rounded-full\"\n @click=\"toggleMenu\"\n >\n <i class=\"pi pi-bars text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-show=\"isMenuOpen\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"category in availableCategories\"\n :key=\"category\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'flex w-full items-center justify-start',\n activeCategory === category && 'bg-button-active-surface'\n )\n \"\n @click=\"selectCategory(category)\"\n >\n <i :class=\"getCategoryIcon(category)\" />\n <span class=\"whitespace-nowrap text-base-foreground\">{{\n $t(categoryLabels[category])\n }}</span>\n </Button>\n </div>\n </div>\n </div>\n\n <div v-show=\"activeCategory\" class=\"rounded-lg bg-smoke-700/30\">\n <SceneControls\n v-if=\"showSceneControls\"\n v-model:show-grid=\"sceneConfig!.showGrid\"\n v-model:background-color=\"sceneConfig!.backgroundColor\"\n v-model:background-image=\"sceneConfig!.backgroundImage\"\n v-model:background-render-mode=\"sceneConfig!.backgroundRenderMode\"\n v-model:fov=\"cameraConfig!.fov\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n />\n\n <ModelControls\n v-if=\"showModelControls\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n v-model:up-direction=\"modelConfig!.upDirection\"\n v-model:show-skeleton=\"modelConfig!.showSkeleton\"\n :hide-material-mode=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n />\n\n <CameraControls\n v-if=\"showCameraControls\"\n v-model:camera-type=\"cameraConfig!.cameraType\"\n v-model:fov=\"cameraConfig!.fov\"\n />\n\n <LightControls\n v-if=\"showLightControls\"\n v-model:light-intensity=\"lightConfig!.intensity\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n />\n\n <ExportControls\n v-if=\"showExportControls\"\n @export-model=\"handleExportModel\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport CameraControls from '@/components/load3d/controls/CameraControls.vue'\nimport ExportControls from '@/components/load3d/controls/ExportControls.vue'\nimport LightControls from '@/components/load3d/controls/LightControls.vue'\nimport ModelControls from '@/components/load3d/controls/ModelControls.vue'\nimport SceneControls from '@/components/load3d/controls/SceneControls.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n CameraConfig,\n LightConfig,\n ModelConfig,\n SceneConfig\n} from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n isSplatModel = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n isSplatModel?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst sceneConfig = defineModel<SceneConfig>('sceneConfig')\nconst modelConfig = defineModel<ModelConfig>('modelConfig')\nconst cameraConfig = defineModel<CameraConfig>('cameraConfig')\nconst lightConfig = defineModel<LightConfig>('lightConfig')\n\nconst isMenuOpen = ref(false)\nconst activeCategory = ref<string>('scene')\nconst categoryLabels: Record<string, string> = {\n scene: 'load3d.scene',\n model: 'load3d.model',\n camera: 'load3d.camera',\n light: 'load3d.light',\n export: 'load3d.export'\n}\n\nconst availableCategories = computed(() => {\n if (isSplatModel) {\n return ['scene', 'model', 'camera']\n }\n\n return ['scene', 'model', 'camera', 'light', 'export']\n})\n\nconst showSceneControls = computed(\n () => activeCategory.value === 'scene' && !!sceneConfig.value\n)\nconst showModelControls = computed(\n () => activeCategory.value === 'model' && !!modelConfig.value\n)\nconst showCameraControls = computed(\n () => activeCategory.value === 'camera' && !!cameraConfig.value\n)\nconst showLightControls = computed(\n () =>\n activeCategory.value === 'light' &&\n !!lightConfig.value &&\n !!modelConfig.value\n)\nconst showExportControls = computed(() => activeCategory.value === 'export')\n\nconst toggleMenu = () => {\n isMenuOpen.value = !isMenuOpen.value\n}\n\nconst selectCategory = (category: string) => {\n activeCategory.value = category\n isMenuOpen.value = false\n}\n\nconst getCategoryIcon = (category: string) => {\n const icons = {\n scene: 'pi pi-image',\n model: 'pi pi-box',\n camera: 'pi pi-camera',\n light: 'pi pi-sun',\n export: 'pi pi-download'\n }\n // @ts-expect-error fixme ts strict error\n return `${icons[category]} text-base-foreground text-lg`\n}\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n (e: 'exportModel', format: string): void\n}>()\n\nconst handleBackgroundImageUpdate = (file: File | null) => {\n emit('updateBackgroundImage', file)\n}\n\nconst handleExportModel = (format: string) => {\n emit('exportModel', format)\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-menu')) {\n isMenuOpen.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <div\n class=\"pointer-events-auto absolute top-12 left-2 z-20 flex flex-col rounded-lg bg-backdrop/30\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @wheel.stop\n >\n <div class=\"show-menu relative\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('menu.showMenu')\"\n class=\"rounded-full\"\n @click=\"toggleMenu\"\n >\n <i class=\"pi pi-bars text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-show=\"isMenuOpen\"\n class=\"absolute top-0 left-12 rounded-lg bg-interface-menu-surface shadow-lg\"\n >\n <div class=\"flex flex-col\">\n <Button\n v-for=\"category in availableCategories\"\n :key=\"category\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'flex w-full items-center justify-start',\n activeCategory === category && 'bg-button-active-surface'\n )\n \"\n @click=\"selectCategory(category)\"\n >\n <i :class=\"getCategoryIcon(category)\" />\n <span class=\"whitespace-nowrap text-base-foreground\">{{\n $t(categoryLabels[category])\n }}</span>\n </Button>\n </div>\n </div>\n </div>\n\n <div v-show=\"activeCategory\" class=\"rounded-lg bg-smoke-700/30\">\n <SceneControls\n v-if=\"showSceneControls\"\n v-model:show-grid=\"sceneConfig!.showGrid\"\n v-model:background-color=\"sceneConfig!.backgroundColor\"\n v-model:background-image=\"sceneConfig!.backgroundImage\"\n v-model:background-render-mode=\"sceneConfig!.backgroundRenderMode\"\n v-model:fov=\"cameraConfig!.fov\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n />\n\n <ModelControls\n v-if=\"showModelControls\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n v-model:up-direction=\"modelConfig!.upDirection\"\n v-model:show-skeleton=\"modelConfig!.showSkeleton\"\n :hide-material-mode=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n />\n\n <CameraControls\n v-if=\"showCameraControls\"\n v-model:camera-type=\"cameraConfig!.cameraType\"\n v-model:fov=\"cameraConfig!.fov\"\n />\n\n <LightControls\n v-if=\"showLightControls\"\n v-model:light-intensity=\"lightConfig!.intensity\"\n v-model:material-mode=\"modelConfig!.materialMode\"\n />\n\n <ExportControls\n v-if=\"showExportControls\"\n @export-model=\"handleExportModel\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport CameraControls from '@/components/load3d/controls/CameraControls.vue'\nimport ExportControls from '@/components/load3d/controls/ExportControls.vue'\nimport LightControls from '@/components/load3d/controls/LightControls.vue'\nimport ModelControls from '@/components/load3d/controls/ModelControls.vue'\nimport SceneControls from '@/components/load3d/controls/SceneControls.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport type {\n CameraConfig,\n LightConfig,\n ModelConfig,\n SceneConfig\n} from '@/extensions/core/load3d/interfaces'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst {\n isSplatModel = false,\n isPlyModel = false,\n hasSkeleton = false\n} = defineProps<{\n isSplatModel?: boolean\n isPlyModel?: boolean\n hasSkeleton?: boolean\n}>()\n\nconst sceneConfig = defineModel<SceneConfig>('sceneConfig')\nconst modelConfig = defineModel<ModelConfig>('modelConfig')\nconst cameraConfig = defineModel<CameraConfig>('cameraConfig')\nconst lightConfig = defineModel<LightConfig>('lightConfig')\n\nconst isMenuOpen = ref(false)\nconst activeCategory = ref<string>('scene')\nconst categoryLabels: Record<string, string> = {\n scene: 'load3d.scene',\n model: 'load3d.model',\n camera: 'load3d.camera',\n light: 'load3d.light',\n export: 'load3d.export'\n}\n\nconst availableCategories = computed(() => {\n if (isSplatModel) {\n return ['scene', 'model', 'camera']\n }\n\n return ['scene', 'model', 'camera', 'light', 'export']\n})\n\nconst showSceneControls = computed(\n () => activeCategory.value === 'scene' && !!sceneConfig.value\n)\nconst showModelControls = computed(\n () => activeCategory.value === 'model' && !!modelConfig.value\n)\nconst showCameraControls = computed(\n () => activeCategory.value === 'camera' && !!cameraConfig.value\n)\nconst showLightControls = computed(\n () =>\n activeCategory.value === 'light' &&\n !!lightConfig.value &&\n !!modelConfig.value\n)\nconst showExportControls = computed(() => activeCategory.value === 'export')\n\nconst toggleMenu = () => {\n isMenuOpen.value = !isMenuOpen.value\n}\n\nconst selectCategory = (category: string) => {\n activeCategory.value = category\n isMenuOpen.value = false\n}\n\nconst getCategoryIcon = (category: string) => {\n const icons = {\n scene: 'pi pi-image',\n model: 'pi pi-box',\n camera: 'pi pi-camera',\n light: 'pi pi-sun',\n export: 'pi pi-download'\n }\n // @ts-expect-error fixme ts strict error\n return `${icons[category]} text-base-foreground text-lg`\n}\n\nconst emit = defineEmits<{\n (e: 'updateBackgroundImage', file: File | null): void\n (e: 'exportModel', format: string): void\n}>()\n\nconst handleBackgroundImageUpdate = (file: File | null) => {\n emit('updateBackgroundImage', file)\n}\n\nconst handleExportModel = (format: string) => {\n emit('exportModel', format)\n}\n\nconst closeSlider = (e: MouseEvent) => {\n const target = e.target as HTMLElement\n\n if (!target.closest('.show-menu')) {\n isMenuOpen.value = false\n }\n}\n\nonMounted(() => {\n document.addEventListener('click', closeSlider)\n})\n\nonUnmounted(() => {\n document.removeEventListener('click', closeSlider)\n})\n</script>\n","<template>\n <Transition name=\"fade\">\n <div\n v-if=\"loading\"\n class=\"absolute inset-0 z-50 flex items-center justify-center bg-backdrop/50\"\n >\n <div class=\"flex flex-col items-center\">\n <div class=\"spinner\" />\n <div class=\"mt-4 text-lg text-base-foreground\">\n {{ loadingMessage }}\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{\n loading: boolean\n loadingMessage: string\n}>()\n</script>\n\n<style scoped>\n.spinner {\n width: 50px;\n height: 50px;\n border: 4px solid #f3f3f3;\n border-top: 4px solid #3498db;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\n <Transition name=\"fade\">\n <div\n v-if=\"loading\"\n class=\"absolute inset-0 z-50 flex items-center justify-center bg-backdrop/50\"\n >\n <div class=\"flex flex-col items-center\">\n <div class=\"spinner\" />\n <div class=\"mt-4 text-lg text-base-foreground\">\n {{ loadingMessage }}\n </div>\n </div>\n </div>\n </Transition>\n</template>\n\n<script setup lang=\"ts\">\ndefineProps<{\n loading: boolean\n loadingMessage: string\n}>()\n</script>\n\n<style scoped>\n.spinner {\n width: 50px;\n height: 50px;\n border: 4px solid #f3f3f3;\n border-top: 4px solid #3498db;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n</style>\n","<template>\n <div\n ref=\"container\"\n class=\"relative h-full w-full min-h-[200px]\"\n data-capture-wheel=\"true\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @mousedown.stop\n @mousemove.stop\n @mouseup.stop\n @contextmenu.stop.prevent\n @dragover.prevent.stop=\"handleDragOver\"\n @dragleave.stop=\"handleDragLeave\"\n @drop.prevent.stop=\"handleDrop\"\n >\n <LoadingOverlay :loading=\"loading\" :loading-message=\"loadingMessage\" />\n <div\n v-if=\"!isPreview && isDragging\"\n class=\"pointer-events-none absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm\"\n >\n <div\n class=\"rounded-lg border-2 border-dashed border-blue-400 bg-blue-500/20 px-6 py-4 text-lg font-medium text-blue-100\"\n >\n {{ dragMessage }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport LoadingOverlay from '@/components/load3d/LoadingOverlay.vue'\nimport { useLoad3dDrag } from '@/composables/useLoad3dDrag'\n\nconst props = defineProps<{\n initializeLoad3d: (containerRef: HTMLElement) => Promise<void>\n cleanup: () => void\n loading: boolean\n loadingMessage: string\n onModelDrop?: (file: File) => void | Promise<void>\n isPreview: boolean\n}>()\n\nconst container = ref<HTMLElement | null>(null)\n\nconst { isDragging, dragMessage, handleDragOver, handleDragLeave, handleDrop } =\n useLoad3dDrag({\n onModelDrop: async (file) => {\n if (props.onModelDrop) {\n await props.onModelDrop(file)\n }\n },\n disabled: computed(() => props.isPreview)\n })\n\nonMounted(() => {\n if (container.value) {\n void props.initializeLoad3d(container.value)\n }\n})\n\nonUnmounted(() => {\n props.cleanup()\n})\n</script>\n","<template>\n <div\n ref=\"container\"\n class=\"relative h-full w-full min-h-[200px]\"\n data-capture-wheel=\"true\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n @mousedown.stop\n @mousemove.stop\n @mouseup.stop\n @contextmenu.stop.prevent\n @dragover.prevent.stop=\"handleDragOver\"\n @dragleave.stop=\"handleDragLeave\"\n @drop.prevent.stop=\"handleDrop\"\n >\n <LoadingOverlay :loading=\"loading\" :loading-message=\"loadingMessage\" />\n <div\n v-if=\"!isPreview && isDragging\"\n class=\"pointer-events-none absolute inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm\"\n >\n <div\n class=\"rounded-lg border-2 border-dashed border-blue-400 bg-blue-500/20 px-6 py-4 text-lg font-medium text-blue-100\"\n >\n {{ dragMessage }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport LoadingOverlay from '@/components/load3d/LoadingOverlay.vue'\nimport { useLoad3dDrag } from '@/composables/useLoad3dDrag'\n\nconst props = defineProps<{\n initializeLoad3d: (containerRef: HTMLElement) => Promise<void>\n cleanup: () => void\n loading: boolean\n loadingMessage: string\n onModelDrop?: (file: File) => void | Promise<void>\n isPreview: boolean\n}>()\n\nconst container = ref<HTMLElement | null>(null)\n\nconst { isDragging, dragMessage, handleDragOver, handleDragLeave, handleDrop } =\n useLoad3dDrag({\n onModelDrop: async (file) => {\n if (props.onModelDrop) {\n await props.onModelDrop(file)\n }\n },\n disabled: computed(() => props.isPreview)\n })\n\nonMounted(() => {\n if (container.value) {\n void props.initializeLoad3d(container.value)\n }\n})\n\nonUnmounted(() => {\n props.cleanup()\n})\n</script>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: isRecording\n ? $t('load3d.stopRecording')\n : $t('load3d.startRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'rounded-full',\n isRecording && 'text-red-500 recording-button-blink'\n )\n \"\n :aria-label=\"\n isRecording ? $t('load3d.stopRecording') : $t('load3d.startRecording')\n \"\n @click=\"toggleRecording\"\n >\n <i\n :class=\"[\n 'pi',\n isRecording ? 'pi-circle-fill' : 'pi-video',\n 'text-lg text-base-foreground'\n ]\"\n />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.exportRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportRecording')\"\n @click=\"handleExportRecording\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.clearRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.clearRecording')\"\n @click=\"handleClearRecording\"\n >\n <i class=\"pi pi-trash text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-if=\"recordingDuration && recordingDuration > 0 && !isRecording\"\n class=\"mt-1 text-center text-xs text-base-foreground\"\n >\n {{ formatDuration(recordingDuration) }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Button from '@/components/ui/button/Button.vue'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst hasRecording = defineModel<boolean>('hasRecording')\nconst isRecording = defineModel<boolean>('isRecording')\nconst recordingDuration = defineModel<number>('recordingDuration')\n\nconst emit = defineEmits<{\n (e: 'startRecording'): void\n (e: 'stopRecording'): void\n (e: 'exportRecording'): void\n (e: 'clearRecording'): void\n}>()\n\nfunction toggleRecording() {\n if (isRecording.value) {\n emit('stopRecording')\n } else {\n emit('startRecording')\n }\n}\n\nfunction handleExportRecording() {\n emit('exportRecording')\n}\n\nfunction handleClearRecording() {\n emit('clearRecording')\n}\n\nfunction formatDuration(seconds: number): string {\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.floor(seconds % 60)\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`\n}\n</script>\n\n<style scoped>\n.recording-button-blink {\n animation: blink 1s infinite;\n}\n\n@keyframes blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n</style>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: isRecording\n ? $t('load3d.stopRecording')\n : $t('load3d.startRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n :class=\"\n cn(\n 'rounded-full',\n isRecording && 'text-red-500 recording-button-blink'\n )\n \"\n :aria-label=\"\n isRecording ? $t('load3d.stopRecording') : $t('load3d.startRecording')\n \"\n @click=\"toggleRecording\"\n >\n <i\n :class=\"[\n 'pi',\n isRecording ? 'pi-circle-fill' : 'pi-video',\n 'text-lg text-base-foreground'\n ]\"\n />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.exportRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.exportRecording')\"\n @click=\"handleExportRecording\"\n >\n <i class=\"pi pi-download text-lg text-base-foreground\" />\n </Button>\n\n <Button\n v-if=\"hasRecording && !isRecording\"\n v-tooltip.right=\"{\n value: $t('load3d.clearRecording'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"$t('load3d.clearRecording')\"\n @click=\"handleClearRecording\"\n >\n <i class=\"pi pi-trash text-lg text-base-foreground\" />\n </Button>\n\n <div\n v-if=\"recordingDuration && recordingDuration > 0 && !isRecording\"\n class=\"mt-1 text-center text-xs text-base-foreground\"\n >\n {{ formatDuration(recordingDuration) }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Button from '@/components/ui/button/Button.vue'\nimport { cn } from '@/utils/tailwindUtil'\n\nconst hasRecording = defineModel<boolean>('hasRecording')\nconst isRecording = defineModel<boolean>('isRecording')\nconst recordingDuration = defineModel<number>('recordingDuration')\n\nconst emit = defineEmits<{\n (e: 'startRecording'): void\n (e: 'stopRecording'): void\n (e: 'exportRecording'): void\n (e: 'clearRecording'): void\n}>()\n\nfunction toggleRecording() {\n if (isRecording.value) {\n emit('stopRecording')\n } else {\n emit('startRecording')\n }\n}\n\nfunction handleExportRecording() {\n emit('exportRecording')\n}\n\nfunction handleClearRecording() {\n emit('clearRecording')\n}\n\nfunction formatDuration(seconds: number): string {\n const minutes = Math.floor(seconds / 60)\n const remainingSeconds = Math.floor(seconds % 60)\n return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`\n}\n</script>\n\n<style scoped>\n.recording-button-blink {\n animation: blink 1s infinite;\n}\n\n@keyframes blink {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n</style>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.openIn3DViewer'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.openIn3DViewer')\"\n @click=\"openIn3DViewer\"\n >\n <i class=\"pi pi-expand text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { t } from '@/i18n'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useLoad3dService } from '@/services/load3dService'\nimport { useDialogStore } from '@/stores/dialogStore'\n\nconst { node } = defineProps<{\n node: LGraphNode\n}>()\n\nconst openIn3DViewer = () => {\n const props = { node: node }\n\n useDialogStore().showDialog({\n key: 'global-load3d-viewer',\n title: t('load3d.viewer.title'),\n component: Load3DViewerContent,\n props: props,\n dialogComponentProps: {\n style: 'width: 80vw; height: 80vh;',\n maximizable: true,\n onClose: async () => {\n await useLoad3dService().handleViewerClose(props.node)\n }\n }\n })\n}\n</script>\n\n<style scoped></style>\n","<template>\n <div class=\"relative rounded-lg bg-backdrop/30\">\n <div class=\"flex flex-col gap-2\">\n <Button\n v-tooltip.right=\"{\n value: t('load3d.openIn3DViewer'),\n showDelay: 300\n }\"\n size=\"icon\"\n variant=\"textonly\"\n class=\"rounded-full\"\n :aria-label=\"t('load3d.openIn3DViewer')\"\n @click=\"openIn3DViewer\"\n >\n <i class=\"pi pi-expand text-lg text-base-foreground\" />\n </Button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Load3DViewerContent from '@/components/load3d/Load3dViewerContent.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { t } from '@/i18n'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useLoad3dService } from '@/services/load3dService'\nimport { useDialogStore } from '@/stores/dialogStore'\n\nconst { node } = defineProps<{\n node: LGraphNode\n}>()\n\nconst openIn3DViewer = () => {\n const props = { node: node }\n\n useDialogStore().showDialog({\n key: 'global-load3d-viewer',\n title: t('load3d.viewer.title'),\n component: Load3DViewerContent,\n props: props,\n dialogComponentProps: {\n style: 'width: 80vw; height: 80vh;',\n maximizable: true,\n onClose: async () => {\n await useLoad3dService().handleViewerClose(props.node)\n }\n }\n })\n}\n</script>\n\n<style scoped></style>\n","<template>\n <div\n class=\"relative size-full\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n >\n <Load3DScene\n v-if=\"node\"\n :initialize-load3d=\"initializeLoad3d\"\n :cleanup=\"cleanup\"\n :loading=\"loading\"\n :loading-message=\"loadingMessage\"\n :on-model-drop=\"isPreview ? undefined : handleModelDrop\"\n :is-preview=\"isPreview\"\n />\n <div class=\"pointer-events-none absolute top-0 left-0 size-full\">\n <Load3DControls\n v-model:scene-config=\"sceneConfig\"\n v-model:model-config=\"modelConfig\"\n v-model:camera-config=\"cameraConfig\"\n v-model:light-config=\"lightConfig\"\n :is-splat-model=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n @export-model=\"handleExportModel\"\n />\n <AnimationControls\n v-if=\"animations && animations.length > 0\"\n v-model:animations=\"animations\"\n v-model:playing=\"playing\"\n v-model:selected-speed=\"selectedSpeed\"\n v-model:selected-animation=\"selectedAnimation\"\n v-model:animation-progress=\"animationProgress\"\n v-model:animation-duration=\"animationDuration\"\n @seek=\"handleSeek\"\n />\n </div>\n <div\n v-if=\"enable3DViewer && node\"\n class=\"pointer-events-auto absolute top-12 right-2 z-20\"\n >\n <ViewerControls :node=\"node as LGraphNode\" />\n </div>\n\n <div\n v-if=\"!isPreview\"\n class=\"pointer-events-auto absolute right-2 z-20\"\n :class=\"{\n 'top-12': !enable3DViewer,\n 'top-24': enable3DViewer\n }\"\n >\n <RecordingControls\n v-model:is-recording=\"isRecording\"\n v-model:has-recording=\"hasRecording\"\n v-model:recording-duration=\"recordingDuration\"\n @start-recording=\"handleStartRecording\"\n @stop-recording=\"handleStopRecording\"\n @export-recording=\"handleExportRecording\"\n @clear-recording=\"handleClearRecording\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport type { Ref } from 'vue'\n\nimport Load3DControls from '@/components/load3d/Load3DControls.vue'\nimport Load3DScene from '@/components/load3d/Load3DScene.vue'\nimport AnimationControls from '@/components/load3d/controls/AnimationControls.vue'\nimport RecordingControls from '@/components/load3d/controls/RecordingControls.vue'\nimport ViewerControls from '@/components/load3d/controls/ViewerControls.vue'\nimport { useLoad3d } from '@/composables/useLoad3d'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport { app } from '@/scripts/app'\nimport type { ComponentWidget } from '@/scripts/domWidget'\nimport type { SimplifiedWidget } from '@/types/simplifiedWidget'\n\nconst props = defineProps<{\n widget: ComponentWidget<string[]> | SimplifiedWidget\n nodeId?: NodeId\n}>()\n\nfunction isComponentWidget(\n widget: ComponentWidget<string[]> | SimplifiedWidget\n): widget is ComponentWidget<string[]> {\n return 'node' in widget && widget.node !== undefined\n}\n\nconst node = ref<LGraphNode | null>(null)\n\nif (isComponentWidget(props.widget)) {\n node.value = props.widget.node\n} else if (props.nodeId) {\n onMounted(() => {\n node.value = app.rootGraph?.getNodeById(props.nodeId!) || null\n })\n}\n\nconst {\n // configs\n sceneConfig,\n modelConfig,\n cameraConfig,\n lightConfig,\n\n // other state\n isRecording,\n isPreview,\n isSplatModel,\n isPlyModel,\n hasSkeleton,\n hasRecording,\n recordingDuration,\n animations,\n playing,\n selectedSpeed,\n selectedAnimation,\n animationProgress,\n animationDuration,\n loading,\n loadingMessage,\n\n // Methods\n initializeLoad3d,\n handleMouseEnter,\n handleMouseLeave,\n handleStartRecording,\n handleStopRecording,\n handleExportRecording,\n handleClearRecording,\n handleSeek,\n handleBackgroundImageUpdate,\n handleExportModel,\n handleModelDrop,\n cleanup\n} = useLoad3d(node as Ref<LGraphNode | null>)\n\nconst enable3DViewer = computed(() =>\n useSettingStore().get('Comfy.Load3D.3DViewerEnable')\n)\n</script>\n","<template>\n <div\n class=\"relative size-full\"\n @mouseenter=\"handleMouseEnter\"\n @mouseleave=\"handleMouseLeave\"\n @pointerdown.stop\n @pointermove.stop\n @pointerup.stop\n >\n <Load3DScene\n v-if=\"node\"\n :initialize-load3d=\"initializeLoad3d\"\n :cleanup=\"cleanup\"\n :loading=\"loading\"\n :loading-message=\"loadingMessage\"\n :on-model-drop=\"isPreview ? undefined : handleModelDrop\"\n :is-preview=\"isPreview\"\n />\n <div class=\"pointer-events-none absolute top-0 left-0 size-full\">\n <Load3DControls\n v-model:scene-config=\"sceneConfig\"\n v-model:model-config=\"modelConfig\"\n v-model:camera-config=\"cameraConfig\"\n v-model:light-config=\"lightConfig\"\n :is-splat-model=\"isSplatModel\"\n :is-ply-model=\"isPlyModel\"\n :has-skeleton=\"hasSkeleton\"\n @update-background-image=\"handleBackgroundImageUpdate\"\n @export-model=\"handleExportModel\"\n />\n <AnimationControls\n v-if=\"animations && animations.length > 0\"\n v-model:animations=\"animations\"\n v-model:playing=\"playing\"\n v-model:selected-speed=\"selectedSpeed\"\n v-model:selected-animation=\"selectedAnimation\"\n v-model:animation-progress=\"animationProgress\"\n v-model:animation-duration=\"animationDuration\"\n @seek=\"handleSeek\"\n />\n </div>\n <div\n v-if=\"enable3DViewer && node\"\n class=\"pointer-events-auto absolute top-12 right-2 z-20\"\n >\n <ViewerControls :node=\"node as LGraphNode\" />\n </div>\n\n <div\n v-if=\"!isPreview\"\n class=\"pointer-events-auto absolute right-2 z-20\"\n :class=\"{\n 'top-12': !enable3DViewer,\n 'top-24': enable3DViewer\n }\"\n >\n <RecordingControls\n v-model:is-recording=\"isRecording\"\n v-model:has-recording=\"hasRecording\"\n v-model:recording-duration=\"recordingDuration\"\n @start-recording=\"handleStartRecording\"\n @stop-recording=\"handleStopRecording\"\n @export-recording=\"handleExportRecording\"\n @clear-recording=\"handleClearRecording\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onMounted, ref } from 'vue'\nimport type { Ref } from 'vue'\n\nimport Load3DControls from '@/components/load3d/Load3DControls.vue'\nimport Load3DScene from '@/components/load3d/Load3DScene.vue'\nimport AnimationControls from '@/components/load3d/controls/AnimationControls.vue'\nimport RecordingControls from '@/components/load3d/controls/RecordingControls.vue'\nimport ViewerControls from '@/components/load3d/controls/ViewerControls.vue'\nimport { useLoad3d } from '@/composables/useLoad3d'\nimport type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'\nimport { useSettingStore } from '@/platform/settings/settingStore'\nimport type { NodeId } from '@/platform/workflow/validation/schemas/workflowSchema'\nimport { app } from '@/scripts/app'\nimport type { ComponentWidget } from '@/scripts/domWidget'\nimport type { SimplifiedWidget } from '@/types/simplifiedWidget'\n\nconst props = defineProps<{\n widget: ComponentWidget<string[]> | SimplifiedWidget\n nodeId?: NodeId\n}>()\n\nfunction isComponentWidget(\n widget: ComponentWidget<string[]> | SimplifiedWidget\n): widget is ComponentWidget<string[]> {\n return 'node' in widget && widget.node !== undefined\n}\n\nconst node = ref<LGraphNode | null>(null)\n\nif (isComponentWidget(props.widget)) {\n node.value = props.widget.node\n} else if (props.nodeId) {\n onMounted(() => {\n node.value = app.rootGraph?.getNodeById(props.nodeId!) || null\n })\n}\n\nconst {\n // configs\n sceneConfig,\n modelConfig,\n cameraConfig,\n lightConfig,\n\n // other state\n isRecording,\n isPreview,\n isSplatModel,\n isPlyModel,\n hasSkeleton,\n hasRecording,\n recordingDuration,\n animations,\n playing,\n selectedSpeed,\n selectedAnimation,\n animationProgress,\n animationDuration,\n loading,\n loadingMessage,\n\n // Methods\n initializeLoad3d,\n handleMouseEnter,\n handleMouseLeave,\n handleStartRecording,\n handleStopRecording,\n handleExportRecording,\n handleClearRecording,\n handleSeek,\n handleBackgroundImageUpdate,\n handleExportModel,\n handleModelDrop,\n cleanup\n} = useLoad3d(node as Ref<LGraphNode | null>)\n\nconst enable3DViewer = computed(() =>\n useSettingStore().get('Comfy.Load3D.3DViewerEnable')\n)\n</script>\n"],"mappings":"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"}
|
comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js → PanelTemplate-BJda9e5J.js}
RENAMED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var M=Object.defineProperty;var r=(e,t)=>M(e,"name",{value:t,configurable:!0});import{f as I,q as N}from"./vendor-primevue-DcMRXJN3.js";import{Ba as C,Do as v,Jo as w,Po as D,Va as U,Xo as O,Ya as T,do as A,gi as j,mo as h,qa as L,za as d,zo as g}from"./vendor-other-DlQF6V2E.js";import{p as S}from"./vendor-vue-D9IUuwPJ.js";import{Bt as _,E as P,Lt as k,r as z,zt as B}from"./api-
|
|
1
|
+
var M=Object.defineProperty;var r=(e,t)=>M(e,"name",{value:t,configurable:!0});import{f as I,q as N}from"./vendor-primevue-DcMRXJN3.js";import{Ba as C,Do as v,Jo as w,Po as D,Va as U,Xo as O,Ya as T,do as A,gi as j,mo as h,qa as L,za as d,zo as g}from"./vendor-other-DlQF6V2E.js";import{p as S}from"./vendor-vue-D9IUuwPJ.js";import{Bt as _,E as P,Lt as k,r as z,zt as B}from"./api-Dwq2LQIW.js";function ne(){const e=d(()=>String(P.global.locale.value)),t=d(()=>e.value==="zh"||e.value==="zh-TW"),n=d(()=>_()?B().getPlatform()==="darwin"?"macos":"windows":null);return{buildDocsUrl:r((a,i={})=>{const{includeLocale:l=!1,platform:u=!1}=i;let c="https://docs.comfy.org";l&&t.value&&(c+="/zh-CN");const m=a.startsWith("/")?a:`/${a}`;return c+=m,u&&n.value&&(c=c.endsWith("/")?c:`${c}/`,c+=n.value),c},"buildDocsUrl"),staticUrls:{discord:"https://www.comfy.org/discord",github:"https://github.com/comfyanonymous/ComfyUI",githubIssues:"https://github.com/comfyanonymous/ComfyUI/issues",githubFrontend:"https://github.com/Comfy-Org/ComfyUI_frontend",githubElectron:"https://github.com/Comfy-Org/electron",forum:"https://forum.comfy.org/",comfyOrg:"https://www.comfy.org/"},docsPaths:{partnerNodesPricing:"/tutorials/partner-nodes/pricing"}}}r(ne,"useExternalLink");const re=S("systemStats",()=>{const e=r(async()=>{try{return await z.getSystemStats()}catch(u){throw console.error("Error fetching system stats:",u),u}},"fetchSystemStatsData"),{state:t,isLoading:n,error:s,isReady:a,execute:i}=j(e,null,{immediate:!0});function l(){if(k)return"cloud";if(!t.value?.system?.os)return"other";const u=t.value.system.os.toLowerCase();if(_()){if(u.includes("windows"))return"desktop-windows";if(u.includes("darwin")||u.includes("mac"))return"desktop-mac"}else{if(u.includes("windows"))return"git-windows";if(u.includes("darwin")||u.includes("mac"))return"git-mac";if(u.includes("linux"))return"git-linux"}return"other"}return r(l,"getFormFactor"),{systemStats:t,isLoading:n,error:s,isInitialized:a,refetchSystemStats:i,getFormFactor:l}});function se(e){return e.toLowerCase().endsWith(".json")||(e+=".json"),e}r(se,"appendJsonExt");function oe(e,t){if(!t)return e;const n=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),s=new RegExp(`(${n})`,"gi");return e.replace(s,'<span class="highlight">$1</span>')}r(oe,"highlightQuery");function ae(e,{precision:t=1,roundToInt:n=!1}={}){const s=["","k","m","b","t"],a=Math.abs(e);if(a<1e3)return n?Math.round(e).toString():e.toFixed(t);const i=Math.min(Math.floor(Math.log10(a)/3),s.length-1);return`${(e/Math.pow(1e3,i)).toFixed(t)}${s[i]}`}r(ae,"formatNumberWithSuffix");function ie(e){if(e==null)return"-";const t=e;if(t===0)return"0 B";const n=1024,s=["B","KB","MB","GB"],a=Math.floor(Math.log(t)/Math.log(n));return`${parseFloat((t/Math.pow(n,a)).toFixed(2))} ${s[a]}`}r(ie,"formatSize");function ue(e){return/^[a-f0-9]{40}$/i.test(e)?e.slice(0,7):e}r(ue,"formatCommitHash");function F(e){return e.includes(".")?{filename:e.split(".").slice(0,-1).join("."),suffix:e.split(".").pop()??null}:{filename:e,suffix:null}}r(F,"getFilenameDetails");function le(e){const t=e.split("/").slice(0,-1).join("/"),n=e.split("/").pop()??e;return{directory:t,fullFilename:n,...F(n)}}r(le,"getPathDetails");function ce(e){return typeof e=="string"?e.replace(/\./g,"_"):""}r(ce,"normalizeI18nKey");function R(e){function t(l){return l.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,"")}r(t,"stripComments");let n=0,s="";e=t(e);const a=r(()=>"\\"+e[n++],"handleEscape");function i(){const l=[];let u="",c=0;for(;n<e.length;){const f=e[n++];if(f==="\\"){u+=a();continue}else if(f==="{")c++;else if(f==="}"){if(!c)break;c--}else if(f==="|"&&!c){l.push(u),u="";continue}u+=f}l.push(u);const m=l[Math.floor(Math.random()*l.length)];return R(m)}for(r(i,"parseChoiceBlock");n<e.length;){const l=e[n++];l==="\\"?s+=a():l==="{"?s+=i():s+=l}return s.replace(/\\([{}|])/g,"$1")}r(R,"processDynamicPrompt");function W(e){try{return new URL(e),!0}catch{return!1}}r(W,"isValidUrl");function fe(e){if(!e?.trim())return{filename:"",subfolder:""};const t=e.replace(/[\\/]+/g,"/").replace(/^\//,"").replace(/\/$/,""),n=t.lastIndexOf("/");return n===-1?{filename:t,subfolder:""}:{filename:t.slice(n+1),subfolder:t.slice(0,n)}}r(fe,"parseFilePath");var y={d:r(e=>e.getDate(),"d"),M:r(e=>e.getMonth()+1,"M"),h:r(e=>e.getHours(),"h"),m:r(e=>e.getMinutes(),"m"),s:r(e=>e.getSeconds(),"s")},X=Object.keys(y).map(e=>e+e+"?").join("|")+"|yyy?y?";function de(e,t){return e.replace(new RegExp(X,"g"),n=>n==="yy"?(t.getFullYear()+"").substring(2):n==="yyyy"?t.getFullYear().toString():n[0]in y?(y[n[0]](t)+"").padStart(n.length,"0"):n)}r(de,"formatDate");const me=r(e=>typeof e=="string"?e:typeof e=="object"&&e!==null?Object.keys(e).sort((t,n)=>t.localeCompare(n)).map(t=>`${t}:${e[t]}`).join("&"):String(e),"paramsToCacheKey"),pe=r(()=>typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)}),"generateUUID"),he=r(e=>{if(!W(e)||!e.includes("civitai.com"))return!1;const t=new URL(e).pathname;return/^\/api\/download\/models\/(\d+)$/.test(t)||/^\/api\/v1\/models\/(\d+)$/.test(t)||/^\/api\/v1\/models-versions\/(\d+)$/.test(t)},"isCivitaiModelUrl"),ge=r(e=>{try{const t=new URL(e).pathname;return`https://huggingface.co/${/^(.*?)(?:\/resolve\/|\/blob\/|$)/.exec(t)?.[1]?.replace(/^\//,"")||""}`}catch{return e}},"downloadUrlToHfRepoUrl");function ye(e,t){return t==="usd"?(e/100).toFixed(2):e.toString()}r(ye,"formatMetronomeCurrency");function xe(e){return Math.round(e*1e6)}r(xe,"usdToMicros");function ve(e){return e?e.replace(/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%?=~_|])|(\bwww\.[-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%?=~_|])/gi,(t,n,s,a)=>{const i=n||a;return`<a href="${a?`http://${i}`:i}" target="_blank" rel="noopener noreferrer" class="text-primary-400 hover:underline">${i}</a>`}):""}r(ve,"linkifyHtml");function we(e){return e?e.replace(/\n/g,"<br />"):""}r(we,"nl2br");function be(e){return`v${e.replace(/\./g,"-")}`}r(be,"formatVersionAnchor");function Ee(e){return["en","es","fr","ja","ko","ru","zh"].includes(e)?e:"en"}r(Ee,"stringToLocale");function Se(e){if(!e||e<0)return"0s";const t=Math.floor(e/1e3),n=Math.floor(t/3600),s=Math.floor(t%3600/60),a=Math.floor(t%60),i=[];return n>0&&i.push(`${n}h`),s>0&&i.push(`${s}m`),(a>0||i.length===0)&&i.push(`${a}s`),i.join(" ")}r(Se,"formatDuration");var V=["png","jpg","jpeg","gif","webp","bmp"],H=["mp4","webm","mov","avi"],Y=["mp3","wav","ogg","flac"],K=["obj","fbx","gltf","glb"];function _e(e,t=20){if(!e||e.length<=t)return e;const n=e.lastIndexOf("."),s=n>-1?e.substring(0,n):e,a=n>-1?e.substring(n):"";if(s.length<=t)return e;const i=Math.floor((t-3)/2);return`${s.substring(0,i)}...${s.substring(s.length-i)}${a}`}r(_e,"truncateFilename");function $e(e){if(!e)return"image";const t=e.split(".").pop()?.toLowerCase();return!t||V.includes(t)?"image":H.includes(t)?"video":Y.includes(t)?"audio":K.includes(t)?"3D":"image"}r($e,"getMediaTypeFromFilename");var b=[],E=["pysssss.Locking","pysssss.SnapToGrid","pysssss.FaviconStatus","KJNodes.browserstatus"];const Me=S("extension",()=>{const e=g({}),t=d(()=>Object.values(e.value)),n=g(new Set),s=d(()=>Array.from(n.value).filter(o=>!(o in e.value))),a=r(o=>o in e.value,"isExtensionInstalled"),i=r(o=>!n.value.has(o),"isExtensionEnabled"),l=d(()=>t.value.filter(o=>i(o.name)));function u(o){return E.includes(o)||b.includes(o)}r(u,"isExtensionReadOnly");function c(o){if(!o.name)throw new Error("Extensions must have a 'name' property.");if(e.value[o.name])throw new Error(`Extension named '${o.name}' already registered.`);n.value.has(o.name)&&console.warn(`Extension ${o.name} is disabled.`),e.value[o.name]=D(o)}r(c,"registerExtension");function m(o){n.value=new Set(o);for(const p of E)n.value.add(p);for(const p of b)n.value.delete(p)}r(m,"loadDisabledExtensionNames");const f=g([]);function $(){f.value=t.value.map(o=>o.name)}r($,"captureCoreExtensions");function x(o){return f.value.includes(o)}return r(x,"isCoreExtension"),{extensions:t,enabledExtensions:l,inactiveDisabledExtensionNames:s,isExtensionInstalled:a,isExtensionEnabled:i,isExtensionReadOnly:u,registerExtension:c,loadDisabledExtensionNames:m,captureCoreExtensions:$,isCoreExtension:x,hasThirdPartyExtensions:d(()=>t.value.some(o=>!x(o.name)))}});var Z={class:"flex h-full w-full flex-col gap-2"},G=T({__name:"PanelTemplate",props:{value:{},class:{}},setup(e){const t=e;return(n,s)=>(A(),U(w(I),{value:t.value,class:O(["h-full w-full",t.class])},{default:v(()=>[C("div",Z,[h(n.$slots,"header"),L(w(N),{class:"h-0 grow pr-2"},{default:v(()=>[h(n.$slots,"default")]),_:3}),h(n.$slots,"footer")])]),_:3},8,["value","class"]))}}),Ie=G;export{fe as C,xe as D,_e as E,re as O,me as S,Ee as T,he as _,ue as a,we as b,ye as c,be as d,pe as f,oe as g,le as h,ge as i,ne as k,ae as l,$e as m,Me as n,de as o,F as p,se as r,Se as s,Ie as t,ie as u,W as v,R as w,ce as x,ve as y};
|
|
2
2
|
|
|
3
|
-
//# sourceMappingURL=PanelTemplate-
|
|
3
|
+
//# sourceMappingURL=PanelTemplate-BJda9e5J.js.map
|