// MWSE Studio — dahili yönetim arayüzü. import ColumnView from '/studio/ColumnView.js'; import { MediaSources } from '/sdk/webrtc/index.js'; export default class Studio { constructor(mwse, container) { this.mwse = mwse; this._el = typeof container === 'string' ? document.querySelector(container) : container; this._view = null; this._devices = { cameras: [], microphones: [] }; this._statusEl = null; this._notifArea = null; this._myIPEl = null; this._myUUIDEl = null; this._localGrid = null; this._remoteGrid = null; this._streamsPanel = null; this._styleOK = false; this._peersCol = null; // reaktif güncelleme için referans } async mount() { this._el.classList.add('mwse-studio'); this._injectStyle(); this._buildToolbar(); this._buildNotifArea(); // Ana alan: kolonlar (sol) + akış monitörü (sağ) const mainArea = document.createElement('div'); mainArea.className = 'mwse-studio__main'; this._el.appendChild(mainArea); this._view = new ColumnView(mainArea); this._view.mount(); this._streamsPanel = this._buildStreamsPanel(mainArea); await this._loadDevices(); this._pushRootColumn(); // Sanal IP al this.mwse.scope(async () => { try { const ip = await this.mwse.virtualPressure.allocAPIPAddress(); this.mwse.me.info.set('ip', ip); if (this._myIPEl) this._myIPEl.textContent = ip; if (this._myUUIDEl) this._myUUIDEl.textContent = this.mwse.me.socketId.slice(-8); } catch (_) {} }); // Gelen eşleme isteği → bildirim banner this.mwse.me.on('request/pair', peer => this._showPairRequest(peer)); // Eşleşme onaylandı (istek gönderen taraf) this.mwse.me.on('accepted/pair', peer => { this._watchIncoming(peer); if (this.mwse.virtualPressure.APIPAddress) { this.mwse.me.info.set('ip', this.mwse.virtualPressure.APIPAddress); } this._rebuildPeerItems(); this._setStatus('online', `${this._peerLabel(peer)} eşleşmesi kuruldu`); }); // Eşleşme bitti (karşı taraf koptu veya endPair çağrıldı) this.mwse.me.on('end/pair', (from) => { this._rebuildPeerItems(); this._setStatus('', `${typeof from === 'string' ? from.slice(-8) : ''} ayrıldı`); }); // WebSocket bağlantısı kopunca (sayfa yenileme, ağ kesilmesi) this.mwse.me.on('peer/disconnect', peer => { this._rebuildPeerItems(); this._setStatus('error', `${this._peerLabel(peer)} bağlantısı kesildi`); }); this.mwse.on('room', () => this._view.refresh()); return this; } // ── Araç çubuğu ────────────────────────────────────────────────────────── _buildToolbar() { const bar = document.createElement('div'); bar.className = 'mwse-studio__toolbar'; const logo = document.createElement('span'); logo.className = 'mwse-studio__title'; logo.innerHTML = 'MWSE Studio'; bar.appendChild(logo); // ID kartı: sadece IP + kısa UUID + kopyala const idCard = document.createElement('div'); idCard.className = 'mwse-id-card'; idCard.title = 'Socket ID kopyala (bağlantı paylaşımı için)'; this._myIPEl = document.createElement('span'); this._myIPEl.className = 'mwse-id-card__ip'; this._myIPEl.textContent = '●'; // IP gelmeden önce nokta this._myUUIDEl = document.createElement('span'); this._myUUIDEl.className = 'mwse-id-card__value'; this._myUUIDEl.textContent = '…'; const copyIcon = document.createElement('span'); copyIcon.className = 'mwse-id-card__copy'; copyIcon.textContent = '⎘'; idCard.append(this._myIPEl, this._myUUIDEl, copyIcon); idCard.addEventListener('click', () => { const id = this.mwse.me.socketId; if (!id) return; navigator.clipboard.writeText(id).then(() => { copyIcon.textContent = '✓'; idCard.classList.add('mwse-id-card--copied'); setTimeout(() => { idCard.classList.remove('mwse-id-card--copied'); copyIcon.textContent = '⎘'; }, 2000); }); }); bar.appendChild(idCard); this._statusEl = document.createElement('span'); this._statusEl.className = 'mwse-studio__status mwse-studio__status--online'; this._statusEl.innerHTML = 'wifi Bağlı'; bar.appendChild(this._statusEl); // Canlı saat const clock = document.createElement('span'); clock.className = 'mwse-studio__clock'; const tick = () => { const now = new Date(); clock.textContent = now.toTimeString().slice(0, 8); }; tick(); setInterval(tick, 1000); bar.appendChild(clock); this._el.insertBefore(bar, this._el.firstChild); } // ── Akış monitör paneli ─────────────────────────────────────────────────── _buildStreamsPanel(parent) { const panel = document.createElement('div'); panel.className = 'mwse-streams-panel'; panel.style.display = 'none'; // akış yokken gizli // Gönderiyorum bölümü const outSec = document.createElement('div'); outSec.className = 'mwse-streams-section'; const outTitle = document.createElement('div'); outTitle.className = 'mwse-streams-section__title'; outTitle.textContent = '▲ Gönderiyorum'; this._localGrid = document.createElement('div'); this._localGrid.className = 'mwse-streams-grid'; outSec.append(outTitle, this._localGrid); // Geliyor bölümü const inSec = document.createElement('div'); inSec.className = 'mwse-streams-section'; const inTitle = document.createElement('div'); inTitle.className = 'mwse-streams-section__title'; inTitle.textContent = '▼ Geliyor'; this._remoteGrid = document.createElement('div'); this._remoteGrid.className = 'mwse-streams-grid'; inSec.append(inTitle, this._remoteGrid); panel.append(outSec, inSec); parent.appendChild(panel); return panel; } // Yerel (gönderilen) akış tile'ı ekle _addLocalTile(label, stream, peerLabel) { const hasVideo = stream.getVideoTracks().length > 0; const tile = this._makeTile( label, `→ ${peerLabel}`, stream, hasVideo, true, // muted (geri besleme yok) () => tile.remove() && this._updatePanelVisibility() ); this._localGrid.appendChild(tile); this._updatePanelVisibility(); } // Uzak (gelen) track tile'ı → ses kontrollü _addRemoteTile(track, streams, peerLabel) { const isVideo = track.kind === 'video'; const stream = streams?.[0] ?? new MediaStream([track]); const tile = document.createElement('div'); tile.className = `mwse-stream-tile${isVideo ? '' : ' mwse-stream-tile--audio'}`; // Media element let mediaEl; if (isVideo) { mediaEl = document.createElement('video'); mediaEl.autoplay = true; mediaEl.playsInline = true; mediaEl.muted = false; mediaEl.srcObject = stream; mediaEl.className = 'mwse-stream-tile__video'; tile.appendChild(mediaEl); } else { // Ses: görünür tile + gizli