diff options
Diffstat (limited to 'src/index.php')
| -rw-r--r-- | src/index.php | 35 |
1 files changed, 27 insertions, 8 deletions
diff --git a/src/index.php b/src/index.php index 5360366..40bd103 100644 --- a/src/index.php +++ b/src/index.php | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | 7 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 8 | <title>sent-web</title> | 8 | <title>sent-web</title> |
| 9 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kj-sh604/noir.css@latest/out/noir.min.css"> | 9 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kj-sh604/noir.css@latest/out/noir.min.css"> |
| 10 | <style> :root { --sent-fg: #000000; --sent-bg: #ffffff; --sent-font: 'Noto Color Emoji', 'DejaVu Sans', sans-serif; } body { max-width: 960px; margin: 0 auto; padding: 1rem; } .subtitle { opacity: 0.6; font-size: 0.9em; margin-top: -0.8em; } /* ── controls ── */ #controls { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin-bottom: 1rem; padding: 0.75rem; border: 1px solid currentColor; border-radius: 4px; } #controls label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; } #controls input[type="color"] { width: 2rem; height: 2rem; padding: 0; border: 1px solid currentColor; cursor: pointer; background: none; } #controls select { max-width: 200px; } .upload-area { display: flex; align-items: center; gap: 0.5rem; } #upload-input { display: none; } #upload-status { font-size: 0.85rem; opacity: 0.7; } /* ── editor ── */ #input { width: 100%; min-height: 420px; font-family: monospace; font-size: 0.95rem; resize: vertical; tab-size: 4; } .btn-row { display: flex; gap: 0.5rem; margin-top: 0.5rem; } .hint { font-size: 0.8rem; opacity: 0.5; margin-top: 0.25rem; } /* ── presentation overlay ── */ #presentation { position: fixed; inset: 0; z-index: 9999; display: none; align-items: center; justify-content: flex-start; background: var(--sent-bg); color: var(--sent-fg); cursor: none; overflow: hidden; padding-left: 7.5%; } #presentation.active { display: flex; } #slide-content { text-align: left; white-space: pre-line; word-wrap: break-word; font-family: var(--sent-font); } #slide-content img { max-width: 85vw; max-height: 85vh; width: 85vw; height: 85vh; object-fit: contain; } </style> | 10 | <style> :root { --sent-fg: #000000; --sent-bg: #ffffff; --sent-font: 'Noto Color Emoji', 'DejaVu Sans', sans-serif; } body { max-width: 960px; margin: 0 auto; padding: 1rem; } .subtitle { opacity: 0.6; font-size: 0.9em; margin-top: -0.8em; } /* ── controls ── */ #controls { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; margin-bottom: 1rem; padding: 0.75rem; border: 1px solid currentColor; border-radius: 4px; } #controls label { display: flex; align-items: center; gap: 0.4rem; font-size: 0.9rem; } #controls input[type="color"] { width: 2rem; height: 2rem; padding: 0; border: 1px solid currentColor; cursor: pointer; background: none; } #controls select { max-width: 200px; } .upload-area { display: flex; align-items: center; gap: 0.5rem; } #upload-input { display: none; } #upload-status { font-size: 0.85rem; opacity: 0.7; } /* ── editor ── */ #input { width: 100%; min-height: 420px; font-family: monospace; font-size: 0.95rem; resize: vertical; tab-size: 4; } .btn-row { display: flex; gap: 0.5rem; margin-top: 0.5rem; } .hint { font-size: 0.8rem; opacity: 0.5; margin-top: 0.25rem; } /* ── presentation overlay ── */ #presentation { position: fixed; inset: 0; z-index: 9999; display: none; align-items: center; justify-content: flex-start; background: var(--sent-bg); color: var(--sent-fg); cursor: none; overflow: hidden; padding-left: 7.5%; } #presentation.active { display: flex; } #slide-content { text-align: left; white-space: pre-line; word-wrap: break-word; font-family: var(--sent-font); } #slide-content img { display: block; max-width: 85vw; max-height: 85vh; object-fit: contain; } </style> |
| 11 | </head> | 11 | </head> |
| 12 | 12 | ||
| 13 | <body> | 13 | <body> |
| @@ -105,8 +105,8 @@ questions?</textarea> | |||
| 105 | bg: '#ffffff', | 105 | bg: '#ffffff', |
| 106 | fontFamily: '', | 106 | fontFamily: '', |
| 107 | lineSpacing: 1.25, | 107 | lineSpacing: 1.25, |
| 108 | usableWidth: 0.80, | 108 | usableWidth: 0.85, |
| 109 | usableHeight: 0.80, | 109 | usableHeight: 0.85, |
| 110 | }, | 110 | }, |
| 111 | 111 | ||
| 112 | init() { | 112 | init() { |
| @@ -245,10 +245,15 @@ questions?</textarea> | |||
| 245 | }); | 245 | }); |
| 246 | 246 | ||
| 247 | // stop presentation whenever fullscreen is exited (covers browser- | 247 | // stop presentation whenever fullscreen is exited (covers browser- |
| 248 | // intercepted Escape that never reaches the keydown handler) | 248 | // intercepted Escape that never reaches the keydown handler). |
| 249 | // Re-render on enter so the slide is drawn with the settled fullscreen | ||
| 250 | // viewport dimensions (requestFullscreen is async, so the initial | ||
| 251 | // renderSlide() call may use pre-fullscreen vw/vh values). | ||
| 249 | document.addEventListener('fullscreenchange', () => { | 252 | document.addEventListener('fullscreenchange', () => { |
| 250 | if (!document.fullscreenElement && this.presenting) { | 253 | if (!document.fullscreenElement && this.presenting) { |
| 251 | this.stopPresentation(); | 254 | this.stopPresentation(); |
| 255 | } else if (document.fullscreenElement && this.presenting) { | ||
| 256 | this.renderSlide(); | ||
| 252 | } | 257 | } |
| 253 | }); | 258 | }); |
| 254 | }, | 259 | }, |
| @@ -361,6 +366,10 @@ questions?</textarea> | |||
| 361 | content.innerHTML = ''; | 366 | content.innerHTML = ''; |
| 362 | 367 | ||
| 363 | if (slide.img) { | 368 | if (slide.img) { |
| 369 | // center image slides — override the text-layout defaults | ||
| 370 | pres.style.justifyContent = 'center'; | ||
| 371 | pres.style.alignItems = 'center'; | ||
| 372 | pres.style.paddingLeft = '0'; | ||
| 364 | const img = document.createElement('img'); | 373 | const img = document.createElement('img'); |
| 365 | if (slide.img.startsWith('http://') || slide.img.startsWith('https://')) { | 374 | if (slide.img.startsWith('http://') || slide.img.startsWith('https://')) { |
| 366 | img.src = slide.img; | 375 | img.src = slide.img; |
| @@ -368,13 +377,23 @@ questions?</textarea> | |||
| 368 | img.src = 'uploads/' + slide.img; | 377 | img.src = 'uploads/' + slide.img; |
| 369 | } | 378 | } |
| 370 | img.alt = slide.img; | 379 | img.alt = slide.img; |
| 371 | img.style.maxWidth = (this.settings.usableWidth * 100) + 'vw'; | 380 | // changed to reflect the fullscreen viewport. |
| 372 | img.style.maxHeight = (this.settings.usableHeight * 100) + 'vh'; | 381 | const pw = pres.offsetWidth || window.innerWidth; |
| 373 | img.style.width = (this.settings.usableWidth * 100) + 'vw'; | 382 | const ph = pres.offsetHeight || window.innerHeight; |
| 374 | img.style.height = (this.settings.usableHeight * 100) + 'vh'; | 383 | const maxW = Math.floor(pw * this.settings.usableWidth); |
| 384 | const maxH = Math.floor(ph * this.settings.usableHeight); | ||
| 385 | img.style.width = maxW + 'px'; | ||
| 386 | img.style.height = maxH + 'px'; | ||
| 387 | img.style.maxWidth = maxW + 'px'; | ||
| 388 | img.style.maxHeight = maxH + 'px'; | ||
| 375 | img.style.objectFit = 'contain'; | 389 | img.style.objectFit = 'contain'; |
| 390 | img.style.display = 'block'; | ||
| 376 | content.appendChild(img); | 391 | content.appendChild(img); |
| 377 | } else { | 392 | } else { |
| 393 | // restore text-layout defaults | ||
| 394 | pres.style.justifyContent = 'flex-start'; | ||
| 395 | pres.style.alignItems = 'center'; | ||
| 396 | pres.style.paddingLeft = '7.5%'; | ||
| 378 | const fontSize = this.calcFontSize(slide.lines); | 397 | const fontSize = this.calcFontSize(slide.lines); |
| 379 | content.style.fontSize = fontSize + 'px'; | 398 | content.style.fontSize = fontSize + 'px'; |
| 380 | content.style.lineHeight = String(this.settings.lineSpacing); | 399 | content.style.lineHeight = String(this.settings.lineSpacing); |
