iOS: smoother animation when toggling fullscreen editor
Hunter
Friday, December 19th, 2025 at 5:21 PM
53 width: 50%;
54 height: 100%;
55 position: relative;
56 + transition: opacity 0.1s ease-out; /* Added transition for smoother opacity hack */
57 }
58
59 .preview-pane {
610 // Initial render
611 updatePreview();
612
- // Track editor focus for mobile keyboard detection
613 + // Track editor focus and handle keyboard dismissal
614 editorView.contentDOM.addEventListener('focus', () => { isEditorFocused = true; });
- editorView.contentDOM.addEventListener('blur', () => { isEditorFocused = false; });
615 +
616 + editorView.contentDOM.addEventListener('blur', () => {
617 + isEditorFocused = false;
618 +
619 + // If we are on mobile and the keyboard mode is active,
620 + // exit immediately. Do not wait for visualViewport resize.
621 + if (isMobileDevice() && document.body.classList.contains('mobile-keyboard-open')) {
622 +
623 + // 1. Hide editor immediately to prevent the "stutter/jump" visual
624 + editorPane.style.opacity = '0';
625 +
626 + // 2. Remove the class to trigger layout engine (100% -> 50% height)
627 + document.body.classList.remove('mobile-keyboard-open');
628 +
629 + // 3. Wait for layout to settle, then restore scroll and opacity
630 + requestAnimationFrame(() => requestAnimationFrame(() => {
631 + if (editorView) {
632 + // Scroll cursor into view in the new 50% layout
633 + const pos = editorView.state.selection.main.head;
634 + const lineBlock = editorView.lineBlockAt(pos);
635 + const targetScroll = lineBlock.top - (editorView.dom.clientHeight / 2);
636 + editorView.scrollDOM.scrollTop = Math.max(0, targetScroll);
637 + }
638 + // Reveal the editor
639 + editorPane.style.opacity = '';
640 + }));
641 + }
642 + });
643
644 // Focus the editor
645 editorView.focus();
681 if (isKeyboardOpen && isEditorFocused) {
682 document.body.classList.add('mobile-keyboard-open');
683 } else if (!isKeyboardOpen) {
684 + // Check if the class is still there (it might have been removed by blur already)
685 const wasKeyboardOpen = document.body.classList.contains('mobile-keyboard-open');
686
- if (wasKeyboardOpen && editorView) {
- // Hide editor, update layout, then scroll cursor into view
- const pos = editorView.state.selection.main.head;
- editorPane.style.opacity = '0';
- document.body.classList.remove('mobile-keyboard-open');
- requestAnimationFrame(() => requestAnimationFrame(() => {
- const lineBlock = editorView.lineBlockAt(pos);
- const targetScroll = lineBlock.top - (editorView.dom.clientHeight / 2);
- editorView.scrollDOM.scrollTop = Math.max(0, targetScroll);
- editorPane.style.opacity = '';
- }));
- } else {
- document.body.classList.remove('mobile-keyboard-open');
687 + if (wasKeyboardOpen) {
688 + // This is a fallback in case blur didn't catch it
689 + // (e.g. if the keyboard was dismissed via a gesture that didn't blur immediately)
690 + if (editorView) {
691 + const pos = editorView.state.selection.main.head;
692 + editorPane.style.opacity = '0';
693 + document.body.classList.remove('mobile-keyboard-open');
694 +
695 + requestAnimationFrame(() => requestAnimationFrame(() => {
696 + const lineBlock = editorView.lineBlockAt(pos);
697 + const targetScroll = lineBlock.top - (editorView.dom.clientHeight / 2);
698 + editorView.scrollDOM.scrollTop = Math.max(0, targetScroll);
699 + editorPane.style.opacity = '';
700 + }));
701 + } else {
702 + document.body.classList.remove('mobile-keyboard-open');
703 + }
704 }
705 }
706 }