From 6831dd8d158a9a7e9a8ac06195fe219efd3fa572 Mon Sep 17 00:00:00 2001 From: "Enjeck C." Date: Tue, 9 Dec 2025 07:05:13 +0000 Subject: [PATCH] feat: lazy initialization for editor setup Signed-off-by: Enjeck C. --- lib/Controller/PageController.php | 1 - src/shared/components/ncEditor/NcEditor.vue | 49 +++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) 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) {