diff --git a/plugin-designs/plugin-studio/plugin-studio.vue b/plugin-designs/plugin-studio/plugin-studio.vue index 6650e56c7..ef2882cf9 100644 --- a/plugin-designs/plugin-studio/plugin-studio.vue +++ b/plugin-designs/plugin-studio/plugin-studio.vue @@ -1073,7 +1073,7 @@ export default defineComponent({ const h = document.createElement('div') h.setAttribute('data-studio-widgets', corner) const [v, x] = corner.split('-') - h.style.cssText = `position:fixed;${v}:12px;${x}:12px;z-index:2147483647;display:flex;flex-direction:column;align-items:${x === 'right' ? 'flex-end' : 'flex-start'};gap:8px;` + h.style.cssText = `position:fixed;${v}:max(12px,env(safe-area-inset-${v}));${x}:max(12px,env(safe-area-inset-${x}));z-index:2147483647;display:flex;flex-direction:column;align-items:${x === 'right' ? 'flex-end' : 'flex-start'};gap:8px;` document.body.appendChild(h) W.hosts[corner] = h return h @@ -1089,7 +1089,7 @@ export default defineComponent({ box.style.cssText = 'display:flex;flex-direction:column;align-items:inherit;gap:6px;' const panel = document.createElement('div') panel.setAttribute('data-widget-panel', '') - panel.style.cssText = 'display:none;width:min(380px,calc(100vw - 24px));height:min(520px,calc(100vh - 80px));position:relative;background:#fff;border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.25);overflow:hidden;' + panel.style.cssText = 'display:none;width:min(380px,calc(100vw - 24px));height:min(520px,calc(100vh - 80px));height:min(520px,calc(100dvh - 80px - env(safe-area-inset-top) - env(safe-area-inset-bottom)));position:relative;background:#fff;border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.25);overflow:hidden;' const btn = document.createElement('button') btn.type = 'button' // A catalogue label per widget ("widget:"), not the row key (rega / repo: the button read @@ -1124,7 +1124,9 @@ export default defineComponent({ const el = document.createElement(row.tag) el.setAttribute('profile-config', JSON.stringify(this.paneConfig(row, this.locale))) if (this.base) el.setAttribute('tree-base', this.base.replace(/\/+$/, '')) - el.style.cssText = 'display:block;position:absolute;inset:0;width:100%;height:100%;' + // The panel is a fixed size; on a phone the widget's content is taller than it, so the element scrolls + // inside the panel rather than being clipped by it. + el.style.cssText = 'display:block;position:absolute;inset:0;width:100%;height:100%;overflow-y:auto;overscroll-behavior:contain;' // LIVE MIC, VISIBLE WHEN COLLAPSED: a widget that emits `recording` (Rudder does) turns its corner // button red with a dot while it listens, so closing the panel never hides that the mic is on. el.addEventListener('recording', (e) => { diff --git a/plugin-designs/rudder-voice/rudder-voice.vue b/plugin-designs/rudder-voice/rudder-voice.vue index a5393e36c..6467ba235 100644 --- a/plugin-designs/rudder-voice/rudder-voice.vue +++ b/plugin-designs/rudder-voice/rudder-voice.vue @@ -590,7 +590,7 @@ const statusText = computed(() => ({ .rudder { --bg: #0b0f14; --panel: #111823; --line: #1f2937; --text: #e5e7eb; --muted: #9ca3af; --accent: #38bdf8; --rec: #ef4444; --ok: #22c55e; --warn: #f59e0b; - box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; min-height: 480px; height: 100%; + box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; min-height: max(100%, 480px); padding: 18px; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: 15px; } @@ -658,4 +658,10 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1 .debug dl { display: grid; grid-template-columns: 90px 1fr; gap: 6px 10px; margin: 0 0 10px; } .debug dt { color: var(--muted); } .debug dd { margin: 0; overflow-wrap: anywhere; } +/* A phone-sized panel: less padding, and a shorter draft so it stays in view below the controls. */ +@media (max-width: 480px) { + .rudder { min-height: 100%; padding: 12px; gap: 10px; } + .compose { min-height: 200px; } + .draft { min-height: 180px; } +}