first pass at iOS improvements
Hunter
Friday, December 19th, 2025 at 3:01 PM
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
6 <title>Web Workshop</title>
7 <link rel="icon" href="resources/rollerskate.png">
8 <link rel="manifest" href="manifest.json">
31 box-sizing: border-box;
32 }
33
34 + html, body {
35 + overscroll-behavior: none;
36 + background-color: var(--editor-bg);
37 + }
38 +
39 + html {
40 + height: 100%;
41 + }
42 +
43 body {
44 font-family: monospace;
45 background: var(--bg-color);
46 color: var(--text-color);
- height: 100vh;
47 + height: 100%;
48 display: flex;
49 overflow: hidden;
50 }
51
52 .editor-pane {
53 width: 50%;
- height: 100vh;
54 + height: 100%;
55 position: relative;
56 }
57
58 .preview-pane {
59 width: 50%;
- height: 100vh;
60 + height: 100%;
61 position: relative;
62 }
63
184 body {
185 flex-direction: column;
186 }
-
187 +
188 .editor-pane {
189 width: 100%;
- height: 50vh;
190 + height: 50%;
191 order: 2;
192 }
-
193 +
194 .preview-pane {
195 width: 100%;
- height: 50vh;
196 + height: 50%;
197 order: 1;
198 + padding-top: env(safe-area-inset-top);
199 }
200 }
201
- /* Mobile keyboard state - portrait mode (height > width) */
- @media (pointer: coarse) and (max-aspect-ratio: 1/1), (pointer: none) and (max-aspect-ratio: 1/1) {
202 + /* Mobile: fullscreen editor when keyboard is open */
203 + @media (pointer: coarse), (pointer: none) {
204 body.mobile-keyboard-open .preview-pane {
205 display: none;
206 }
-
- body.mobile-keyboard-open .editor-pane {
- height: var(--visible-height, 100vh);
- order: 1;
- overflow: hidden;
- }
- }
207
- /* Mobile keyboard state - landscape mode (width > height) */
- @media (pointer: coarse) and (min-aspect-ratio: 1/1), (pointer: none) and (min-aspect-ratio: 1/1) {
- body.mobile-keyboard-open .preview-pane {
- display: none;
- }
-
208 body.mobile-keyboard-open .editor-pane {
209 + position: fixed;
210 + top: 0;
211 + left: 0;
212 width: 100%;
- height: var(--visible-height, 100vh);
213 + height: 100%;
214 order: 1;
- overflow: hidden;
215 + overscroll-behavior: none;
216 }
217 }
218
598 parent: document.getElementById('editor')
599 });
600
- // Track editor focus for mobile keyboard behavior
- editorView.contentDOM.addEventListener('focus', function() {
- isEditorFocused = true;
- handleViewportChange(); // Check if keyboard is already open
- });
-
- editorView.contentDOM.addEventListener('blur', function() {
- isEditorFocused = false;
- handleViewportChange(); // Exit fullscreen mode if keyboard is open
- });
-
601 // Initial render
602 updatePreview();
-
603 +
604 + // Track editor focus for mobile keyboard detection
605 + editorView.contentDOM.addEventListener('focus', () => { isEditorFocused = true; });
606 + editorView.contentDOM.addEventListener('blur', () => { isEditorFocused = false; });
607 +
608 // Focus the editor
609 editorView.focus();
610
618 }
619
620 // Mobile keyboard detection
- let initialViewportHeight = window.visualViewport ? window.visualViewport.height : window.innerHeight;
- let keyboardOpenThreshold = 150; // pixels
- let isEditorFocused = false;
- let wasKeyboardOpen = false;
-
621 function isMobileDevice() {
622 return window.matchMedia("(pointer: coarse), (pointer: none)").matches;
623 }
-
624 +
625 + let initialViewportHeight = window.visualViewport ? window.visualViewport.height : window.innerHeight;
626 + let isEditorFocused = false;
627 +
628 function handleViewportChange() {
629 if (!isMobileDevice()) return;
-
630 +
631 const currentHeight = window.visualViewport ? window.visualViewport.height : window.innerHeight;
-
- // Detect potential orientation change: very large height difference when no keyboard activity
- // Use a higher threshold to avoid interfering with keyboard detection
- if (!wasKeyboardOpen && !isEditorFocused && Math.abs(initialViewportHeight - currentHeight) > 300) {
- initialViewportHeight = currentHeight;
- }
-
632 const heightDifference = initialViewportHeight - currentHeight;
- const isKeyboardOpen = heightDifference > keyboardOpenThreshold;
-
- // Only activate fullscreen editor mode if the editor is focused
633 + const isKeyboardOpen = heightDifference > 150;
634 +
635 + const wasFullscreen = document.body.classList.contains('mobile-keyboard-open');
636 +
637 if (isKeyboardOpen && isEditorFocused) {
638 document.body.classList.add('mobile-keyboard-open');
- // Set CSS custom property for the visible height
- document.documentElement.style.setProperty('--visible-height', `${currentHeight}px`);
- // Match editor background color
- document.body.style.backgroundColor = 'var(--editor-bg)';
- wasKeyboardOpen = true;
- } else if (wasKeyboardOpen && !isKeyboardOpen) {
- // Only run this when transitioning from keyboard open to closed
639 + } else if (!isKeyboardOpen) {
640 document.body.classList.remove('mobile-keyboard-open');
- // Reset to full viewport height
- document.documentElement.style.setProperty('--visible-height', '100vh');
- // Reset background color
- document.body.style.backgroundColor = '';
- // Blur the editor only if it was focused to prevent keyboard from reopening
- if (isEditorFocused) {
- editorView.contentDOM.blur();
- }
- // Update preview when keyboard closes to show any changes made while typing
- updatePreview();
- wasKeyboardOpen = false;
641 }
642 }
-
- // Listen for viewport changes
643 +
644 if (window.visualViewport) {
645 window.visualViewport.addEventListener('resize', handleViewportChange);
646 } else {