From 143dd26c31bfa848fb48fe2ef9ac927226818d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20T=C3=B6rnberg?= Date: Wed, 23 Sep 2026 11:06:20 +0200 Subject: [PATCH] fix --- public/app.js | 248 ++++++++++++++++++++++++++++++++-------------- public/index.html | 2 + public/style.css | 52 ++++++++-- 3 files changed, 222 insertions(+), 80 deletions(-) diff --git a/public/app.js b/public/app.js index c1105e5..6e2d4fc 100644 --- a/public/app.js +++ b/public/app.js @@ -1073,19 +1073,22 @@ function stopScreenShare(stream, silent) { // --------------------------------------------------------------------------- // position sync // --------------------------------------------------------------------------- +// Fans new joiners out from directly in front of the room's center: the +// first joiner lands straight ahead, then each next one alternates sides, +// widening by 10deg per pair (0, +10, -10, +20, -20, ...) instead of the +// old distance-maximizing grid search, which tended to dump joiners in a +// far corner rather than somewhere natural to face the group. function chooseOpenParticipantPosition() { - const occupied = [...S.peers.values()].map((peer) => ({ x: peer.x, y: peer.y })); - const candidates = []; - for (let row = 0; row < 5; row++) { - for (let col = 0; col < 7; col++) { - candidates.push({ x: 15 + col * 11.7, y: 15 + row * 17.5 }); - } - } - if (!occupied.length) return { x: 50, y: 50 }; - return candidates.sort((a, b) => { - const distance = (point) => Math.min(...occupied.map((other) => Math.hypot(point.x - other.x, point.y - other.y))); - return distance(b) - distance(a); - })[0]; + const ANCHOR = { x: 50, y: 50 }; + const RADIUS = 24; + const n = S.peers.size; + const step = Math.ceil(n / 2) * 10; + const angleDeg = n === 0 ? 0 : (n % 2 === 1 ? step : -step); + const rad = (angleDeg * Math.PI) / 180; + return { + x: ANCHOR.x + RADIUS * Math.sin(rad), + y: ANCHOR.y - RADIUS * Math.cos(rad), + }; } let lastPosSend = 0; @@ -1326,6 +1329,7 @@ function renderTiles() { // share keys: 'me' or peer id; popped-out shares render in their own window const poppedWindows = new Map(); // key -> {win, name} +let focusedShareKey = null; // clicked remote share, rendered large with the rest as a filmstrip function activeShares() { const shares = []; @@ -1342,80 +1346,164 @@ function activeShares() { return shares; } +// Picks the column/row count (out of `count` tiles) that best fills a +// width x height area assuming ~16:9 tiles, so multiple streams can be +// watched at once with as little wasted space as possible. +function bestGrid(count, width, height) { + const aspect = 16 / 9; + let best = { cols: count, rows: 1, area: 0 }; + for (let cols = 1; cols <= count; cols++) { + const rows = Math.ceil(count / cols); + let cellW = width / cols; + let cellH = height / rows; + if (cellW / cellH > aspect) cellW = cellH * aspect; else cellH = cellW / aspect; + const area = cellW * cellH; + if (area > best.area) best = { cols, rows, area }; + } + return best; +} + +// Re-measures the live grid and updates its column/row count only — avoids +// renderScreens()'s full teardown, which would restart