Files
nocodb/packages/nc-gui/app.vue
2026-04-01 12:50:38 +00:00

167 lines
4.5 KiB
Vue

<script setup lang="ts">
import ErrorBoundary from './components/nc/ErrorBoundary.vue'
const router = useRouter()
useUpdateChecker()
const route = router.currentRoute
const { showOnboardingFlow } = useOnboardingFlow()
const { hideSharedBaseBtn } = storeToRefs(useConfigStore())
const disableBaseLayout = computed(
() => route.value.path.startsWith('/nc/view') || route.value.path.startsWith('/nc/form') || showOnboardingFlow.value,
)
const { isExperimentalFeatureModalOpen, initializeFeatures } = useBetaFeatureToggle()
initializeFeatures()
useAntDvTheme()
useTheme()
const { isRtl } = useRtl()
const antDirection = computed(() => (isRtl.value ? 'rtl' : 'ltr'))
const { commandPalette, cmdData, cmdPlaceholder, activeScope, loadTemporaryScope } = useCommandPalette()
const { cmdK, cmdL, cmdJ, setActiveCmdView } = useCommand()
useUserSync()
useRealtime()
applyNonSelectable()
const { chatwootInit } = useProvideChatwoot()
onMounted(() => {
window.addEventListener('chatwoot:ready', chatwootInit)
if (route.value.query.hideSharedBaseBtn === 'true') {
hideSharedBaseBtn.value = true
}
})
onBeforeUnmount(() => {
window.removeEventListener('chatwoot:ready', chatwootInit)
})
useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey
if (cmdOrCtrl) {
switch (e.key.toLowerCase()) {
case 'a':
// prevent Ctrl + A selection for non-editable nodes
if (!['input', 'textarea'].includes((e.target as any).nodeName.toLowerCase())) {
e.preventDefault()
}
break
case 'k':
e.preventDefault()
commandPalette.value?.open?.()
break
case 'l':
e.preventDefault()
break
case 'j':
e.preventDefault()
break
}
}
})
// TODO: Remove when https://github.com/vuejs/core/issues/5513 fixed
const key = ref(0)
const messages = [
`Uncaught NotFoundError: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`, // chromium based
`NotFoundError: The object can not be found here.`, // safari
"Cannot read properties of null (reading 'parentNode')",
]
if (typeof window !== 'undefined') {
// @ts-expect-error using arbitrary window key
if (!window.__ncvue) {
window.addEventListener('error', (event) => {
if (messages.includes(event.message)) {
event.preventDefault()
console.warn('Re-rendering layout because of https://github.com/vuejs/core/issues/5513')
key.value++
}
})
}
// @ts-expect-error using arbitrary window key
window.__ncvue = true
}
function onScope(scope: string) {
if (scope === 'root' && isEeUI) {
loadTemporaryScope({ scope: 'root', data: {} })
}
}
// ref: https://github.com/vuejs/vue-cli/issues/7431#issuecomment-1793385162
// Stop error resizeObserver
const debounce = (callback: (...args: any[]) => void, delay: number) => {
let tid: any
return function (...args: any[]) {
const ctx = self
tid && clearTimeout(tid)
tid = setTimeout(() => {
callback.apply(ctx, args)
}, delay)
}
}
const _ = (window as any).ResizeObserver
;(window as any).ResizeObserver = class ResizeObserver extends _ {
constructor(callback: (...args: any[]) => void) {
callback = debounce(callback, 20)
super(callback)
}
}
</script>
<template>
<a-config-provider :direction="antDirection">
<NuxtLayout :name="disableBaseLayout ? false : 'base'">
<ErrorBoundary>
<NuxtPage :key="key" :transition="false" />
</ErrorBoundary>
</NuxtLayout>
</a-config-provider>
<ErrorBoundary>
<div>
<!-- Page Loading Indicator -->
<NcNuxtLoadingIndicator />
<!-- Command Menu -->
<CmdK
ref="commandPalette"
v-model:open="cmdK"
:scope="activeScope.scope"
:data="cmdData"
:placeholder="cmdPlaceholder"
:load-temporary-scope="loadTemporaryScope"
:set-active-cmd-view="setActiveCmdView"
@scope="onScope"
/>
<!-- Recent Views. Cycles through recently visited Views -->
<CmdL v-model:open="cmdL" :set-active-cmd-view="setActiveCmdView" />
<!-- Documentation. Integrated NocoDB Docs directly inside the Product -->
<CmdJ v-model:open="cmdJ" :set-active-cmd-view="setActiveCmdView" />
<DashboardFeatureExperimentation v-model:value="isExperimentalFeatureModalOpen" />
</div>
</ErrorBoundary>
<LazyChatPanel />
<LazyChatFab />
</template>