diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 28109a392c..e78d93a3d6 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -43,7 +43,6 @@ public function index(): TemplateResponse { Util::addStyle(Application::APP_ID, 'grid'); Util::addStyle(Application::APP_ID, 'modal'); Util::addStyle(Application::APP_ID, 'tiptap'); - Util::addStyle(Application::APP_ID, 'tables-style'); if (class_exists(LoadViewer::class)) { $this->eventDispatcher->dispatchTyped(new LoadViewer()); diff --git a/src/shared/components/ncEditor/NcEditor.vue b/src/shared/components/ncEditor/NcEditor.vue index b9c1aa7837..efd5907ed5 100644 --- a/src/shared/components/ncEditor/NcEditor.vue +++ b/src/shared/components/ncEditor/NcEditor.vue @@ -63,6 +63,9 @@ export default { textAppAvailable: !!window.OCA?.Text?.createEditor, editor: null, localValue: '', + observer: null, + initialized: false, + idleHandle: null, } }, @@ -96,16 +99,56 @@ export default { async mounted() { this.localValue = this.text - await this.setupEditor() - this.editor?.setContent(this.localValue, false) + // Lazy initialize the editor: + // 1) When the component becomes visible (IntersectionObserver) + // 2) Or when the browser is idle (requestIdleCallback fallback) + this.setupLazyInitialization() }, beforeDestroy() { - this?.editor?.destroy() + this?.observer?.disconnect?.() + if (this.idleHandle && typeof cancelIdleCallback === 'function') { + cancelIdleCallback(this.idleHandle) + } + this?.editor?.destroy?.() }, methods: { t, + setupLazyInitialization() { + if (this.initialized) return + + // Prefer initializing when the editor wrapper enters the viewport + if ('IntersectionObserver' in window) { + this.observer = new IntersectionObserver((entries) => { + for (const entry of entries) { + if (entry.isIntersecting && !this.initialized) { + this.initialized = true + this.setupEditor().then(() => { + this.editor?.setContent(this.localValue, false) + }) + this.observer?.disconnect?.() + break + } + } + }, { rootMargin: '200px' }) + this.$nextTick(() => { + const el = this.$el + if (el) this.observer.observe(el) + }) + } else { + // Fallback: schedule during idle time to avoid blocking + const idle = window.requestIdleCallback || ((cb) => setTimeout(() => cb({ timeRemaining: () => 0 }), 50)) + const cancel = window.cancelIdleCallback || clearTimeout + this.idleHandle = idle(() => { + if (this.initialized) return + this.initialized = true + this.setupEditor().then(() => { + this.editor?.setContent(this.localValue, false) + }) + }) + } + }, async setupEditor() { this?.editor?.destroy() if (this.textAppAvailable) {